Strings Sample

This sample demonstrates how to use a string returned from an unmanaged function, and how to pass a structure that contains a Unicode-formatted or ANSI-formatted string. It shows how to properly initialize these strings and how to retrieve returned values.

The String sample uses the following unmanaged functions, shown with their original function declaration:

  • TestStringAsResult exported from PinvokeLib.dll.

    char* TestStringAsResult();
    
  • TestStringInStruct exported from PinvokeLib.dll.

    void TestStringInStruct(MYSTRSTRUCT* pStruct);
    
  • TestStringInStructAnsi exported from PinvokeLib.dll.

    void TestStringInStructAnsi(MYSTRSTRUCT2* pStruct);
    

PinvokeLib.dll is a custom unmanaged library that contains implementations for the previously listed functions and two structures, MYSTRSTRUCT and MYSTRSTRUCT2. These structures contain the following elements:

typedef struct _MYSTRSTRUCT
{
   wchar_t* buffer;
   UINT size; 
} MYSTRSTRUCT;
typedef struct _MYSTRSTRUCT2
{
   char* buffer;
   UINT size; 
} MYSTRSTRUCT2;

In this sample, the managed MyStrStruct and MyStrStruct2 structures contain managed strings instead of StringBuilder buffers because the StringBuilder type cannot be used inside a structure. The StructLayoutAttribute attribute is set to ensure that the members are arranged in memory sequentially, in the order in which they appear. The CharSet field is set to specify ANSI or Unicode formats.

The LibWrap class contains the managed prototype methods called by the App class. Although structures are normally passed by value, the arguments to the TestStringInStruct and TestStringInStructAnsi methods are marked with the ref (ByRef in Visual Basic) keyword, which passes structures by reference.

The source code for the following code examples is provided by the .NET Framework Platform Invoke Technology Sample.

Declaring Prototypes

' Declares a managed structure for each unmanaged structure.
< StructLayout( LayoutKind.Sequential, CharSet:=CharSet.Unicode )> _
Public Structure MyStrStruct
   Public buffer As String
   Public size As Integer
End Structure 'MyStrStruct

< StructLayout( LayoutKind.Sequential, CharSet:=CharSet.Ansi )> _
Public Structure MyStrStruct2
   Public buffer As String
   Public size As Integer
End Structure 'MyStrStruct2

Public Class LibWrap
   ' Declares managed prototypes for unmanaged functions.
   Declare Function TestStringAsResult Lib "..\LIB\PinvokeLib.dll" () _
      As String
   Declare Sub TestStringInStruct Lib "..\LIB\PinvokeLib.dll" _
      ( ByRef mss As MyStrStruct )
   Declare Sub TestStringInStructAnsi Lib "..\LIB\PinvokeLib.dll" _
      ( ByRef mss As MyStrStruct2 )
End Class 'LibWrap
// Declares a managed structure for each unmanaged structure.
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Unicode )]
public struct MyStrStruct 
{  
   public String buffer;
   public int size;
}
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct MyStrStruct2 
{  
   public String buffer;
   public int size;
}
public class LibWrap
{
   // Declares managed prototypes for unmanaged functions.
   [DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern String TestStringAsResult();

   [DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern void TestStringInStruct( ref MyStrStruct mss );

   [DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern void TestStringInStructAnsi( ref MyStrStruct2 
      mss );
}

Calling Functions

Public Class App
   Public Shared Sub Main()
      ' String as result.
      Dim strng As String = LibWrap.TestStringAsResult()
      Console.WriteLine( ControlChars.CrLf + "String returned: {0}", _
      strng )

      ' Initializes buffer and appends something to the end so the whole
      ' buffer is passed to the unmanaged side.
      Dim buffer As New StringBuilder( "content", 100 )
      buffer.Append( ChrW(0) )
      buffer.Append( "*"c, buffer.Capacity - 8)
      
      Dim mss As MyStrStruct
      mss.buffer = buffer.ToString()
      mss.size = mss.buffer.Length
      
      LibWrap.TestStringInStruct( mss )
      Console.WriteLine( ControlChars.CrLf + _
         "Buffer after Unicode function call: {0}", mss.buffer )

      Dim buffer2 As New StringBuilder( "content", 100 )
      buffer2.Append( ChrW(0) )
      buffer2.Append( "*"c, buffer2.Capacity - 8 )
      
      Dim mss2 As MyStrStruct2
      mss2.buffer = buffer2.ToString()
      mss2.size = mss2.buffer.Length 
      
      LibWrap.TestStringInStructAnsi( mss2 )
      Console.WriteLine( ControlChars.CrLf + _
         "Buffer after Ansi function call: {0}", mss2.buffer )
   End Sub 'Main
End Class 'App
public class App
{
   public static void Main()
   {
      // String as result.
      String str = LibWrap.TestStringAsResult();
      Console.WriteLine( "\nString returned: {0}", str );
      
      // Initializes buffer and appends something to the end so the whole
      // buffer is passed to the unmanaged side.
      StringBuilder buffer = new StringBuilder( "content", 100 ); 
      buffer.Append( (char)0 );
      buffer.Append( '*', buffer.Capacity - 8 );
      
      MyStrStruct mss;
      mss.buffer = buffer.ToString();
      mss.size = mss.buffer.Length;
      
      LibWrap.TestStringInStruct( ref mss );
      Console.WriteLine( "\nBuffer after Unicode function call: {0}", 
         mss.buffer );
      
      StringBuilder buffer2 = new StringBuilder( "content", 100 ); 
      buffer2.Append( (char)0 );
      buffer2.Append( '*', buffer2.Capacity - 8 );
      
      MyStrStruct2 mss2;
      mss2.buffer = buffer2.ToString();
      mss2.size = mss2.buffer.Length;
      
      LibWrap.TestStringInStructAnsi( ref mss2 );
      Console.WriteLine( "\nBuffer after Ansi function call: {0}", 
         mss2.buffer );
   }
}

See Also

Tasks

Platform Invoke Technology Sample

Concepts

Marshaling Strings

Platform Invoke Data Types

Default Marshaling for Strings

Creating Prototypes in Managed Code