C# 中的 PInvoke DLL
我想将一个结构传递给 C 函数,并编写以下代码。
当我运行它时,第一个函数 - Foo1
正在工作,然后函数 Foo
出现异常。你能帮我理解问题是什么吗?...
C 代码:
typedef struct
{
int Size;
//char *Array;
}TTest;
__declspec(dllexport) void Foo(void *Test);
__declspec(dllexport) int Foo1();
void Foo(void *Test)
{
TTest *X = (TTest *)Test;
int i = X->Size;
/*for(int i=0;i<Test->Size;Test++)
{
Test->Array[i] = 127;
}*/
}
int Foo1()
{
return 10;
}
C# 代码:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential)]
public class TTest
{
public int Size;
}
class Program
{
[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern void Foo(
[MarshalAs(UnmanagedType.LPStruct)]
TTest lplf // characteristics
);
[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern int Foo1();
static void Main(string[] args)
{
TTest Test = new TTest();
Test.Size = 25;
int XX = Program.Foo1();
Program.Foo(Test);
}
}
}
I want to pass a structure to C function and I write the following code.
When I run it, the first function - Foo1
is working and then function Foo
gets an exception. Can you help me to understand what the problem is?...
The C code:
typedef struct
{
int Size;
//char *Array;
}TTest;
__declspec(dllexport) void Foo(void *Test);
__declspec(dllexport) int Foo1();
void Foo(void *Test)
{
TTest *X = (TTest *)Test;
int i = X->Size;
/*for(int i=0;i<Test->Size;Test++)
{
Test->Array[i] = 127;
}*/
}
int Foo1()
{
return 10;
}
The C# code:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential)]
public class TTest
{
public int Size;
}
class Program
{
[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern void Foo(
[MarshalAs(UnmanagedType.LPStruct)]
TTest lplf // characteristics
);
[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern int Foo1();
static void Main(string[] args)
{
TTest Test = new TTest();
Test.Size = 25;
int XX = Program.Foo1();
Program.Foo(Test);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
致反对者:这个答案解决了两个问题:调用约定/
MarhsalAs
属性的直接问题,以及他很快就会发现他的TTest<的问题如果他接受我将
TTest
转换为结构体的建议,那么 /code> 参数将不起作用。您的本机代码要求一个
void*
,在 C# 中是一个IntPtr
。首先,您应该将TTest
定义为结构而不是类。其次,您应该将Foo
的声明更改为:第三,您应该使用
fixed
关键字固定TTest
并将其指针传递给 <代码>Foo。如果您使用的是类,则可以使用Marhsal.StructureToPtr
从TTest
获取IntPtr
。这在两侧提供了相同的功能,可以传入指向任何类型的指针。您还可以使用要使用的所有类类型编写重载,因为它们都等同于
void*
本机方面。对于结构体,您的参数将前面加上ref
。我很好奇的是,当您在非托管代码中执行的第一件事被转换为
时,为什么您的本机代码需要
。如果您将参数切换为void*
而不是TTest*
>T测试*TTest*
,那么提供相同的功能就会变得更简单。您的声明将变为:并且您将调用该函数
Program.Foo(ref Test);
如果您使用该类,则
ref
作为类不需要是引用类型。To the downvoters: This answer solves two issues: the immediate issue of the calling convention/the
MarhsalAs
attribute, and the issue he will soon find where hisTTest
parameter won't work if he takes my suggestion of turningTTest
into a struct.Your native code is asking for a
void*
, which in C# is anIntPtr
. First you should defineTTest
as a struct and not a class. Second, you should change the declaration ofFoo
to:And third, you should pin the
TTest
using thefixed
keyword and pass it's pointer toFoo
. If you're using a class, you can useMarhsal.StructureToPtr
to get anIntPtr
from yourTTest
.This provides the same functionality on both sides, where a pointer to any type can be passed in. You can also write overloads with all the class types that you want to use since they all equate to
void*
on the native side. With a struct, your parameters would be prepended with aref
.What I'm curious about is why your native code wants a
void*
instead of aTTest*
when the first thing you do in the unmanaged code is cast to aTTest*
. If you switched the parameter to aTTest*
, then providing identical functionality becomes simpler. You declaration would become:And you would call the function as
Program.Foo(ref Test);
If you're using the class, the
ref
isn't necessary as classes are reference types.您正在使用 C 调用,因此您需要指定 CallingConvention.Cdecl
默认情况下,我记得它在 C# pinvoke 中的 stdcall ;您也可以更改 C 代码,并保留 C# 代码,如下所示。
但对我来说,最好是在 C 导出中声明 __cdecl (或 stdcall),并在 C# 代码中声明 CallingConvention.Cdecl (或 stdcall),以保持方便。您可以检查 https ://learn.microsoft.com/en-gb/cpp/cpp/argument-passing-and-naming-conventions?view=vs-2017 和https:// learn.microsoft.com/en-gb/dotnet/api/system.runtime.interopservices.callingconvention?view=netframework-4.7.2 了解更多信息
You are using C call so you need to specify CallingConvention.Cdecl
By default its stdcall in C# pinvoke as i remember; You can also do change C code instead and leave your C# code as is like in below
But for me best is to both declare __cdecl (or stdcall) in your C export and CallingConvention.Cdecl (or stdcall) in your C# code to keep convenience. You can check https://learn.microsoft.com/en-gb/cpp/cpp/argument-passing-and-naming-conventions?view=vs-2017 and https://learn.microsoft.com/en-gb/dotnet/api/system.runtime.interopservices.callingconvention?view=netframework-4.7.2 for further info