该示例演示如何调用需要指向包含字符串的结构的指针的非托管函数。此外,它还演示如何使用托管类来表示非托管结构,如何应用 InAttribute 和 OutAttribute 属性将该类封送回调用方,以及如何声明并初始化该类的不同字段以产生正确的非托管表示形式。
OpenFileDlg 示例使用以下非托管函数(这里同时显示其原始函数声明):
从 Comdlg32.dll 导出的 GetOpenFileName。
BOOL GetOpenFileName(LPOPENFILENAME lpofn);
BOOL GetOpenFileName(LPOPENFILENAME lpofn);
传递给上一函数的 LPOPENFILENAME 结构(来自 Win32 API)包含以下元素:
typedef struct tagOFN {
DWORD lStructSize;
//…
LPCTSTR lpstrFilter;
//…
LPTSTR lpstrFile;
DWORD nMaxFile;
LPTSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCTSTR lpstrInitialDir;
LPCTSTR lpstrTitle;
//…
LPCTSTR lpstrDefExt;
//…
} OPENFILENAME, *LPOPENFILENAME;
typedef struct tagOFN {
DWORD lStructSize;
//…
LPCTSTR lpstrFilter;
//…
LPTSTR lpstrFile;
DWORD nMaxFile;
LPTSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCTSTR lpstrInitialDir;
LPCTSTR lpstrTitle;
//…
LPCTSTR lpstrDefExt;
//…
} OPENFILENAME, *LPOPENFILENAME;
在该示例中,OpenFileName 类包含作为类成员的原始结构的元素。非托管结构被声明为类而不是托管结构,以显示当非托管函数需要一个指向结构的指针时可以如何使用类。由于托管类是引用类型,因此当它通过值传递时,指向该类的指针将被传递给非托管代码中。这正是非托管函数所需要的。
将 StructLayoutAttribute 属性应用于该类,以确保成员在内存中按它们的出现顺序依次排列。CharSet 字段设置为使平台调用可以在运行时根据目标平台在 ANSI 和 Unicode 格式之间进行选择。
LibWrap 类包含 GetOpenFileName 方法的托管原型,该方法将 OpenFileName 类作为 In/Out 参数传递。通过显式应用 InAttribute 和 OutAttribute,该示例确保将 OpenFileName 作为 In/Out 参数进行封送处理并且调用方可以查看封送回的更改。(为了提高性能,类的默认方向属性为 In,以防止调用方查看封送回的更改。) App 类创建 OpenFileName 类的一个新实例,并使用 Marshal.SizeOf 方法确定非托管结构的大小(以字节为单位)。
下面的代码示例的源代码由 .NET Framework 平台调用技术示例提供。
声明原型
// 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 );
}
// 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 );
}
调用函数
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 );
}
}
}