C# 方法的类型签名与 pinvoke 不兼容

C# method's type signature is not pinvoke compatible

我想使用 Omron V4KU 的 API,文档描述如下:

原始 c# 代码:

    const string DllLocation = @"..\..\Libs\Omron\OMCR.dll";

    [DllImport(DllLocation)]
    public static extern LPCOMCR OMCR_OpenDevice(string lpcszDevice, LPCOMCR_OPTION lpcOption);

    public void Start()
    {
        var lpcOption = new LPCOMCR_OPTION();
        var result = OMCR_OpenDevice(null, lpcOption); // error method's type signature is not pinvoke compatible
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct LPCOMCR
    {
        public string lpcszDevice;
        public IntPtr hDevice;
        public uint lpcDevice;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct LPCOMCR_OPTION
    {
        public uint dwReserved0;
        public uint dwReserved1;
        public uint dwReserved2;
        public uint dwReserved3;
    }

如果我在写代码时遗漏或错误? 对不起,我的英语不好。感谢您的帮助。

嗯,一方面,文档要求您将 LPCOMCR_OPTION 作为指针传递 - 您将其作为值传递。使用 ref 应该有所帮助。但是,还有另一个问题,那就是 return 值——同样,您试图将它解释为一个值,而文档说它是一个指针。但是,这比第一个错误要棘手得多——据我所知,您唯一的选择是使用 C++/CLI 互操作库,或者期望 IntPtr 作为 return 值。在任何情况下,您都需要处理以这种方式获得的内存的正确释放。

你需要这样做:

[StructLayout(LayoutKind.Sequential)]
public struct OMCR
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string lpcszDevice;
    public IntPtr hDevice;
    public IntPtr lpcDevice;
}

[StructLayout(LayoutKind.Sequential)]
public struct OMCR_OPTION
{
    public uint dwReserved0;
    public uint dwReserved1;
    public uint dwReserved2;
    public uint dwReserved3;
}

[DllImport(DllLocation, CallingConvention = CallingConvention.???,
    SetLastError = true)]
public static extern IntPtr OMCR_OpenDevice(string lpcszDevice, 
    ref OMCR_OPTION lpcOption);

您需要将 CallingConvention.??? 替换为适当的调用约定。我们无法从问题中分辨出那是什么。您将必须通过阅读头文件来找出答案。

return 值是指向 OMCR 的指针。当你完成它时,你需要抓住这个指针并把它传递给OMCR_CloseDevice

为了获得 OMCR 值,您需要执行以下操作:

OMCR_OPTION Option = new OMCR_OPTION(); // not sure how to initialize this
IntPtr DevicePtr = OMCR_OpenDevice(DeviceType, ref Option);
if (DevicePtr == IntPtr.Zero)
    throw new Win32Exception();
OMCR Device = (OMCR)Marshal.PtrToStructure(DevicePtr, typeof(OMCR));

从正确定义联合结构开始:

// OMCR_OPTION.COM
[StructLayout(LayoutKind.Sequential)]
public struct OmcrCom
{
    public IntPtr Reserved0;
    public uint BaudRate;
    public uint Reserved1;
    public uint Reserved2;
    public uint Reserved3;
    public IntPtr Reserved1;
    public IntPtr Reserved2;
}

// OMCR_OPTION.USB
[StructLayout(LayoutKind.Sequential)]
public struct OmcrUsb
{
    public uint Reserved0;
    public uint Reserved1;
    public uint Reserved2;
    public uint Reserved3;
}

// OMCR_OPTION (union of COM and USB)
[StructLayout(LayoutKind.Explicit)]
public struct OmcrOptions
{
    [FieldOffset(0)]
    public OmcrCom Com;

    [FieldOffset(0)]
    public OmcrUsb Usb;
}

// OMCR
[StructLayout(LayoutKind.Sequential)]
public struct OmcrDevice
{
    public string Device;
    public IntPtr DeviceHandle;
    public IntPtr DevicePointer;
}

[DllImport(dllName: DllLocation, EntryPoint = "OMCR_OpenDevice"]
public static extern IntPtr OmcrOpenDevice(string type, ref OmcrOptions options);

然后调用方法,类似于:

var options = new OmcrOptions();
options.Com.BaudRate = 115200; // or whatever you need to set

var type = "COM"; // is this USB/COM? not sure

OmcrDevice device;

var devicePtr = OmcrOpenDevice(type, ref options);
if (devicePtr == IntPtr.Zero)
    device = (OmcrDevice)Marshal.PtrToStructure(devicePtr, typeof(OmcrDevice));