我需要将指向结构的指针传递给我的DLL,任何想法我将如何去做?
在我的C DLL中:
typedef struct
{
int length;
unsigned char *value;
} Sample;
__declspec(dllexport) void __stdcall helloWorld( Sample *sample );
在我的C#代码中:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CSharpConsole
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct Sample
{
public Int32 length;
// What Should I Declare Here?
}
[DllImport("C:\\CTestDLL.dll")]
private static extern void helloWorld( Sample sample ); // How would I make this a pointer?
void HelloWorld()
{
Sample sample = new Sample();
sample .length = 20;
// How can I fill up the values of value?
helloWorld( sample ); // How should I pass it inside here
return;
}
static void Main(string[] args)
{
Program program = new Program();
program.HelloWorld();
}
}
}
答案 0 :(得分:3)
要将指向值类型的指针传递给P / Invoke函数,只需将参数声明为ref
或out
即可。这隐含地引用了参数并传递了:
[DllImport("C:\\CTestDLL.dll")]
private static extern void helloWorld(ref Sample sample);
由于你的结构中有一个数组,你必须小心地声明它才能正常工作。如果可能的话,我强烈建议你把它变成一个固定长度的数组,因为它会使编组程序更加快乐:
typedef struct
{
int length;
unsigned char value[MAX_LENGTH];
} Sample;
这变为:
public struct Sample
{
int length;
[MarshalAs(UnmanagedType.LPArray, SizeConst = MAX_LENGTH)] byte[] value;
}
如果那是不可能的,那么运行时无法知道要编组多少数据;在这种情况下,您可能不得不求助于手动编组数据:
public struct Sample
{
int length;
IntPtr value;
}
var sample = new Sample();
helloWorld(ref sample);
byte[] value = new byte[sample.length];
Marshal.Copy(sample.value, value, 0, sample.Length);
但是,根据你对另一个答案的评论,看起来你只需要从C DLL中获取一个字节块到C#中。为此你根本不需要一个结构,消除它会简化很多事情。如果您只是想传入一个数组并将其填入并返回给您,请尝试以下方法:
(这假设您可以控制C和C#代码库;如果没有,则ref
建议是实现所需内容的方法。)
// In C# code:
[DllImport(@"C:\CTestDll.dll")]
private static extern void helloWorld(
int length,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] byte[] buffer);
byte[] buffer = new byte[1024 * 8];
helloWorld(1024 * 8, buffer);
// In C:
__declspec(dllexport) void __stdcall helloWorld(int, unsigned char *);
void helloWorld(int cb, unsigned char *buf)
{
memcpy(buf, DATASRC, cb);
}
在C中,根据定义,无符号字符与C#字节大小相同--8位,无符号。在C#中,char实际上是两个字节。运行时会自动将unsigned char *
“转换”为byte[]
。 (根本没有真正的转换;它们只是同一类型的不同名称。)
答案 1 :(得分:1)
这对我有用:
DLL:
typedef struct
{
int length;
unsigned char *value;
} Sample;
extern "C" __declspec(dllexport) void __stdcall helloWorld( Sample *sample )
{
MessageBoxA(NULL, (LPCSTR) sample->value, (LPCSTR) sample->value, 0);
}
C#:
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private class Sample
{
public Int32 length;
public String value;
}
[DllImport("C:\\Users\\Kep\\Documents\\Visual Studio 2010\\Projects\\SODLL\\Debug\\DLL.dll")]
private static extern void helloWorld(Sample sample);
static void Main(string[] args)
{
Sample s = new Sample();
s.length = 10;
s.value = "Huhu";
helloWorld(s);
}
}
重要的是将其标记为类,而不是C#中的结构。
有了这个,您还可以在C#中使用构造函数等:
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private class Sample
{
public Int32 length;
public String value;
public Sample(String s)
{
length = s.Length;
value = s;
}
}
[DllImport("C:\\Users\\Kep\\Documents\\Visual Studio 2010\\Projects\\SODLL\\Debug\\DLL.dll")]
private static extern void helloWorld(Sample sample);
static void Main(string[] args)
{
Sample s = new Sample("Huhu");
helloWorld(s);
}
}
答案 2 :(得分:1)
Default Marshaling for Value Types - 提供有关编组结构的一些信息 Calling Win32 DLLs in C# with P/Invoke - 在页面的一半左右,有一个表格显示标准非托管和托管类型之间的类型转换。
有几种传递和转换类型的方法 正如迈克尔·伊登菲尔德指出的那样,你通常可以只使用ref关键字来指示参数应该通过引用传递,这基本上是一个指针。
但是有时事情不合作,特别是在字符串或复杂数据类型方面,所以这就是IntPtr类型的用武之地。 您有几种使用IntPtrs的选项 您可以使用以下命令将IntPtr创建为指定大小的非托管内存块:
IntPtr pointer = Marshal.AllocHGlobal(sizeOfBufferInBytes);
//Do stuff with the pointer
Marshal.FreeHGlobal(pointer); //Don't forget to release the memory
这显然有点危险,因为您手动分配非托管内存 您需要使用Marshal.Copy(),Marshal.PtrToStructure(),Buffer.BlockCopy()等方法将缓冲区中的数据备份回托管类型。
或者,您可以创建托管对象并在需要时将其固定在内存中,获取指向它的指针并将其传递给您的方法。
MyObject instance = new MyObject();
GCHandle gch = GCHandle.Alloc(instance, GCHandleType.Pinned);
importedMethod(gch.AddrOfPinnedObject()); //AddrOfPinnedObject() gives you an IntPtr
gch.Free(); //Release the pinned memory so the garbage collector can deal with it
这避免了手动编组回正确数据类型的必要性,但这并不总是一个选项,具体取决于MyObject的类型以及它是否是blittable。