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 OpenFileDlg sample uses the following unmanaged function, shown with its original function declaration:

  • GetOpenFileName exported from Comdlg32.dll.

    BOOL GetOpenFileName(LPOPENFILENAME lpofn);
    

The LPOPENFILENAME structure from the Win32 API, which is passed to the previous 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 hwnd As IntPtr = IntPtr.Zero
    Public hinst As IntPtr = IntPtr.Zero
    Public filter As String = Nothing
    Public custFilter As String = Nothing
    Public custFilterMax As Integer = 0
    Public filterIndex As Integer = 0
    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 flags As Integer = 0
    Public fileOffset As Short = 0
    Public fileExtMax As Short = 0
    Public defExt as String = Nothing
    Public custData As Integer = 0
    Public pHook As IntPtr = IntPtr.Zero
    Public template As String = Nothing
End Class

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
// Declare a class member for each structure element.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public class OpenFileName
{
    public int       structSize = 0;
    public IntPtr    hwnd = IntPtr.Zero;
    public IntPtr    hinst = IntPtr.Zero;
    public string    filter = null;
    public string    custFilter = null;
    public int       custFilterMax = 0;
    public int       filterIndex = 0;
    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 int       flags = 0;
    public short     fileOffset = 0;
    public short     fileExtMax = 0;
    public string    defExt = null;
    public int       custData = 0;
    public IntPtr    pHook = IntPtr.Zero;
    public string    template = 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);
}
// Declare a class member for each structure element.
[StructLayout(LayoutKind::Sequential, CharSet=CharSet::Auto)]
public ref class OpenFileName
{
public:
    int       structSize;
    IntPtr    hwnd;
    IntPtr    hinst;
    String^   filter;
    String^   custFilter;
    int       custFilterMax;
    int       filterIndex;
    String^   file;
    int       maxFile;
    String^   fileTitle;
    int       maxFileTitle;
    String^   initialDir;
    String^   title;
    int       flags;
    short     fileOffset;
    short     fileExtMax;
    String^   defExt;
    int       custData;
    IntPtr    pHook;
    String^   tmplate;

    OpenFileName()
    {
        // Initialize the fields.
        for each (FieldInfo^ fi in this->GetType()->GetFields())
        {
            fi->SetValue(this, nullptr);
        }
    }
};

public ref class LibWrap
{
public:
    // Declare a managed prototype for the unmanaged function.
    [DllImport("Comdlg32.dll", CharSet=CharSet::Auto)]
    static 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
End Class
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);
        }
    }
}
public ref class App
{
public:
    static void Main()
    {
        OpenFileName^ ofn = gcnew OpenFileName();

        ofn->structSize = Marshal::SizeOf(ofn);
        ofn->filter = "Log files\0*.log\0Batch files\0*.bat\0";
        ofn->file = gcnew String(gcnew array<Char>(256));
        ofn->maxFile = ofn->file->Length;
        ofn->fileTitle = gcnew String(gcnew array<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

Concepts

Marshaling Strings

Platform Invoke Data Types

Default Marshaling for Strings

Other Resources

Creating Prototypes in Managed Code