从C#程序调用C DLL
本文关键字:DLL 调用 程序 | 更新日期: 2023-09-27 18:24:59
我需要向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();
}
}
}
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#中,字符实际上是两个字节。运行时会自动将unsigned char *
"转换"为byte[]
。(实际上根本没有转换;它们只是同一类型的不同名称。)
值类型的默认封送-提供了有关封送结构的一些信息
用P/Invoke调用C#中的Win32 DLL——在页面的一半多一点的地方,有一个表显示了标准非托管类型和托管类型之间的类型转换。
有几种方法可以传递和转换类型
正如Michael Edenfield所指出的,通常可以使用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的类型以及它是否是可blitable的。
这对我有效:
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);
}
}