WIN32学习 之 Win32类型和.net类型的对应表》及感悟例子
来源:互联网 发布:python win32service 编辑:程序博客网 时间:2024/04/29 06:09
http://blog.csdn.net/AppleDotnet/article/details/19861
http://chris.blog.51cto.com/112473/29285/
string -> LPCTSTR (point to const string)
stringbuilder -> LPTSTR (point to string)
习惯用C#写东西,但平时又会碰到很多要用win32 API的地方,所以经常要用DllImport,但win32函数的类型写法是很庞杂的,相信为之困扰的不止我一个,现在我整理一份我个人的理解如下,希望高人不吝赐教。
我的基本原则有如下几点:
1、下面都是针对32位系统的,所以int是32位.long也是32位;
2、各种句柄类的(H开头),我认为一律是System.IntPtr,到目前为止没发现出错;如果哪位在使用中出错,请指出;
3、LP和P,我实在不懂(对C++不太了解),对于LP和P开头的函数,如果是和STR有关的,一律写为System.String,像PLCID这样指向什么东西的,写为System.UInt32(因为指向另一个地址,那就是指针,指针是32位吧),int之类的数值型,那我就写为int[],以方便.net程序引用(写成System.UInt32的话,给API调用应该也不会出错,但.net程序就不好引用了)
如有意见,欢迎指教: AppleDotnet@hotmail.com
BOOL=System.Int32
BOOLEAN=System.Int32
BYTE=System.UInt16
CHAR=System.Int16
COLORREF=System.UInt32
DWORD=System.UInt32
DWORD32=System.UInt32
DWORD64=System.UInt64
FLOAT=System.Float
HACCEL=System.IntPtr
HANDLE=System.IntPtr
HBITMAP=System.IntPtr
HBRUSH=System.IntPtr
HCONV=System.IntPtr
HCONVLIST=System.IntPtr
HCURSOR=System.IntPtr
HDC=System.IntPtr
HDDEDATA=System.IntPtr
HDESK=System.IntPtr
HDROP=System.IntPtr
HDWP=System.IntPtr
HENHMETAFILE=System.IntPtr
HFILE=System.IntPtr
HFONT=System.IntPtr
HGDIOBJ=System.IntPtr
HGLOBAL=System.IntPtr
HHOOK=System.IntPtr
HICON=System.IntPtr
HIMAGELIST=System.IntPtr
HIMC=System.IntPtr
HINSTANCE=System.IntPtr
HKEY=System.IntPtr
HLOCAL=System.IntPtr
HMENU=System.IntPtr
HMETAFILE=System.IntPtr
HMODULE=System.IntPtr
HMONITOR=System.IntPtr
HPALETTE=System.IntPtr
HPEN=System.IntPtr
HRGN=System.IntPtr
HRSRC=System.IntPtr
HSZ=System.IntPtr
HWINSTA=System.IntPtr
HWND=System.IntPtr
INT=System.Int32
INT32=System.Int32
INT64=System.Int64
LONG=System.Int32
LONG32=System.Int32
LONG64=System.Int64
LONGLONG=System.Int64
LPARAM=System.IntPtr
LPBOOL=System.Int16[]
LPBYTE=System.UInt16[]
LPCOLORREF=System.UInt32[]
LPCSTR=System.String
LPCTSTR=System.String
LPCVOID=System.UInt32
LPCWSTR=System.String
LPDWORD=System.UInt32[]
LPHANDLE=System.UInt32
LPINT=System.Int32[]
LPLONG=System.Int32[]
LPSTR=System.String
LPTSTR=System.String
LPVOID=System.UInt32
LPWORD=System.Int32[]
LPWSTR=System.String
LRESULT=System.IntPtr
PBOOL=System.Int16[]
PBOOLEAN=System.Int16[]
PBYTE=System.UInt16[]
PCHAR=System.Char[]
PCSTR=System.String
PCTSTR=System.String
PCWCH=System.UInt32
PCWSTR=System.UInt32
PDWORD=System.Int32[]
PFLOAT=System.Float[]
PHANDLE=System.UInt32
PHKEY=System.UInt32
PINT=System.Int32[]
PLCID=System.UInt32
PLONG=System.Int32[]
PLUID=System.UInt32
PSHORT=System.Int16[]
PSTR=System.String
PTBYTE=System.Char[]
PTCHAR=System.Char[]
PTSTR=System.String
PUCHAR=System.Char[]
PUINT=System.UInt32[]
PULONG=System.UInt32[]
PUSHORT=System.UInt16[]
PVOID=System.UInt32
PWCHAR=System.Char[]
PWORD=System.Int16[]
PWSTR=System.String
REGSAM=System.UInt32
SC_HANDLE=System.IntPtr
SC_LOCK=System.IntPtr
SHORT=System.Int16
SIZE_T=System.UInt32
SSIZE_=System.UInt32
TBYTE=System.Char
TCHAR=System.Char
UCHAR=System.Byte
UINT=System.UInt32
UINT32=System.UInt32
UINT64=System.UInt64
ULONG=System.UInt32
ULONG32=System.UInt32
ULONG64=System.UInt64
ULONGLONG=System.UInt64
USHORT=System.UInt16
WORD=System.UInt16
WPARAM=System.IntPtr
附:举一个声明API的例子
[ DllImport( "Kernel32.dll" )]
public static extern System.UInt32 VirtualAllocEx(
System.IntPtr hProcess,
System.UInt32 lpAddress,
System.UInt32 dwSize,
System.UInt32 flAllocationType,
System.UInt32 flProtect
);
=========================
Figure 2 Non-Pointer Data Types
Win32 Types
Specification
CLR Type
char, INT8, SBYTE, CHARâ€
8-bit signed integer
System.SByte
short, short int, INT16, SHORT
16-bit signed integer
System.Int16
int, long, long int, INT32, LONG32, BOOL†, INT
32-bit signed integer
System.Int32
__int64, INT64, LONGLONG
64-bit signed integer
System.Int64
unsigned char, UINT8, UCHAR†, BYTE
8-bit unsigned integer
System.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR†, __wchar_t
16-bit unsigned integer
System.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT
32-bit unsigned integer
System.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG
64-bit unsigned integer
System.UInt64
float, FLOAT
Single-precision floating point
System.Single
double, long double, DOUBLE
Double-precision floating point
System.Double
†In Win32 this type is an integer with a specially assigned meaning; in contrast, the CLR provides a specific type devoted to this meaning.
对于简单类型的对应,已经差不多就可以解决掉了。但是如果要将如下两个函数(从海量智能分词研究版中提供的接口函数中选择出来的,因为他提供了文档说明J适合做例子)转成C#中的版本,该如何操作呢?这就需要涉及到数据封送处理了。数据封送处理是 P/Invoke 具有挑战性的方面。当在托管和非托管代码之间传递数据时,就需要用到Marshal这个很有用的东西了。
Function 1:SHLSegWord*HLGetFileKeyAt(HANDLE hHandle, int iIndex);
Function 2:boolHLGetFingerM (HANDLE hHandle, LPBYTE &rpData, DWORD &rdwLen);
struct SHLSegWord
{
char *s_szWord;
DWORD s_dwPOS;
float s_fWeight ;
SHLSegWord()
{
Reset();
};
void Reset()
{
s_szWord = NULL ;
s_dwPOS = 0 ;
s_fWeight = 0;
};
};
对于Function 1,一开始,可能一看到他的返回值是一个struct,就会自然地想到使用C#写一个和SHLSegWord一摸一样的结构体,但是这样是不行的,因为Function 1返回的是一个SHLSegWord*,是一个指向结构体的指针,因此就不能使用C#中为值类型的struct来接收这个函数的返回值,是错误的。而C#中的class却是引用类型的,因此使用class是可行的,于是我就写了一个对应的:
namespace PInvoke.By.Jizhou
{
[StructLayout(LayoutKind.Sequential, CharSet =CharSet.Ansi)] //这个attribute是必须的,不然编译器会改变字段的顺序和大小,就没有办法做到内存对齐了
publicclass SHLSegWord
{
public string word = ""; //StringBuilder ??
publicint POS = 0;
publicfloat weight = 0;
}
}
接下来,用Marshal.SizeOf得到其大小,进行最初步的测试,看看它和在C/C++程序中用sizeof得到的大小是否相等,测试结果显示,相等,于是我就继续开始往下做,把Function 1写成:
[DllImport("HLSSplit.dll")]
publicstaticextern SHLSegWord HLGetWordAt(IntPtr hHandle,int nIndex);
编译,通过,OK!太爽了,没想到这么顺利,于是就开始测试。可是到函数一执行,就让我大跌眼镜。一下子崩出来如下exception:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏”,更让人郁闷的是,仔细查看后,发现循环调用这个函数时,居然是在index=5时出错,于是加上try catch,嘿,居然index=6,7,8,9就可以通过,但是index=10又不行,之后一直到24却又可以。观察了一下,出错的index之间没有任何联系,也找不出任何规律,郁闷ing。于是我将SHLSegWord结构体中的public string word = "";修改承了publicStringBuilder word = newStringBuilder(),可是问题依然存在。实在有点太诡异了,于是请教我的好朋友夏桅(CSDN上的suma),他是P/Invoke方面的牛人。他建议让我先把public string word = ""; 改成publicIntPtr word =newIntPtr(); 原因在于非内嵌的string不能放在传出参数里面,毕竟传出参数和传入参数是不同的,传出的话需要预先分配缓冲区,string是固定了值的类型,所以一般用stringbuilder。但是这个又是在结构体里面的string,稍微复杂一点,所以使用IntPtr应该是最好的方法了,因为IntPtr类型可以由支持指针的语言使用,并可作为在支持与不支持指针的语言间引用数据的一种通用方式。修改过后,错误仍然存在。仔细看看,发现原函数也返回的是一个指针,于是就觉得是不是应该也将publicstaticextern SHLSegWord HLGetWordAt(IntPtr hHandle,int nIndex);的返回类型,也修改成IntPtr,然后再使用Marshal.PtrToStructure()来接收数据,应该就比较安全了。果然,修改后,问题立马消失,至此,搞定。下面是较完整的示例代码:
1.SHLSegWord定义
namespace PInvoke.By.Jizhou
{
[StructLayout(LayoutKind.Sequential, CharSet =CharSet.Ansi)]
publicclassSHLSegWord
{
publicIntPtrword = newIntPtr();
publicint POS = 0;
publicfloat weight = 0;
}
}
2.HLGetWordAt函数定义
[DllImport("HLSSplit.dll")]
publicstaticextern IntPtr HLGetWordAt(IntPtr hHandle,int nIndex);
3.调用代码:
IntPtrwordPtr =HLSegFunc.HLGetWordAt(hHandle, i);
SHLSegWord wordStruct = (SHLSegWord)Marshal.PtrToStructure(wordPtr,typeof(SHLSegWord));
string word =Marshal.PtrToStringAnsi(wordStruct.word);
至此,Function 1的示例叙述完毕,下面看看Function 2:
bool HLGetFingerM (HANDLE hHandle, LPBYTE &rpData, DWORD&rdwLen);
这个家伙同样长的比较怪异,第一个参数比较好办,使用IntPtr即可,第三个参数,转化为int,然后使用C#的out修饰符即可。可是第二个参数是指向指针的指针,有点难办了,再看看海量给出来的demo的C++代码,rpData居然是一个数组,这下给接收数据带来了不小的麻烦,我尝试将他转成
bool HLGetFingerM (IntPtrhHandle, out byte[]rpData,outint rdwLen);
但是结果不正确。几经try,都得不到正确的结果,无奈中又想起了IntPtr这个救苦救难的兄弟,换成IntPtr一try,唉,还真好了。下面给出实现代码:
1. 函数定义:
bool HLGetFingerM (IntPtrhHandle, outIntPtrrpData,outint rdwLen);
2. 调用代码示例
IntPtr data =newIntPtr();
int dataLen = 0;
bool successOrNot =HLSegFunc.HLGetFingerM(hHandle,out data, out dataLen);
if (successOrNot)//获得语义指纹
{
for (int j = 0; j < dataLen; j++)
{
IntPtr dataSnippet =Marshal.ReadIntPtr(data, j);
string strU =Marshal.ReadByte(data, j).ToString("x2");
}
}
关于这个主题,可以参考以下资料:Marshaling Data with Platform Invoke、Platform Invoke Data Types、An Introduction to P/Invoke and Marshaling on the Microsoft .NET Compact Framework。
至此,全部完毕,总一个小的总结:
1. 在使用DllImport将Win32函数导入成C#对应的函数时,一定要注意Win32函数中的参数是传入还是传出,是值类型还是引用类型;
2. 对于C/C++中的复杂类型如Struct,转成C#对应的数据结构时,最好使用C#的Class而不是值类型的Struct;
3. 对于C/C++中的指针类型,最好使用IntPtr类型来进行数据的封送处理;
4. .NET的字符串在内存中总是以Unicode方式编码的,但对于API函数需要string的编码,有时取决于Windows版本,有时需要显式设定,有时又需要显式转换。
5. 使用与基础 API 函数类型不同但与之兼容的 CLR 类型是 P/Invoke 较难使用的一个方面,这就需要多多实践,积累经验。
6.上面费劲搞了这么多,就源于我没有DLL的源代码,如果你有C/C++ DLL的源代码,那么问题就简单多了。这里有解决方法:How do I mix C# and C++ code in a single assembly? 引用其方法如下:
How do I mix C# and C++ code in a single assembly?
If your C++ code is not compiled with /clr:safe (i.e. it is compiled with /clr or /clr:pure), do the following:
1) compile your C++ code into .obj files
2) compile your C# code into a .netmodule, using /AddModule to reference the C++ .obj files
3) link the C# netmodule directly with the C++ object files using the C++ linker to create a mixed language assembly
If your C++ code is compiled with /clr:safe, build your C++ code as a .netmodule. You can use it just like you would use a .netmodule from any other language.
This applies to .Net framework beta2+.
6. 如果你对P/Invoke感兴趣的话,这本书上面有提到一些,可以参考一下。.Net Compact Framework Programming with C#
- WIN32学习 之 Win32类型和.net类型的对应表》及感悟例子
- 《Win32类型和.net类型的对应表》及感悟例子
- Win32类型和.net类型的对应表
- Win32类型和.net类型的对应表
- Win32类型和.net类型的对应表
- Win32类型和.net类型的对应表
- Win32类型和.net类型的对应表
- Win32的时间类型
- Win32的时间类型
- Win32常用的 类型和宏
- Win32常用的 类型和宏
- Win32常用的 类型和宏
- Win32 API与C#数据结构类型对应关系表
- win32的输出int类型
- 常用Win32数据类型与.NET平台数据类型的对应表
- Hibernate映射类型、对应的Java时间和日期类型及对应的标准SQL类型
- .net和win32的区别
- 【转】WIN32编程中的HRESULT类型和LRESULT类型的区别
- bug:顺序错误导致CSS伪类覆盖问题
- JAVA 堆栈 堆 方法区 静态区 final static 内存分配 详解
- C语言编译过程总结详解
- C語言的記事
- HTTP GET 和 POST认知
- WIN32学习 之 Win32类型和.net类型的对应表》及感悟例子
- 并行计算的概念
- mac os x显示隐藏文件
- 区分Activity的四种加载模式----以及Intent的setFlags
- Loader
- ASP.NET网站在线更新注意事项
- rails常用命令
- 第十三周实验报告3
- 对VICVectCntl寄存器设置的理解