Marshal在C#中的应用(void *指针到IntPtr的转化)
? C#調用C語言的API時一般把void *指針轉換成IntPtr,但這經常遠遠不夠的。在C語言中void *是個萬金油,尤其是一些老的c語言程序,所有的參數就一個void*指針,里面包羅萬象,然后在程序中來一個switch,甚至多個switch來處理不同的參數。最近筆者就碰到了這個問題,不得不來研究一下怎么把void *指針轉換成IntPtr。
1.void *指針到IntPtr的簡單轉化。
c語言函數原型:
int SetConfig(int type, void *p);
這里假設p的所傳遞的參數式是結構體A:
| struct?A??????????? { ????wchar_t?osdbuffer[100];???????????? ????unsigned short?ix;????????????????????? ????unsigned short?iy;????????????????????? }; |
那么在C#中原型可以定義如下:
int SetConfig(int type, IntPtr p);
結構體A?
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] ????????public?struct?A { ????????????[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] ????????????public?string?osdbuffer;??????? ????????????public?ushort?ix;?????????????????????? //顯示坐標x ????????????public?ushort?iy;?????????????????????? //顯示坐標y ????????} |
注意這里的CharSet,它由c中wchar_t決定的,如果c程序編譯時使用Unicode,這里就用CharSet.Unicode,否則使用CharSet.Ansi。關于字符串的編碼問題如果不懂可以去網上查一下。至于怎么知道C語言是用Unicode還是Ansi編譯,我是經過調用它的API測試出來的,調用成功了就說明他的編碼和我的調用代碼一致。
這里還有一個很重要的問題,那就是內存在編譯時的分配問題。一般默認情況下,內存的分配是4byte的整數倍,在這里我省略了,但為了便于理解,補充一下。結構體A完整一點的定義:(注意Pack的值)
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode,Pack = 4)] ????????public?struct?A { ????????????[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] ????????????public?string osdbuffer;??????? ????????????public?ushort ix;?????????????????????? //顯示坐標x ????????????public?ushort iy;?????????????????????? //顯示坐標y ????????} |
c語言是用的是非托管代碼,c#使用的是托管代碼,c#的調用代碼如下:
A s_a = new A();int lenght = Marshal.SizeOf(s_a);IntPtr pA= Marshal.AllocHGlobal(lenght);Marshal.StructureToPtr(s_a, pA, true);int type = 1;int ret = SetConfig( type, pA);Marshal.FreeHGlobal(pA);2.void *指針到IntPtr的復雜轉化。
在這里結構體A變得復雜一點,如果它內部包含一個指向另一個結構體B的指針
struct A {wchar_t osdbuffer[100]; unsigned short ix; unsigned short iy;、B *pB; }; struct B {wchar_t title[20]; };在C#中你要做的也就稍微復雜一點,也就是說你不但要為A分配內存,也要為B分配內存
B s_b = new B();//賦值省略int lenght1 = Marshal.SizeOf(s_b);IntPtr pB= Marshal.AllocHGlobal(lenght1);Marshal.StructureToPtr(s_b, pB, true);A s_a = new A();s_a.pB = pB;//其他賦值//int lenght2 = Marshal.SizeOf(s_a);IntPtr pA= Marshal.AllocHGlobal(lenght2);Marshal.StructureToPtr(s_a, pA, true);int type = 1; int ret = SetConfig( type, pA);Marshal.FreeHGlobal(pB);Marshal.FreeHGlobal(pA);萬變不離其宗,只要掌握了原理,不管void *指針傳遞的參數有多么復雜,都可以搞定。
總結
以上是生活随笔為你收集整理的Marshal在C#中的应用(void *指针到IntPtr的转化)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新手git教程
- 下一篇: 编程语言API性能大比拼