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