我正在尝试使用来自C#的非托管API,并将我的头撞在墙上。 (当谈到PInvoke时,我相当初学者。)
头文件的相关部分如下所示:
#define CTAPICALL __stdcall
#ifdef __cplusplus
extern "C" {
#endif
extern BOOL CTAPICALL ctTagReadEx(HANDLE,LPCSTR,LPSTR,DWORD,CT_TAGVALUE_ITEMS*); /* read extended data from tag */
#ifdef __cplusplus
}
#endif
CT_TAGVALUE_ITEMS
看起来像这样:
typedef struct
{
DWORD dwLength; /* size, in bytes, of this structure */
unsigned __int64 nTimestamp; /* timestamp */
unsigned __int64 nValueTimestamp; /* value timestamp */
unsigned __int64 nQualityTimestamp; /* quality timestamp */
BYTE bQualityGeneral; /* quality general */
BYTE bQualitySubstatus; /* quality substatus */
BYTE bQualityLimit; /* quality limit */
BYTE bQualityExtendedSubstatus; /* quality extended substatus */
UINT nQualityDatasourceErrorCode; /* quality datasource error */
BOOLEAN bOverride; /* quality override flag */
BOOLEAN bControlMode; /* quality control mode flag */
} CT_TAGVALUE_ITEMS;
我的C#方法声明:
[DllImport("ctapi.dll", SetLastError = true)]
public static extern bool ctTagReadEx(
IntPtr hCTAPI,
[MarshalAs(UnmanagedType.LPStr)] string tag,
[MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder value,
int length,
CtTagValueItems tagValueItems);
C#struct:
[StructLayout(LayoutKind.Sequential)]
public struct CtTagValueItems
{
public int dwLength;
public ulong nTimestamp;
public ulong nValueTimestamp;
public ulong nQualityTimestamp;
public byte bQualityGeneral
public byte bQualitySubstatus;
public byte bQualityLimit;
public byte bQualityExtendedSubstatus;
public uint nQualityDatasourceErrorCode;
public uint bOverride;
public uint bControlMode;
}
当我将其称为(来自构建为x86的测试程序集)时,我得到System.AccessViolationException : Attempted to read or write protected memory
:
StringBuilder valueBuilder = new StringBuilder(300);
CtTagValueItems tagValueItems = new CtTagValueItems {dwLength = Marshal.SizeOf(typeof (CtTagValueItems))};
bool ok = CTAPI.ctTagReadEx(new IntPtr(handle), "TIC_Hold_PV", valueBuilder, valueBuilder.Capacity, tagValueItems);
我一直在尝试各种各样的事情,例如使用LayoutKind.Explicit
和/或CallingConvention = CallingConvention.Cdecl
,但无济于事。
有人可以帮忙吗?
答案 0 :(得分:2)
UINT
映射为ushort
。它有4个字节吗?BOOLEAN
类型映射到4个字节,AFAIK。CtTagValueItems
(作为类或ref
)。答案 1 :(得分:1)
问题可能是对齐的。尝试像
这样的东西StructLayout(LayoutKind.Sequential, Pack = 1)
答案 2 :(得分:1)
C#调用中的handle
变量来自哪里?
我更喜欢在IntPtr
方法定义中使用DllImport
。通过这种方式管理和编组似乎更容易。
我已经改变了struct
定义的相当一部分,因为我没有和你一样的定义。我的ctTagReadEx
函数中也没有太多的正文(我会尝试充实它以确保传入的参数与收到的参数相匹配)。但这对我有用。
更新:看起来所有参数和结构值都正确传递。
<强> C 强>
typedef struct
{
int dwLength; /* size, in bytes, of this structure */
unsigned long nTimestamp; /* timestamp */
unsigned long nValueTimestamp; /* value timestamp */
unsigned long nQualityTimestamp; /* quality timestamp */
int bQualityGeneral; /* quality general */
int bQualitySubstatus; /* quality substatus */
int bQualityLimit; /* quality limit */
int bQualityExtendedSubstatus; /* quality extended substatus */
unsigned int nQualityDatasourceErrorCode; /* quality datasource error */
int bOverride; /* quality override flag */
int bControlMode; /* quality control mode flag */
} CT_TAGVALUE_ITEMS;
CTAPICALL int ctTagReadEx(void *, const char *, char *, int, CT_TAGVALUE_ITEMS *);
int ctTagReadEx(void * hCTAPI, const char * tag, char * value, int length, CT_TAGVALUE_ITEMS *tagValueItems) {
return 15;
}
<强> C#强>
[StructLayout(LayoutKind.Sequential)]
public struct CtTagValueItems {
public int dwLength;
public ulong nTimestamp;
public ulong nValueTimestamp;
public ulong nQualityTimestamp;
public int bQualityGeneral;
public int bQualitySubstatus;
public int bQualityLimit;
public int bQualityExtendedSubstatus;
public uint nQualityDatasourceErrorCode;
public int bOverride;
public int bControlMode;
}
[DllImport("ctapi.dll")]
static extern int ctTagReadEx(IntPtr hCTAPI, IntPtr tag, IntPtr value, int length, IntPtr tagValueItems);
public void TestMe() {
var tagValueItems = new CtTagValueItems();
var tagValueItemsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CtTagValueItems)));
Marshal.StructureToPtr(tagValueItems, tagValueItemsPtr, true);
var tag = "tag";
var tagPtr = Marshal.StringToHGlobalAnsi(tag);
var value = "value";
var valuePtr = Marshal.StringToHGlobalAnsi(value);
int length = value.Length;
var result = ctTagReadEx(IntPtr.Zero, tagPtr, valuePtr, length, tagValueItemsPtr);
if (result != 15) throw new Exception();
Marshal.FreeHGlobal(tagValueItemsPtr);
Marshal.FreeHGlobal(tagPtr);
Marshal.FreeHGlobal(valuePtr);
}