3

我正在尝试访问某些 DLL 导出的函数(用于访问某些特定硬件)。使用 DLLImport 和简单数据类型对函数的访问是有效的。但是对于某些功能,我需要传递一些相当大的结构。为了克服自动编组的大小限制,我想出了使用 IntPtr。

typedef struct
{
    double value[MemDepth];
    double offset[MemDepth];
    unsigned long start;
    BOOL active;
} RegisterData;

typedef struct 
{
    RegisterData a,b,c,d,e,f,g,h;
    BOOL active;
    BOOL test;
} Register;

这就是我尝试将它们放入 C# 的方式:

[StructLayout(LayoutKind.Sequential)]
public struct RegisterData
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
    public double[] value;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
    public double[] offset;

    UInt32 start;

    [MarshalAs(UnmanagedType.U1)]
    bool active;
}

[StructLayout(LayoutKind.Sequential)]
public struct Register
{
    RegisterData a,b,c,d,e,f,g,h;
    [MarshalAs(UnmanagedType.U1)]
    bool active;
    [MarshalAs(UnmanagedType.U1)]
    bool test;
}

我试图调用的函数在 DLL 中如下所示:

long ResetControl(Register* control);

我翻译成:

[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 Reset(IntPtr control);

然后我尝试像这样调用函数:

public Int32 lib_Reset(ref Register control)
{
    int size = System.Runtime.InteropServices.Marshal.SizeOf(control);

    //IntPtr ptr = System.Runtime.InteropServices.Marshal.FreeHGlobal(size);
    IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);

    System.Console.WriteLine("requested size of: " + size); // shows correct

    System.Runtime.InteropServices.Marshal.StructureToPtr(control, ptr, false);

    System.Console.WriteLine("mem done");

    Int32 ret = Reset(ptr);

    System.Console.WriteLine("called reset");

    System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, control);
    System.Console.WriteLine("transfered back");

    System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr);
    //System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr);

    System.Console.WriteLine("freeed mem");
    ptr = IntPtr.Zero;

    return ret;
}

这会导致“A 堆已损坏”。调用 DLL 重置函数时。我已经尝试了不同的方法,但没有奏效。

编辑:

我现在通过使用 IntPtr 方法清理和重建项目来让它工作。它不能直接使用“ref”。使用“ref”时,我总是得到:

 Cannot marshal 'parameter #1': Internal limitation: structure is too complex or too large.

我现在执行以下操作以从结构转换为指针并返回:

public Int32 lib_Reset(ref Register control)
{
    int size = Marshal.SizeOf(control);
    IntPtr ptr = Marshal.FreeHGlobal(size);
    Marshal.StructureToPtr(control, ptr, false);
    Int32 ret = Reset(ptr);
    control = (Register)Marshal.PtrToStructure(ptr, typeof(control));
    Marshal.FreeHGlobal(ptr);
    ptr = IntPtr.Zero;
    return ret;
}

这样我就可以调用重置,但似乎这些功能没有做任何事情。

Register reg = new Register();

reg.active = true;
System.Console.WriteLine(reg.active);   // .active = true
lib_Reset(ref reg);         // should set .active = false
System.Console.WriteLine(reg.active);   // .active = true

函数 Reset 应该初始化寄存器结构。从而将 active 设置为 false。但在上面显示的代码中,active 的值不会改变。我用整数值测试了相同的值。它也没有改变。

我希望这里的任何人都可以指出我访问数据的正确方向。

谢谢你,托比亚斯

4

1 回答 1

0

现在它起作用了。清理和重建项目起到了作用。它现在正在使用 IntPtr 方法。但是重置功能不会影响结构中的值。

从 DLL 的代码中,我知道元素应该被初始化为一些值。但是元素的值从调用之前到调用之后不会改变:

Register reg = new Register();

reg.active = true;
System.Console.WriteLine(reg.active);   // .active = true
lib_Reset(ref reg);         // should set .active = false
System.Console.WriteLine(reg.active);   // .active = true

其他元素也是如此。从结构到指针再从指针到结构的转换有什么问题吗?

如果我取一些整数类型的值,并在调用前将其设置为 4,则调用后将是 4。因此保留了之前设置的值。

于 2013-04-14T09:58:32.237 回答