高手们,请教个问题,关于Pinvoke的 谢谢楼上,msdn上我是没搜到对照表的,另外,pinvoke有一个专门的网站,上面有很多的对应,但是pinvoke上面的对应也不是统一的,而且我还下了一个小软件,专门转换pinvoke类型的,好像还是ms自己出的一个。这两个上面的有些类型在使用过程中,并不是唯一的统一的标准,而是可以采用其他的方式来代替的 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 你说的很对。因为API是Native C写的,类型很不完善。说实话,C基本只有一种类型,就是32bit整数。特别是SendMessage这个函数,我们知道,lParam wParam这两个参数根据消息不同含义不同,所以在C#使用,我们经常定义出很多版本,比如有时候让lParam直接是字符串甚至结构体类型,这样更明确,使用更简单。 一个优秀的程序员,不仅要编写“正确”的程序,同时要编写更好的程序。好比ArrayList也能完成List<T>的功能,但是我们更倾向选择后者,类型更清晰,意味着调用的明确、代码可读性的提高,更好的编译器检查。你给api传int或者intptr,你就需要自己保证传的类型的正确——比如你可能传了无效的指针。但是如果你用string,那么编译器就阻止你传非字符串(的指针),这不是更好么。 先说简单的,如果是Win32的API,那就去pinvoke.net上找,一般都能用。如果是其它的库,可能根据需要自己来确定用什么方式。你主要提了int和string,我先说int,因为C/C++那边的int不是定长的,而是“平台”尺寸的,导致32位是32位的int,64位是64位的int,但是C#确定了int是32位的,所以在pinvoke的时候,如果C#这边也用int,那么对64位的c/c++库可能会出问题,这个时候为了同时兼容32位和64位,就可以用IntPtr了,因为它也是变长的(这里IntPtr没有当作指针使用,而是当作一个整型)。DWORD是确定32位的,在C#这边应该用int,用IntPtr的话如果是64位的方式会有问题。string就更复杂一点,因为涉及编码方式(还有比如在COM中非0结尾的string的处理)。默认的string处理通过attribute的设置能支持ansi,平台默认(中文系统就是GB2312或其超集),和Unicode三种编码。对于.net,string内部始终是unicode方式的,但是对于C/C++,本身没有字符串这个类型,就是一串byte。比如C/C++那边需要一个utf8编码的字符串,就没法直接用string传递了,因为默认的情况没法支持utf8编码,这个时候就必须用IntPtr传递了,先把字符串用utf8编码方式转换成字节数组,然后分配非托管内存,把转换后的内容copy过去,然后传递这个非托管内容的指针。当C/C++那边不需要这个字符串了,再释放掉分配的内存。所以对string用IntPtr是最底层的方式,但是也最麻烦。像你说的 char *,那就看这个在C/C++那边到底是做怎么用的。情况也比较多:如果不考虑中文,只是最简单的ASCII,那传递的时候就直接用ANSI方式的string,最简单。如果考虑中文,但是不考虑在中文平台以外使用,那一般用平台默认方式的string就好了。如果考虑中文,还考虑在中文平台以外使用,而C/C++那边要求必须是gb2312的编码,那就只好自己转码用IntPtr方式了。用IntPtr还有个原因是生命周期,大部分情况下pinvoke的时候会自动分配非托管内存,根据需要的编码转换并copy那个string到分配的内存,然后传递这个指针,在pinvoke调用结束后,这个分配的内存会被回收。也就是说如果C/C++那边在调用结束后还需要用这个字符串,要不然自己再copy遍,要不然就是用IntPtr,在托管环境管理这个字符串的生命周期。最后说StringBuilder,这个一般是用来做双向传递的,StringBuilder有内部的buffer内存空间,传递StringBuilder到C/C++那边其实就是直接传递了StringBuilder的buffer地址,这样就支持C/C++直接对这块内存进行修改。我写了半天,其实考虑的情况也有限,具体的情况还是要去看msdn了,其实没有标准的转换方式,只能自己搞明白原理然后根据实际情况来 http://msdn.microsoft.com/en-us/library/zah6xy75(v=vs.110).aspx 谢谢上面的回复你们写的东西,我都认真的看过了,只不过这两天实在有其他的事情耽搁了,一直没机会上网,所以今天中午才看到。其实你们都是一个意思,具体情况要具体分析。caozhy,你说的很对,这种做法的确是在提供SDK的时候的最好做法,用一种约束来减少后期的二次开发错误几率。winnowc ,首先感谢你手写这么多····你的意思我明白,win32之前我也用过,只不过这个pinvoke让我有点拿不定主意。通过你们的回复,我看出了一点东西,那就是pinvoke其实对类型的转换并没有那么严格,当c中需要什么类型,我给他一个类似的类型就行了,但是具体的这个类型是不是管用,还是要用过才知道,对吧? 请问大家关于音频传输的问题??(解决给分) 模态对话框的返回值问题 c# ? C# 和 C语言 编译出来的结果为什么不一样? 请教数据集的问题 asp.net 页面如何改变默认的button按钮 就是说在文本框回车能出发它的点击事件的按钮 在主窗口中点击菜单某项,弹出对应的窗口的问题 combox绑定到dataview,如何实现关联? 知道键值,怎么转换成字符! Excel中Cell的格式设置问题 异常来自HRESULT: 0X800AC472 新手求教,程序看不懂,关于折线图的坐标。
因为C/C++那边的int不是定长的,而是“平台”尺寸的,导致32位是32位的int,64位是64位的int,但是C#确定了int是32位的,所以在pinvoke的时候,如果C#这边也用int,那么对64位的c/c++库可能会出问题,这个时候为了同时兼容32位和64位,就可以用IntPtr了,因为它也是变长的(这里IntPtr没有当作指针使用,而是当作一个整型)。DWORD是确定32位的,在C#这边应该用int,用IntPtr的话如果是64位的方式会有问题。string就更复杂一点,因为涉及编码方式(还有比如在COM中非0结尾的string的处理)。默认的string处理通过attribute的设置能支持ansi,平台默认(中文系统就是GB2312或其超集),和Unicode三种编码。对于.net,string内部始终是unicode方式的,但是对于C/C++,本身没有字符串这个类型,就是一串byte。比如C/C++那边需要一个utf8编码的字符串,就没法直接用string传递了,因为默认的情况没法支持utf8编码,这个时候就必须用IntPtr传递了,先把字符串用utf8编码方式转换成字节数组,然后分配非托管内存,把转换后的内容copy过去,然后传递这个非托管内容的指针。当C/C++那边不需要这个字符串了,再释放掉分配的内存。所以对string用IntPtr是最底层的方式,但是也最麻烦。像你说的 char *,那就看这个在C/C++那边到底是做怎么用的。情况也比较多:
如果不考虑中文,只是最简单的ASCII,那传递的时候就直接用ANSI方式的string,最简单。
如果考虑中文,但是不考虑在中文平台以外使用,那一般用平台默认方式的string就好了。
如果考虑中文,还考虑在中文平台以外使用,而C/C++那边要求必须是gb2312的编码,那就只好自己转码用IntPtr方式了。用IntPtr还有个原因是生命周期,大部分情况下pinvoke的时候会自动分配非托管内存,根据需要的编码转换并copy那个string到分配的内存,然后传递这个指针,在pinvoke调用结束后,这个分配的内存会被回收。也就是说如果C/C++那边在调用结束后还需要用这个字符串,要不然自己再copy遍,要不然就是用IntPtr,在托管环境管理这个字符串的生命周期。最后说StringBuilder,这个一般是用来做双向传递的,StringBuilder有内部的buffer内存空间,传递StringBuilder到C/C++那边其实就是直接传递了StringBuilder的buffer地址,这样就支持C/C++直接对这块内存进行修改。我写了半天,其实考虑的情况也有限,具体的情况还是要去看msdn了,其实没有标准的转换方式,只能自己搞明白原理然后根据实际情况来 http://msdn.microsoft.com/en-us/library/zah6xy75(v=vs.110).aspx
你们写的东西,我都认真的看过了,只不过这两天实在有其他的事情耽搁了,一直没机会上网,所以今天中午才看到。
其实你们都是一个意思,具体情况要具体分析。caozhy,你说的很对,这种做法的确是在提供SDK的时候的最好做法,用一种约束来减少后期的二次开发错误几率。
winnowc ,首先感谢你手写这么多····你的意思我明白,win32之前我也用过,只不过这个pinvoke让我有点拿不定主意。通过你们的回复,我看出了一点东西,那就是pinvoke其实对类型的转换并没有那么严格,当c中需要什么类型,我给他一个类似的类型就行了,但是具体的这个类型是不是管用,还是要用过才知道,对吧?