OpenFileDlg Sample

This sample demonstrates how to call an unmanaged function that expects a pointer to a structure containing a string. Further, it demonstrates how to use a managed class to represent an unmanaged structure, how to apply the InAttribute and OutAttribute attributes to marshal the class back to the caller, and how to declare and initialize different fields of the class to produce the correct unmanaged representation. The Microsoft .NET Framework SDK includes the complete Visual Basic .NET and C# versions of this sample in Samples\Technologies\Interop\Platform-Invoke.

The OpenFileDlg sample uses the following unmanaged function, shown with its original function declaration:

  • GetOpenFileName exported from Comdlg32.dll.

    BOOL GetOpenFileName(LPOPENFILENAME lpofn);
    

The original structure passed to the function contains the following elements:

typedef struct tagOFN { 
  DWORD         lStructSize; 
  ...
  LPCTSTR       lpstrFilter; 
  ...
  LPTSTR        lpstrFile; 
  DWORD         nMaxFile; 
  LPTSTR        lpstrFileTitle; 
  DWORD         nMaxFileTitle; 
  LPCTSTR       lpstrInitialDir; 
  LPCTSTR       lpstrTitle; 
  ... 
  LPCTSTR       lpstrDefExt; 
  ...
} OPENFILENAME, *LPOPENFILENAME; 

In this sample, the OpenFileName class contains the elements of the original structure as class members. The unmanaged structure is declared as a class, instead of as a managed structure, to show how a class can be used when the unmanaged function expects a pointer to a structure. Because a managed class is a reference type, when it is passed by value, a pointer to the class is passed to unmanaged code. This is exactly what the unmanaged function expects.

The StructLayoutAttribute attribute is applied to the class to ensure that the members are laid out in memory sequentially, in the order in which they appear. The CharSet field is set so that platform invoke can choose between ANSI and Unicode formats at run time, based on the target platform.

The LibWrap class contains the managed prototype of the GetOpenFileName method, which passes the OpenFileName class as an In/Out parameter. By applying InAttribute and OutAttribute explicitly, the sample ensures that OpenFileName is marshaled as an In/Out parameter and the caller can see the changes marshaled back. (For performance, the default directional attribute for a class is In, which prevents the caller from seeing the changes marshaled back.) The App class creates a new instance of the OpenFileName class, and uses the Marshal.SizeOf method to determine the size, in bytes, of the unmanaged structure.

Declaring Prototypes

' Declare a class member for each structure element.
< StructLayout( LayoutKind.Sequential, CharSet:=CharSet.Auto )> _
Public Class OpenFileName

   Public structSize As Integer = 0
   ...
   Public filter As String = Nothing
   ...
   Public file As String = Nothing
   Public maxFile As Integer = 0
   Public fileTitle As String = Nothing
   Public maxFileTitle As Integer = 0
   Public initialDir As String = Nothing
   Public title As String = Nothing
   ...
   Public defExt As String = Nothing
   ...
End Class 'OpenFileName

Public Class LibWrap
   ' Declare managed prototype for the unmanaged function.
   Declare Auto Function GetOpenFileName Lib "Comdlg32.dll" ( _
      <[In], Out> ByVal ofn As OpenFileName ) As Boolean
End Class 'LibWrap
[C#]
// Declare a class member for each structure element.
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Auto )]  
public class OpenFileName 
{
    public int       structSize = 0;
    ...
    public String    filter = null;
    ...
    public String    file = null;
    public int       maxFile = 0;
    public String    fileTitle = null;
    public int       maxFileTitle = 0;
    public String    initialDir = null;
    public String    title = null;   
    ...
    public String    defExt = null; 
    ...
}

public class LibWrap
{
   // Declare a managed prototype for the unmanaged function.
   [ DllImport( "Comdlg32.dll", CharSet=CharSet.Auto )]
   public static extern bool GetOpenFileName([ In, Out ] 
OpenFileName ofn );  
}

Calling Functions

Public Class App
   Public Shared Sub Main()
   
      Dim ofn As New OpenFileName()
      ofn.structSize = Marshal.SizeOf( ofn )
      ofn.filter = "Log files" & ChrW(0) & "*.log" & ChrW(0) & _
         "Batch files" & ChrW(0) & "*.bat" & ChrW(0)
      ofn.file = New String( New Char( 256 ) {})
      ofn.maxFile = ofn.file.Length
      ofn.fileTitle = New String( New Char( 64 ) {})
      ofn.maxFileTitle = ofn.fileTitle.Length
      ofn.initialDir = "C:\"
      ofn.title = "Open file called using platform invoke..."
      ofn.defExt = "txt"
      
      If LibWrap.GetOpenFileName( ofn ) Then
         Console.WriteLine( "Selected file with full path: {0}", _
         ofn.file )
         ...
      End If
   End Sub 'Main
End Class 'OpenFileDlgSample
[C#]
public class App
{
   public static void Main()
   {
      OpenFileName ofn = new OpenFileName();
      ofn.structSize = Marshal.SizeOf( ofn );
      ofn.filter = "Log files\0*.log\0Batch files\0*.bat\0";
      ofn.file = new String( new char[ 256 ]);
      ofn.maxFile = ofn.file.Length;
      ofn.fileTitle = new String( new char[ 64 ]);
      ofn.maxFileTitle = ofn.fileTitle.Length;
      ofn.initialDir = "C:\\";
      ofn.title = "Open file called using platform invoke...";
      ofn.defExt = "txt";
      
      if( LibWrap.GetOpenFileName( ofn ))
      {
         Console.WriteLine( "Selected file with full path: {0}", 
            ofn.file );
         ...
      }
   }
}

See Also

Marshaling Strings | Platform Invoke Data Types | Default Marshaling for Strings | Creating Prototypes in Managed Code