I'm invoking a C++ function from within C#.
The function I am trying to invoke returns following C++ struct type result:
LPWFSCIMNOTETYPELIST lpNoteTypeList;
typedef struct _wfs_cim_note_type_list
{
USHORT usNumOfNoteTypes;
LPWFSCIMNOTETYPE *lppNoteTypes;
} WFSCIMNOTETYPELIST, *LPWFSCIMNOTETYPELIST;
typedef struct _wfs_cim_note_type
{
USHORT usNoteID;
CHAR cCurrencyID[3];
ULONG ulValues;
USHORT usRelease;
BOOL bConfigured;
} WFSCIMNOTETYPE, *LPWFSCIMNOTETYPE;
I found the suitable struct in C#:
[StructLayout(LayoutKind.Sequential, Pack = XFSConstants.STRUCTPACKSIZE, CharSet = CharSet.Ansi)]
public unsafe struct WfsCimNoteType
{
public ushort usNoteID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] cCurrencyID;
public uint ulValues;
public ushort usRelease;
public byte bConfigured;
}
[StructLayout(LayoutKind.Sequential, Pack = XFSConstants.STRUCTPACKSIZE, CharSet = CharSet.Ansi)]
public unsafe struct WfsCimNoteTypeList
{
public ushort usNumOfNoteTypes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public WfsCimNoteType[] lppNoteTypes;
}
I used the following code to parse the result from invoked function:
WfsCimNoteTypeList data = new WfsCimNoteTypeList();
XFSUtil.PtrToStructure<WfsCimNoteTypeList>(lpBuffer, ref data);
(here lbBuffer is IntPtr)
XFSUtil.PtrToStructure is defined like this:
public static void PtrToStructure<T>(IntPtr ptr, ref T p) where T : struct
{
p = (T)Marshal.PtrToStructure(ptr, typeof(T));
}
But after parsing from IntPtr to WfsCimNoteType object array, I am getting different values???
I'm working with SafeCom (print server) and figured out that they have a Administrator DLL Programmer’s Manual. As a proof of concept I wanted to make a small C# application that sends and retrieves data using this library. I've managed to insert a user object by calling LoginByLogon() followed by AddUser().
However, I'm not able to retrieve/return a user struct by calling LoginByLogon() followed by GetUser. I can see that ptrTemp is set (not null), but all properties in newStructure are still null. I don't fully understand PInvoke yet, so I hope someone here can enlighten me a little. :)
I'm for now importing the following:
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int sc_LoginByLogon(string szIpAddr, string szUserName, string szPassword);
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int sc_Logoff();
[DllImport("scAPI.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern int sc_GetUserInfoByUserLogon(string szUserLogon, out IntPtr ppUserInfo);
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern short sc_AddUser(ref sUserInfoApiV4 ppUserInfo, double fAccount, double FLowLimit, ref int pNewId);
Define my structs:
struct SYSTEMTIME
{
short wYear;
short wMonth;
// short wDayOfWeek;
short wDay;
short wHour;
short wMinute;
short wSecond;
short wMilliseconds;
public static implicit operator SYSTEMTIME(DateTime time)
{
return new SYSTEMTIME
{
wYear = (short)time.Year,
wMonth = (short)time.Month,
wDay = (short)time.Day,
// wDayOfWeek = (short)time.DayOfWeek,
wHour = (short)time.Hour,
wMinute = (short)time.Minute,
wSecond = (short)time.Second,
wMilliseconds = (short)time.Millisecond
};
}
public static implicit operator DateTime(SYSTEMTIME time)
{
return new DateTime(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Card
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string m_szCardNo; //Card number
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string m_szPINCode; //PIN code - zero terminated
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string m_szPUKCode; //PUK code - zero terminated
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct sUserInfoApiV4
{
[MarshalAs(UnmanagedType.I4)]
public Int32 m_nStructLength; //Length of the struct including this field
[MarshalAs(UnmanagedType.I2)]
public short m_nVersion; //Version Number (set to 4)
[MarshalAs(UnmanagedType.I2)]
public short m_nSubVersion; //Version of Reserved (set to 0)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] m_achReserved; // Get room for m_nAccessRights
[MarshalAs(UnmanagedType.I2)]
public short m_nUserType; //USER_STRUCT (uses as UserType - BitFields)
[MarshalAs(UnmanagedType.I4)]
public int m_nUserId; //Unique id identifying the user
public int m_nSubRights; // Sub version 2
[MarshalAs(UnmanagedType.I4)]
public int m_nNotUsed1; // Sub version 5
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzFullName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzEMail;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string m_wzUserLogon; //Asc-ii string identifying the user
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string m_wzPassword; //Asc-ii string password
public Card card;
[MarshalAs(UnmanagedType.I2)]
public short m_nLogonFails; //Number of failed logins
[MarshalAs(UnmanagedType.I2)]
public short m_bUserLocked; //Unlocked(0), Locked(1)
[MarshalAs(UnmanagedType.I2)]
public short m_bUserDisabled; //Not Disable(0), Open(1)
[MarshalAs(UnmanagedType.I2)]
public short m_bAvoidPin; //Pin Enabled(0), Pin disabled (1)
[MarshalAs(UnmanagedType.I2)]
public short m_bPrintAll; //Print All when card is slid - Yes(1) No(0)
[MarshalAs(UnmanagedType.I2)]
public short m_bCardOpen; //PIN code assigned(0), Awaiting pin assignment/Outstanding PUK(1)
[MarshalAs(UnmanagedType.I2)]
public short m_nBillingModel; //None(0), BillingDialog(1)
[MarshalAs(UnmanagedType.I2)]
public short m_nAccountingModel; //None(0), Tracking(1), Pay(2)
[MarshalAs(UnmanagedType.I4)]
public int m_nUserRights; // None(0)
[MarshalAs(UnmanagedType.I2)]
public short m_bAllowEncryption; // The user uses encryption YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bAllowCheckPrinting; // The user is allowed to receives checks YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bAllowPmail; // The user is allowed distribution YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bDenyRetain; // User allowed to retain his documents. New in SubVersion 3
public int m_lCreationDate;
public int m_lLastLogin;
[MarshalAs(UnmanagedType.I4)]
public int m_nServerId; // HomeServer of this user.
[MarshalAs(UnmanagedType.I4)]
public int m_nDomainId; // New in Subversion 1 - Reference to the windows domain the user are located in
[MarshalAs(UnmanagedType.I4)]
public int m_nTreeNodeId;
[MarshalAs(UnmanagedType.I4)]
public int m_nNid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public string m_wzCostCode; // Cost center, new in SubVersion 3
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SCardInfoApiV4
{
[MarshalAs(UnmanagedType.I4)]
public Int32 m_nStructLength; // Length of entire struct
[MarshalAs(UnmanagedType.I2)]
public short m_nVersion; // Version (short)
[MarshalAs(UnmanagedType.I2)]
public short m_nSubVersion; // Subversion of reserved, set to 1 (short)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] m_achReserved; // Reserved for protocol expansion (fixed char)
[MarshalAs(UnmanagedType.I4)]
public int m_nCardId;
[MarshalAs(UnmanagedType.I4)]
public int m_nUserId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string m_achCardNo; // originalt fixed char [40]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string m_achReservedPinCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string m_achReservedPukCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public string m_achDescription;
[MarshalAs(UnmanagedType.I4)]
int m_nCreatorId;
SYSTEMTIME m_sCreated;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string m_nUseLifeTime; //originalt char
SYSTEMTIME m_sLifeStart;
SYSTEMTIME m_sLifeEnd;
[MarshalAs(UnmanagedType.I4)]
int m_nSourceId; // Sub version 1
}
And finally my methods:
public int LoginByLogon(string ipAddr, string userLogon, string password)
{
return sc_LoginByLogon(ipAddr, userLogon, password);
}
public int Logoff()
{
return sc_Logoff();
}
public sUserInfoApiV4 GetUser(string userLogon)
{
sUserInfoApiV4 tmpUserInfo = new sUserInfoApiV4();
IntPtr ptrTemp = Marshal.AllocHGlobal(Marshal.SizeOf(tmpUserInfo));
sc_GetUserInfoByUserLogon(userLogon, out ptrTemp);
Marshal.StructureToPtr(tmpUserInfo, ptrTemp, false);
sUserInfoApiV4 newStructure;
newStructure = (sUserInfoApiV4)Marshal.PtrToStructure(ptrTemp, typeof(sUserInfoApiV4));
return newStructure;
}
public unsafe int AddUser(string usercode, string email, string pinCode, string fullName)
{
sUserInfoApiV4 sUserstruct = new sUserInfoApiV4();
int sizeOfstruct = Marshal.SizeOf(typeof(sUserInfoApiV4));
IntPtr pSUserstruct = Marshal.AllocHGlobal(sizeOfstruct); //Make note of memory used
Marshal.StructureToPtr(sUserstruct, pSUserstruct, false);
sUserstruct.m_wzUserLogon = usercode;
sUserstruct.card.m_szCardNo = usercode; //Card number same as logon
sUserstruct.card.m_szPINCode = pinCode;
sUserstruct.m_wzFullName = fullName;
sUserstruct.m_wzEMail = email;
sUserstruct.m_nUserRights = 3; //Standard user rights
sUserstruct.m_nAccountingModel = 2; //Pay user
sUserstruct.m_nVersion = 4; //Safecom version
sUserstruct.m_nStructLength = sizeOfstruct;
sUserstruct.m_bAvoidPin = 1; // Disable PIN
int userId = 0; //Value of variable not used, just to initialize the variable. Used as pointer for return value. Index number for user in db
int* userIndex = &userId;
int nRes = sc_AddUser(ref sUserstruct, 0, 0, ref *userIndex);
Marshal.FreeHGlobal(pSUserstruct); // Release memory
pSUserstruct = IntPtr.Zero; // used by pointer.
return nRes;
}
I am having trouble mapping the following c++ struct in c# for memory mapping purposes.
typedef struct
{
DWORD Data1;
DWORD Data2;
double AverageData[2];
DWORD NumData[2];
} DIRECTIONAL_STATS;
typedef struct
{
DIRECTIONAL_STATS DirectionStats[2];
char Name[100];
int StatLength;
} OTHER_STATS;
typedef struct
{
OTHER_STATS SystemStat[64][2];
long LastUpdate;
}STATS;
Can someone please shed some lights on how to achieve the mapping? Mapping from c++ type to c# type is fine for me.
However, I don't have any clue how to map nested struct and the explicit array size required.
Update 1:
Managed to map to following c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct STATS
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 128)]
public OTHER_STATS[] SystemStat; //64x2
public long LastUpdate;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct OTHER_STATS
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 2)]
public DIRECTIONAL_STATS[] DirectionStats;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public char[] Name;
public int StatLength;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DIRECTIONAL_STATS
{
public UInt16 Data1;
public UInt16 Data2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public double[] AverageData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public UInt16[] NumColdRes;
}
Unsure about the following mapping since it is a multidimensional arrays.
public SENTRY_PERFORMANCE_STATS[] Sentry; //64x2
Using the above mapping, it is able to run without any exception, however, the data mapped into OTHER_STATS are all wrong.
Can anyone see what I have done wrong?
Thanks.
my struct looks like this in c code:
typedef struct _REQUEST {
char D [_D_MAX+1];
char M [_M_MAX+1];
char T [_T_MAX+1];
char ClRef [_CL_REF_MAX+1];
char load [_LOAD_MAX];
ULONG loadLen;
} _REQUEST, *LP_REQUEST;
in c# like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct _REQUEST_STRUCT {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string D;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string M;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string T;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string clRef;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1200)]
public string Load;
public UInt32 LoadLen;
}
the method I am calling is like this in c:
int
AJ_STDCALL
SimpTran (const char* svc_name,
const LP_REQUEST query,
LP_RESPONSE response)
{
//init stuff
//Validate input parameters
if ( query == NULL )
return(ERR_QUERY_NULL);
if (query->loadLen == 0)
return (ERR_QUERY_NULL);
}
in c#:
[DllImport(LINUXLIB, CallingConvention = CallingConvention.StdCall)]
public static extern int SimpTran(string serID, ref _REQUEST_STRUCT request, out IntPtr response);
and i am calling it like this:
p_request_struct = new _REQUEST_STRUCT();
// fill p_request_struct
// response
p_response_struct = new _RESPONSE_STRUCT();
// initialize unmanaged memory to hold return struct
IntPtr ptrRet = Marshal.AllocHGlobal(Marshal.SizeOf(p_response_struct));
Marshal.StructureToPtr(p_response_struct, ptrRet, false);
iRet = SimpTran(SerID, ref p_request_struct, out ptrRet);
I am getting null query! In C code query==null is true and it returns null. But I am populating the request struct?
Do I have to allocate memory for that as well?
Hi I writing a wrapper in c# and i have some problem. I have this struct in c++.
typedef struct pjmedia_format
{
pj_uint32_t id;
pjmedia_type type;
pjmedia_format_detail_type detail_type;
union
{
pjmedia_audio_format_detail aud;
pjmedia_video_format_detail vid;
char user[PJMEDIA_FORMAT_DETAIL_USER_SIZE];
} det;
} pjmedia_format;
This is link to this struct pjmedia_format
in c# I have this:
[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_format
{
public uint id;
public pjmedia_type type;
public pjmedia_format_detail_type detail_type;
public det_t det;
}
[StructLayout(LayoutKind.Explicit)]
public struct det_t
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_audio_format_detail aud;
[FieldOffset(36)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_video_format_detail vid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
[FieldOffset(60)]
public char[] user;
}
[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_audio_format_detail
{
public uint clock_rate;
public uint channel_count;
public uint frame_time_usec;
public uint bits_per_sample;
public int avg_bps;
public int max_bps;
}
[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_video_format_detail
{
public pjmedia_rect_size size;
public pjmedia_ratio fps;
public int avg_bps;
public int max_bps;
}
and when i want to use this struct i get this error
System.Runtime.InteropServices.MarshalDirectiveException was unhandled.
Message="A method signature is not PInvoke compatible with the element."
I try to use some attributes like size or pack but it doesn't help (probably i use it wrong). I tested singly other struct e.g. pjmedia_video_format_detail and they works well. Any advice?
Best regards
Andrzej
As this is a union, shouldn't that be:
[StructLayout(LayoutKind.Explicit)]
public struct det_t
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_audio_format_detail aud;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_video_format_detail vid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
[FieldOffset(0)]
public char[] user;
}
i.e. overlapping? Also, you might need user to be a fixed buffer rather than an array.
In the C++ code, det is a union. That means that all the fields have zero offset, they are overlayed. Simply change your C# declaration to match by using [FieldOffset(0)] for all fields in det_t.