在.Net Core中工作的互操作在Framework中失败
我有一个国家仪器SPI设备的包装器——它在.Net内核中工作得非常好。我想用一个UI包装它,所以将同一个包装器推到了.Net Framework上。在那里,大多数调用都有效,但有一个失败了……我尝试了不同的输入签名,但运气不好,老实说,我对它为什么在.NETCore3.0中有效而在Framework4.7.2中无效感到惊讶 我还制作了一个.Net标准包装器项目,并在两个应用程序中引用。同一个包装器dll在.Net core项目中工作,但在Framework中不工作!这里发生了什么?似乎.Net core在如何处理互操作方面有一些额外的启发。问题是如何/为什么?.Net core Winforms alpha插件不稳定或不稳定ull的功能足够我使用,我真的需要它在一个标准的.Net框架项目中工作 它可以用于配置、设置DIO等,但当我们尝试执行以下操作时,它会崩溃在.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
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”。