///<summary>
///原型是:HMODULE LoadLibrary(LPCTSTR lpFileName);
///</summary>
///<param name="lpFileName">DLL 文件名</param>
///<returns>函数库模块的句柄</returns>
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lpFileName);
///<summary>
/// int GetClassName(HWND hWnd, LPTSTR lpClassName, int nMaxCount);
///</summary>
[DllImport("user32",CharSet=CharSet.Ansi)]
public static extern Int32 GetClassName(IntPtr hwnd, Byte[] lpClassName, Int32 nMaxCount);
调用事例:
String sClassName = null;
Byte[] abClassName = null;
Int32 dwRet = 0;
abClassName = new Byte[100];
dwRet = GetClassName(this.Handle, abClassName, 100);
sClassName = System.Text.ASCIIEncoding.ASCII.GetString(abClassName,0,dwRet);
MessageBox.Show(sClassName);
//<summary>
///原形:BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
///</summary>
[DllImport("kernel32.dll")]
public extern static Int32 ReadFile(IntPtr hFile, Byte[] buffer,Int32 nNumberOfBytesToRead, ref Int32 lpNumberOfByte
对于C/C++中数组参数,就是一块连续内存的首地址。在C#中的数组默认都是从Array派生的类,虽然结构复杂了,但是内存布局应该是相同的,所以只要把参数定义为基本类型的数组就可以了。例如:
///<summary>
/// BOOL GetCharWidth(HDC hdc,UINT iFirstChar,UINT iLastChar,LPINT lpBuffer);
///</summary>
[DllImport("gdi32")]
public static extern Int32 GetCharWidth(HDC hdc, Int32 wFirstChar, Int32 wLastChar, int32[] lpBuffer);
///<summary>
///原形:HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest);
///</summary>
///<param name="pGuidSrc"></param>
///<param name="pGuidDest"></param>
///<returns></returns>
[DllImport("Dsound.dll")]
private static extern Int32 GetDeviceID(ref Guid pGuidSrc, ref Guid pGuidDest);
如果自定义结构的话,结构在内存中占据的字节数务必要匹配,当结构中包含数组的时候需要用MarshalAsAttribute属性进行修饰,设定数组长度。具体可以参考下面的例子:
///<summary>
///原形:
/// typedef struct tagPAINTSTRUCT {
/// HDC hdc;
/// BOOL fErase;
/// RECT rcPaint;
/// BOOL fRestore;
/// BOOL fIncUpdate;
/// BYTE rgbReserved[32];
/// } PAINTSTRUCT;
///</summary>
public struct PAINTSTRUCT
{
public IntPtr hdc;
public Boolean fErase;
public RECT rcPaint;
public Boolean fRestore;
public Boolean fIncUpdate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]public Byte[] rgbReserved;
}
///<summary>
///原形:HDC BeginPaint( HWND hwnd, LPPAINTSTRUCT lpPaint);
///</summary>
[DllImport("user32")]
public static extern IntPtr BeginPaint(IntPtr hwnd, ref PAINTSTRUCT lpPaint);
///<summary>
///原形:
/// typedef struct tagACCEL {
/// BYTE fVirt;
/// WORD key;
/// WORD cmd;
/// } ACCEL, *LPACCEL;
///</summary>
public struct ACCEL
{
public Byte fVirt;
public UInt16 key;
public UInt16 cmd;
}
///<summary>
///原形:int CopyAcceleratorTable(HACCEL hAccelSrc,LPACCEL lpAccelDst,int cAccelEntries);
///</summary>
///<returns></returns>
[DllImport("user32")]
public static extern Int32 CopyAcceleratorTable(IntPtr hAccelSrc, ACCEL[] lpAccelDst, Int32 cAccelEntries);
[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct CmBoxInfo
{
public static CmBoxInfo Empty = new CmBoxInfo();
public byte MajorVersion;
public byte MinorVersion;
public ushort BoxMask;
public uint SerialNumber;
public ushort BoxKeyId;
public ushort UserKeyId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CM_PUBLIC_KEY_LEN)] public byte[] BoxPublicKey;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CM_PUBLIC_KEY_LEN)] public byte[] SerialPublicKey;
public uint Reserve;
public void Init()
{
BoxPublicKey = new byte[CM_PUBLIC_KEY_LEN];
Debug.Assert(BoxPublicKey != null);
SerialPublicKey = new byte[CM_PUBLIC_KEY_LEN];
Debug.Assert(SerialPublicKey != null);
}
}
///<summary>
///原型:int CMAPIENTRY CmGetBoxes(HCMSysEntry hcmse, unsigned long idPort, CMBOXINFO *pcmBoxInfo, unsigned int cbBoxInfo)
///</summary>
[DllImport("xyz.dll")]
private static extern Int32 CmGetBoxes(IntPtr hcmse, CmGetBoxesOption idPort,IntPtr pcmBoxInfo, Int32 cbBoxInfo);
调用示例
IntPtr hcmBoxes = IntPtr.Zero;
CmAccess cma = new CmAccess();
CmBoxInfo[] aBoxList = null;
Int32 dwBoxNum = 0, dwLoop = 0,dwBoxInfoSize = 0;
IntPtr pBoxInfo = IntPtr.Zero;
dwBoxNum = m_pCmGetBoxes(hcmBoxes, CmGetBoxesOption.AllPorts, IntPtr.Zero, 0);
if (dwBoxNum > 0)
{
aBoxList = new CmBoxInfo[dwBoxNum];
if (aBoxList != null)
{
dwBoxInfoSize = Marshal.SizeOf(aBoxList[0]);
pBoxInfo = Marshal.AllocHGlobal(dwBoxInfoSize * dwBoxNum);
if (pBoxInfo != IntPtr.Zero)
{
dwBoxNum = m_pCmGetBoxes(hcmBoxes, CmGetBoxesOption.AllPorts, pBoxInfo, dwBoxNum);
for (dwLoop = 0; dwLoop < dwBoxNum; dwLoop++)
{
aBoxList[dwLoop] = (CmBoxInfo)Marshal.PtrToStructure((IntPtr)((UInt32)pBoxInfo + dwBoxInfoSize * dwLoop), CmBoxInfo.Empty.GetType());
}
Marshal.FreeHGlobal(pBoxInfo);
pBoxInfo = IntPtr.Zero;
}
else
{
aBoxList = null;
}
}
}
最后提一句,Marshal类非常有用,其中包括了大量内存申请、复制和类型转换的函数,灵活运用的话,基本上可以避免unsafe code。
///<summary>
///原形:typedef BOOL (CALLBACK *LPDSENUMCALLBACKA)(LPGUID, LPCSTR, LPCSTR, LPVOID);
///</summary>
public delegate Boolean LPDSENUMCALLBACK(IntPtr guid, String sDesc, String sDevName, ref Int32 dwFlag);
///<summary>
///原形:HRESULT WINAPI DirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
///</summary>
[DllImport("Dsound.dll")]
public static extern Int32 DirectSoundCaptureEnumerate(LPDSENUMCALLBACK pDSEnumCallBack, ref Int32 dw
具体调用方法如下:
dwRet = DirectSoundEnumerate(new LPDSENUMCALLBACK(DSoundEnumCallback),ref dwFlag);
///<summary>
///原形:BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
///</summary>
[DllImport("kernel32.dll")]
public extern static Int32 ReadFile(IntPtr hFile, Byte[] buffer,Int32 nNumberOfBytesToRead, ref Int32 lpNumberO
第二种情况比较复杂,C#中类型转换是有限制的,一个Int32是没法直接转换成为Point的,这个时候之能够根据不同的参数类型定义不同的重载函数了。例如GetProcAddress函数的lpProcName既可以是一个字符串表示函数名,又可以是一个高字为0的Int32类型,表示函数的序号,我们可以这样分别定义:
///<summary>
///原型是: FARPROC GetProcAddress(HMODULE hModule,LPCSTR lpProcName);
///</summary>
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
private extern static IntPtr GetProcAddress(IntPtr hModule, String sFuncName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
private extern static IntPtr GetProcAddressByIndex(IntPtr hModule, Int32 dwIndex);
在这里总结了调用API时有关指针的一些常见问题,你会发现大多数情况下C#依靠自身的能力就能解决问题,希望对大家有帮助。
怎样处理UNION。
C/C++中UNION是这样定义:
“联合”是一种特殊的类,也是一种构造类型的数据结构。在一个“联合”内可以定义多种不同的数据类型,一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,已达到节省空间的目的(还有一个节省空间的类型:位域)。这是一个非常特殊的地方,也是联合的特征。另外,同struct一样,联合默认访问权限也是公有的,并且,也具有成员函数。
首先UNION和结构(STRUCT)一样是值类型,两者最大的不同在于内部成员所占用的空间上。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。所以在使用上和结构是一致的,关键在于定义不同。C#中没有UNION关键字,我们就用struct,外加一点技巧来实现。例如:
/// <summary>
/// 原型:typedef struct tagINPUT {
/// DWORD type;
/// union {MOUSEINPUT mi;
/// KEYBDINPUT ki;
/// HARDWAREINPUT hi;
/// };
/// }INPUT, *PINPUT;
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public UInt32 type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
这里“[StructLayout(LayoutKind.Explicit)]”的修饰很重要,就是表明结构内的成员按照设定的位置在内存中排列。上面的例子中mi、ki、hi三个成员都是结构体内便宜的第四个字节开始的,也就暗合了UNION内的成员共享同一段内存(首地址相同)。UNION能够处理了,API的申明就很简单了,和STRUCT完全一致,例如:
/// <summary>
/// The SendInput function synthesizes keystrokes, mouse motions, and button clicks.
/// 原型:UINT SendInput(UINT nInputs,LPINPUT pInputs, int cbSize);
/// </summary>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern UInt32 SendInput(UInt32 nInputs,ref INPUT pInputs,Int32 cbSize);