在 C# 中调用 DLL

ife*_*fer 3 c c# pinvoke

我想将结构传递给 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;
}
Run Code Online (Sandbox Code Playgroud)

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*ani 5

对于投票者:这个答案解决了两个问题:调用约定/MarhsalAs属性的直接问题,以及TTest如果他接受我的建议TTest变成结构体,他很快就会发现他的参数不起作用的问题。

您的本机代码要求一个void*,在 C# 中是一个IntPtr. 首先,您应该定义TTest为结构而不是类。其次,您应该将声明更改Foo为:

[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(IntPtr lplf);
Run Code Online (Sandbox Code Playgroud)

第三,你应该固定TTestusingfixed关键字并将它的指针传递给Foo。如果您使用的是类,你可以使用Marhsal.StructureToPtr得到一个IntPtr从你的TTest

这在两侧提供了相同的功能,可以传入指向任何类型的指针。您还可以使用要使用的所有类类型编写重载,因为它们都等同void*于本机端。对于结构体,您的参数将在前面加上ref.

我很好奇的是void*TTest*当您在非托管代码中执行的第一件事被强制转换为TTest*. 如果您将参数切换为 a TTest*,则提供相同的功能会变得更简单。您的声明将变为:

[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(ref TTest lplf);
Run Code Online (Sandbox Code Playgroud)

你会调用这个函数 Program.Foo(ref Test);

如果您使用的是类,ref则不需要,因为类是引用类型。