如何将void *转换为可以在C#中使用的类型? C DLL和C#之间的互操作性 [英] How to convert a void* to a type that can be used in C#? Interoperability between C DLL and C#
问题描述
我是一名C / C ++程序员,但我被要求更新用C#编写的程序来与设备通信。我对C#的了解非常基础。之前的版本完全是用C#编写的,但现在实际访问该设备的API已更改为C.我发现我可以使用以下命令导入C函数API:
[DllImport(myapi.dll)]
public static extern int myfunct(
[MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
IntPtr hpDevice);
在C中这个函数原型是:
int myFunct(LPTStr lpDeviceName,HANDLE * hpDevice);
其中HANDLE定义为:
typedef void * HANDLE;
但是这个功能没有按预期工作。事实上,在C#代码调用中我应该声明什么样的类型并传递给C#方法?
//稍后编辑我为假答案aplogize。 br />
我现在面临一个新问题。我有一个C结构,如:
struct MyStruct
{unsigned char * ptr;
int ptr_size;}
这个指针由C API函数填充值,因此在CI中只传递给函数一个足以容纳数据的缓冲区。
要将其转换为C#方式,我可以这样做:
[ StructLayout(LayoutKind.Sequential)]
public class MyStruct
{IntPtr ptr; //这是指向
int size的缓冲区的指针; //由C API填充值
}; //接下来我实例化itMyStruct ms = new MyStruct();
现在,奇怪的部分进来。我有一个简单的C#数组:
byte [ ] RESET = new byte [] {0x00,0x00,0x80,0x00};
好的,现在ptr必须指向RESET,所以怎么可以这样做? Ptr需要保留它自己的地址....
非常感谢你的帮助!
你的代码看起来好像是正确的,我怀疑问题更可能是你传递给C例程的参数的实际值。尝试使用调试器或添加一些日志代码来验证您的参数。
在我的(有限)经验中,这里有三个潜在的问题。
1.正如理查德建议的那样 - 你是否使用正确的参数值?特别是hpdevice
。这是您之前从另一个非托管函数调用中检索的指针吗?如果没有,那可能是你的问题。
2.使用P / Invoke的字符串可能很麻烦。尝试使用System.Text.StringBuider
,因为它内置了编组功能。
[DllImport(< span class =code-string> myapi.dll)]
public static extern int myfunct(
StringBuilder lpDeviceName,
IntPtr hpDevice);
3.不太可能但是hpDevice
应该是指针的指针?这种情况在C / C ++中很常见,并且有时会弄清楚正在发生的事情。
问题2
问题使用byte []
它是否可以在内存中移动。要停止,你需要固定RESET。请特别关注GCHandle结构:
GCHandle.Alloc(object,GCHandleType.Pinned)
并且不要忘记使用
GCHandle.Free()
完成后,获取指针
GCHandle.AddrOfPinnedObject ()
Hi,
I am a C/C++ programmer, but I was asked to update a program that was written in C# to communicate with a device. My knowledge of C# is very basic. The previous version was totally written in C#, but now the API that in fact access the device was changed to C. I found out that I can import the C function APIs by using:
[DllImport("myapi.dll")] public static extern int myfunct( [MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName, IntPtr hpDevice);
In C this function prototype is:
int myFunct( LPTStr lpDeviceName, HANDLE* hpDevice );
Where HANDLE is defined as :
typedef void *HANDLE;
However this function does not work as expected. In fact, in the C# code call what kind of type I should declare and pass to the C# method?
// Later edit I aplogize for the fake answer.
I am facing a new problem now. I have a C structure like:
struct MyStruct { unsigned char* ptr; int ptr_size;}
This pointer filled with values by a C API function, thus in C I only pass to the function a buffer large enough to hold the data.
To convert this to C# way I can do:
[StructLayout(LayoutKind.Sequential)] public class MyStruct { IntPtr ptr;// this is a pointer to a buffer that will int size; // be filled with values by the C API };// Next I instantiate itMyStruct ms = new MyStruct();
Now, the odd part comes in. I have a simple C# array as :
byte[] RESET = new byte[]{ 0x00, 0x00, 0x80, 0x00};
OK, now ptr must point to RESET, so how could this be done? Ptr needs to hold the address it self....
Thanks a lot for the help!
Well your code would appear to be correct, I suspect the problem is more likely to be with the actual values of the parameters that you are passing to the C routine. Try using the debugger or add some logging code to verify your parameters.
In my (limited) experience there are three potential issues here.
1. As Richard suggested - are you using correct values for the parameters? Particularlyhpdevice
. Is this a pointer that you have previously retrieved from another unmanaged function call? If not, that could be your issue.
2. Strings with P/Invoke can be troublesome. Try using aSystem.Text.StringBuider
instead as it has built in marshalling capabilities.
[DllImport("myapi.dll")] public static extern int myfunct( StringBuilder lpDeviceName, IntPtr hpDevice);
3. Unlikely but ishpDevice
supposed to be a pointer to a pointer? This happens alot in C/C++ and it's a pain sometimes to work out what's going on.
To question 2
The problem with yourbyte[]
is it can be moved around in memory. To stop that you would need to pin RESET. Have a look at the GCHandle structure inparticular:
GCHandle.Alloc(object, GCHandleType.Pinned)
and DON'T FORGET to use
GCHandle.Free()
when done with it, and to get the pointer
GCHandle.AddrOfPinnedObject()
.
这篇关于如何将void *转换为可以在C#中使用的类型? C DLL和C#之间的互操作性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!