在.Net Core中工作的互操作在Framework中失败

在.Net Core中工作的互操作在Framework中失败,.net,.net-core,interop,.net,.net Core,Interop,我有一个国家仪器SPI设备的包装器——它在.Net内核中工作得非常好。我想用一个UI包装它,所以将同一个包装器推到了.Net Framework上。在那里,大多数调用都有效,但有一个失败了……我尝试了不同的输入签名,但运气不好,老实说,我对它为什么在.NETCore3.0中有效而在Framework4.7.2中无效感到惊讶 我还制作了一个.Net标准包装器项目,并在两个应用程序中引用。同一个包装器dll在.Net core项目中工作,但在Framework中不工作!这里发生了什么?似乎.Net

我有一个国家仪器SPI设备的包装器——它在.Net内核中工作得非常好。我想用一个UI包装它,所以将同一个包装器推到了.Net Framework上。在那里,大多数调用都有效,但有一个失败了……我尝试了不同的输入签名,但运气不好,老实说,我对它为什么在.NETCore3.0中有效而在Framework4.7.2中无效感到惊讶

我还制作了一个.Net标准包装器项目,并在两个应用程序中引用。同一个包装器dll在.Net core项目中工作,但在Framework中不工作!这里发生了什么?似乎.Net core在如何处理互操作方面有一些额外的启发。问题是如何/为什么?.Net core Winforms alpha插件不稳定或不稳定ull的功能足够我使用,我真的需要它在一个标准的.Net框架项目中工作

它可以用于配置、设置DIO等,但当我们尝试执行以下操作时,它会崩溃

 NI845x.ni845xSpiWriteRead(handle, SPIHandle, (uint)B.Length, B, ref ReadSize, ref ReadMsg);
有没有人对如何使其工作或如何解决问题有什么好的想法?谢谢

签名如下:

public class NI845x
{
    public const byte kNi845x33Volts = 33; // 3.3V
    public const byte kNi845x25Volts = 25; // 2.5V
    public const byte kNi845x18Volts = 18; // 1.8V
    public const byte kNi845x15Volts = 15; // 1.5V
    public const byte kNi845x12Volts = 12; // 1.2V

    public const byte kNi845xSpiClockPolarityIdleLow = 0;// Idle Low
    public const byte kNi845xSpiClockPolarityIdleHigh = 1; // Idle High

    public const byte kNi845xSpiClockPhaseFirstEdge = 0; // First Edge
    public const byte kNi845xSpiClockPhaseSecondEdge = 1; // Second Edge

    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xFindDevice(StringBuilder FirstDevice, ref ulong FindDeviceHandle, ref UInt32 NumberFound);

    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiWriteRead(ulong DeviceHandle, ulong ConfigurationHandle, UInt32 WriteSize, byte[] WriteData, ref UInt32 ReadSize, ref byte[] ReadData);

    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xOpen(StringBuilder ResourceName, ref ulong DeviceHandle);


    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSetIoVoltageLevel(ulong DeviceHandle, byte VoltageLevel);

    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiConfigurationOpen(ref ulong SPIHandle);

    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiConfigurationSetChipSelect(ulong SPIHandle, UInt32 ChipSelect);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiConfigurationSetClockRate(ulong SPIHandle, UInt16 ClockRate);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiConfigurationSetClockPolarity(ulong SPIHandle, Int32 ClockPolarity);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiConfigurationSetClockPhase(ulong SPIHandle, Int32 ClockPhase);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSpiConfigurationClose(ulong DeviceHandle);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xClose(ulong DeviceHandle);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xDioWriteLine(ulong DeviceHandle, byte PortNumber, byte LineNumber, Int32 WriteData);
    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xDioSetPortLineDirectionMap(ulong DeviceHandle, byte PortNumber, byte Map);

