Reputation: 996
I'm trying to use an unmanaged API from C#, and banging my head against the wall. (I'm rather a beginner when it comes to PInvoke.)
The relevant parts of the header file look like this:
#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
looks like this:
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;
My C# method declaration:
[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);
The 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;
}
When I call it like this (from a test assembly built as x86), I get a 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);
I've been trying all kinds of things, like using LayoutKind.Explicit
and/or CallingConvention = CallingConvention.Cdecl
, but to no avail.
Can anybody help?
Upvotes: 3
Views: 2045
Reputation: 171178
UINT
as ushort
. Doesn't it have 4 bytes?BOOLEAN
type maps to 4 bytes, AFAIK.CtTagValueItems
by ref (as class or ref
).Upvotes: 2
Reputation: 17574
Where does the handle
variable in your C# call come from?
I much prefer to use IntPtr
in my DllImport
method definitions. It seems easier to manage and marshal that way.
I've changed quite a bit of the struct
definition because I don't have the same defines as you. I also don't have much of a body in my ctTagReadEx
function (I'll try to flesh it out to make sure the parameters passed in match the parameters received). But this works for me.
Update: It looks like all the parameters and struct values are passed correctly.
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);
}
Upvotes: 1
Reputation: 2880
The problem might be in alignment. try somting like
StructLayout(LayoutKind.Sequential, Pack = 1)
Upvotes: 1