    [DllImport("Ni845x.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern Int32 ni845xSetTimeout(ulong DeviceHandle, UInt32 Timeout);
以下是NI的.h文件:

#ifndef __Ni845x_HEADER__
#define __Ni845x_HEADER__

#ifdef __cplusplus
   extern "C" {
#endif

#if defined(WIN32)
   #define NI845X_FUNC  __stdcall
#elif defined(WIN64)
   #define NI845X_FUNC __fastcall
#elif defined(macosxU)
   #define NI845X_FUNC
#endif

#if (defined(WIN32) || defined(WIN64))
   #if defined(kExportSymbols)
      #define kNI845XExport
   #else
      #define kNI845XExport
   #endif
#elif defined (macosxU)
   #if defined(kExportSymbols)
      #if ( __GNUC__ >= 4 )
         #define kNI845XExport __attribute__((visibility("default")))
      #else
         #define kNI845XExport __attribute__ ((section ("__TEXT,__export")))
      #endif
   #else
      #define kNI845XExport
   #endif
#endif

#ifdef _CVI_
   #pragma EnableLibraryRuntimeChecking
#endif

#ifndef _NI_int8_DEFINED_
#define _NI_int8_DEFINED_
   typedef char            int8;
#endif

#ifndef _NI_uInt8_DEFINED_
#define _NI_uInt8_DEFINED_
   typedef unsigned char   uInt8;
#endif

#ifndef _NI_int16_DEFINED_
#define _NI_int16_DEFINED_
   typedef short           int16;
#endif

#ifndef _NI_uInt16_DEFINED_
#define _NI_uInt16_DEFINED_
   typedef unsigned short  uInt16;
#endif

#ifndef _NI_int32_DEFINED_
#define _NI_int32_DEFINED_
   typedef long            int32;
#endif

#ifndef _NI_uInt32_DEFINED_
#define _NI_uInt32_DEFINED_
   typedef unsigned long   uInt32;
#endif

#if defined(WIN64)
   typedef unsigned long long NiHandle;
#elif defined(WIN32)
   typedef unsigned long NiHandle;
#endif

typedef struct {
   uInt32 dimSize;
   uInt8 elt[1];
} Array1DU8_t;
typedef Array1DU8_t **Array1DU8Handle_t;


#define kNi845xWarningClockRateCoerced             301700
#define kNi845xWarningSpiSampleDataIgnored         301701
#define kNi845xWarningUnknown                      301719


#define kNi845xErrorNoError                        0
#define kNi845xErrorInsufficientMemory             -301700
#define kNi845xErrorInvalidResourceName            -301701
#define kNi845xErrorInvalidClockRate               -301702
#define kNi845xErrorTooManyScriptReads             -301703
#define kNi845xErrorInvalidScriptReadIndex         -301704
#define kNi845xErrorInvalidScriptReference         -301705
#define kNi845xErrorInvalidDeviceId                -301706
#define kNi845xErrorConnectionLost                 -301707
#define kNi845xErrorTimeout                        -301708
#define kNi845xErrorInternal                       -301709
#define kNi845xErrorInvalidConfigurationReference  -301710
#define kNi845xErrorTooManyConfigurations          -301711
#define kNi845xErrorInvalidActiveProperty          -301712
#define kNi845xErrorInvalidParameter               -301713
#define kNi845xErrorResourceBusy                   -301714
#define kNi845xErrorInvalidMasterCode              -301715
#define kNi845xErrorMasterCodeAck                  -301716
#define kNi845xErrorOverCurrentError               -301718
#define kNi845xErrorSpiStreamingModeNotSupported   -301780
#define kNi845xErrorI2cSlaveModeNotSupported       -301781
#define kNi845xErrorInvalidI2cSlaveEventResponse   -301782
#define kNi845xErrorI2cSlaveEventPending           -301783

#define kNi845xErrorUnknown                        -301719


//=====================================
//
// General Errors from the device
//
//=====================================
#define kNi845xErrorBadOpcode                      -301720
#define kNi845xErrorUnknownStatus                  -301721
#define kNi845xErrorProtocolViolation              -301722
#define kNi845xErrorInvalidScript                  -301723
#define kNi845xErrorInvalidFirmware                -301724
#define kNi845xErrorIncompatibleFirmware           -301725

//=====================================
//
// SPI Errors from the device
//
//=====================================
#define kNi845xErrorMasterWriteCollision           -301730
#define kNi845xErrorInvalidSpiPortNumber           -301732
#define kNi845xErrorInvalidCsPortNumber            -301733
#define kNi845xErrorInvalidChipSelect              -301734
#define kNi845xErrorInvalidBitsPerSample           -301735

//=====================================
//
// I2C Errors from the device
//
//=====================================
#define kNi845xErrorMasterBusFreeTimeout           -301740
#define kNi845xErrorMasterCodeArbLost              -301741
#define kNi845xErrorMasterAddressNotAcknowledged   -301742
#define kNi845xErrorMasterDataNotAcknowledged      -301743
#define kNi845xErrorMasterAddressArbitrationLost   -301744
#define kNi845xErrorMasterDataArbitrationLost      -301745
#define kNi845xErrorInvalidI2CPortNumber           -301746

//=====================================
//
// DIO Errors from the device
//
//=====================================
#define kNi845xErrorInvalidDioPortNumber           -301750
#define kNi845xErrorInvalidDioLineNumber           -301751

//=====================================
//
// SPI Streaming Errors from the device
//
//=====================================
#define kNi845xErrorInStreamingMode                -301717
#define kNi845xErrorNotInStreamingMode             -301760


//=====================================
//
// SPI Function Arguments
//
//=====================================

#define kNi845xSpiClockPolarityIdleLow             0 // Idle Low
#define kNi845xSpiClockPolarityIdleHigh            1 // Idle High

#define kNi845xSpiClockPhaseFirstEdge              0 // First Edge
#define kNi845xSpiClockPhaseSecondEdge             1 // Second Edge

//=====================================
//
// DIO Function Arguments
//
//=====================================

#define kNi845xDioInput                            0 // DIO Direction Input
#define kNi845xDioOutput                           1 // DIO Direction Output

#define kNi845xDioLogicLow                         0 // DIO Level Low
#define kNi845xDioLogicHigh                        1 // DIO Level High

//=====================================
//
// Generic Function Arguments
//
//=====================================

#define kNi845xOpenDrain                           0 // Open Drain
#define kNi845xPushPull                            1 // Push Pull

#define kNi845x33Volts                             33 // 3.3V
#define kNi845x25Volts                             25 // 2.5V
#define kNi845x18Volts                             18 // 1.8V
#define kNi845x15Volts                             15 // 1.5V
#define kNi845x12Volts                             12 // 1.2V

//These defines are deprecated and the 845x Driver Type and IO Voltage Level
//should be used instead
#define kNi845xOpenDrain                           0 // Open-Drain
#define kNi845xPushPull33Volts                     1 // 3.3V Push-Pull



kNI845XExport int32 NI845X_FUNC ni845xFindDevice (
   char *     FirstDevice,
   NiHandle * FindDeviceHandle,
   uInt32 *   NumberFound
   );

kNI845XExport int32 NI845X_FUNC ni845xFindDeviceNext (
   NiHandle FindDeviceHandle,
   char *   NextDevice
   );

kNI845XExport int32 NI845X_FUNC ni845xCloseFindDeviceHandle(
   NiHandle FindDeviceHandle
   );

kNI845XExport int32 NI845X_FUNC ni845xOpen(
   char *     ResourceName,
   NiHandle * DeviceHandle
   );

kNI845XExport int32 NI845X_FUNC ni845xClose (
   NiHandle DeviceHandle
   );

kNI845XExport int32 NI845X_FUNC ni845xDeviceLock (
   NiHandle DeviceHandle
   );

kNI845XExport int32 NI845X_FUNC ni845xDeviceUnlock (
   NiHandle DeviceHandle
   );

kNI845XExport void NI845X_FUNC ni845xStatusToString(
   int32  StatusCode,
   uInt32 MaxSize,
   int8 * StatusString
   );

kNI845XExport int32 NI845X_FUNC ni845xSetIoVoltageLevel(
   NiHandle DeviceHandle,
   uInt8    VoltageLevel
   );


kNI845XExport int32 NI845X_FUNC ni845xSetTimeout(
   NiHandle DeviceHandle,
   uInt32   Timeout
   );


//==============================================================================
//
// SPI BASIC API FUNCTION PROTOTYPES
//
//==============================================================================


kNI845XExport int32 NI845X_FUNC ni845xSpiWriteRead(
   NiHandle DeviceHandle,
   NiHandle ConfigurationHandle,
   uInt32   WriteSize,
   uInt8  * WriteData,
   uInt32 * ReadSize,
   uInt8  * ReadData
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationOpen (
   NiHandle * ConfigurationHandle
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationClose (
   NiHandle ConfigurationHandle
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationSetChipSelect (
   NiHandle ConfigurationHandle,
   uInt32 ChipSelect
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationSetClockRate (
   NiHandle ConfigurationHandle,
   uInt16 ClockRate
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationSetClockPolarity (
   NiHandle ConfigurationHandle,
   int32  ClockPolarity
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationSetClockPhase (
   NiHandle ConfigurationHandle,
   int32  ClockPhase
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationSetNumBitsPerSample (
   NiHandle ConfigurationHandle,
   uInt16   NumBitsPerSample
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationSetPort (
   NiHandle ConfigurationHandle,
   uInt8  PortNumber
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationGetChipSelect (
   NiHandle ConfigurationHandle,
   uInt32 * ChipSelect
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationGetClockRate (
   NiHandle ConfigurationHandle,
   uInt16 * ClockRate
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationGetClockPolarity (
   NiHandle ConfigurationHandle,
   int32 * ClockPolarity
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationGetClockPhase (
   NiHandle ConfigurationHandle,
   int32 * ClockPhase
   );

int32 NI845X_FUNC ni845xSpiConfigurationGetNumBitsPerSample (
   NiHandle ConfigurationHandle,
   uInt16 * NumBitsPerSample
   );

kNI845XExport int32 NI845X_FUNC ni845xSpiConfigurationGetPort (
   NiHandle ConfigurationHandle,
   uInt8 * PortNumber
   );


kNI845XExport int32 NI845X_FUNC ni845xDioWritePort (
   NiHandle DeviceHandle,
   uInt8    PortNumber,
   uInt8    WriteData
   );

kNI845XExport int32 NI845X_FUNC ni845xDioReadPort (
   NiHandle DeviceHandle,
   uInt8    PortNumber,
   uInt8 *  ReadData
   );

kNI845XExport int32 NI845X_FUNC ni845xDioWriteLine (
   NiHandle DeviceHandle,
   uInt8    PortNumber,
   uInt8    LineNumber,
   int32    WriteData
   );

kNI845XExport int32 NI845X_FUNC ni845xDioReadLine (
   NiHandle DeviceHandle,
   uInt8    PortNumber,
   uInt8    LineNumber,
   int32 *  ReadData
   );

//This function has been deprecated and ni845xDioSetDriverType and
//ni845xSetIoVoltageLevel should be used instead.
kNI845XExport int32 NI845X_FUNC ni845xDioSetPortVoltageType (
   NiHandle DeviceHandle,
   uInt8    PortNumber,
   uInt8    Type
   );

kNI845XExport int32 NI845X_FUNC ni845xDioSetDriverType(
   NiHandle DeviceHandle,
   uInt8    DioPort,
   uInt8    Type
   );

kNI845XExport int32 NI845X_FUNC ni845xDioSetPortLineDirectionMap (
   NiHandle DeviceHandle,
   uInt8    PortNumber,
   uInt8    Map
   );



#ifdef __cplusplus
   }
#endif

#endif // __Ni845x_HEADER__
此签名似乎适用于.net framework:

[DllImport("Ni845x.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern Int32 ni845xSpiWriteRead(ulong DeviceHandle, ulong ConfigurationHandle, UInt32 WriteSize, [In] byte[] WriteData,  out UInt32 ReadSize, [Out] byte[] ReadData);

那么,有没有关于.Net Core如何/为什么正确处理“out”声明的文档?

请参见上面的编辑以获得答案。只是一直插上插头--现在两者都可以使用。我猜.Net Core是跨平台的,它在PInvoke和Interop方面有更多/更好的启发方法?有没有什么原因导致在.Net Framewo上没有使用相同的方法rk?是否有任何文档说明.Net Core如何/为什么会有不同的做法?

作为参考,这似乎是可行的:[DllImport(“Ni845x.dll”,CallingConvention=CallingConvention.StdCall,CharSet=CharSet.Ansi)]公共静态外部Int32 ni845xSpiWriteRead(ulong DeviceHandle,ulong ConfigurationHandle,UInt32 WriteSize,[In]字节[]WriteData,out UInt32 ReadSize,[out]byte[]ReadData])。NETCore版本是意外工作的,这是由默认情况下以64位模式运行引起的。NETFramework项目默认情况下以32位模式运行。此时,不正确的
ulong
参数声明开始严重损坏字节。“设备句柄”"是IntPtr,不是ulong。最好调用NI btw,你不是第一个需要实现此功能的.NET程序员。Hans,我强制使用32位和64位模式,但对.NET Framework是否工作没有任何影响。如果这也是个问题,为什么所有的DIO和其他函数在.NET Framework中都工作得很好?只是WriteRead函数没有work.NI在这方面并没有什么帮助——他们只是告诉你“嘿,这是Interop上的MSDN文档,试试看。”你还可以在.h文件中看到,“NiHandle”在他们的C代码中被定义为一个ulong。所以,我们复制了它。C中的“unsigned long”在C中是uint。“unsigned long”是ulong。所以它需要是IntPtr。不正确地控制位是一个常见的错误。不要添加新的配置,保留AnyCPU并使用Project>Properties>Build选项卡。C程序确实控制位——在64位以上时使用unsigned long long,在不使用时使用regular。显然,它工作正常,正如我所说的那样句柄及其位几乎没有任何问题;许多使用它的函数都能完美地工作。请参阅NiHandle的typedef。在这一过程中,我仍然想了解为什么作为x64项目运行的.Net Core和.Net Framework对ni845xSpiWriteRead函数中的UInt32 ReadSize变量执行不同的操作,one与“ref”配合良好,另一个需要“out”。