C# Marshaling, ubalanced stack and getting PInvoke signature correct - c#

I'm trying to call a C DLL in my C# project using marshaling and have some functions working but I have trouble with others. Like the one below.
My first problem is getting the structs correct and next problem is to return PROFILE_INFO as an array with the list of program files or maybe it won't return a list and proNum is an index.
function in C
extern "C" __declspec(dllexport) int WINAPI GetProgramFileList (unsigned long proNum, PROFILE_INFO *proFile);
typedef struct{
PROINFO proInfo;
__int64 proSize;
PROGRAM_DATE createDate;
PROGRAM_DATE writeDate;
}PROFILE_INFO;
typedef struct{
char wno[33];
char dummy[7];
char comment[49];
char dummy2[7];
char type;
char dummy3[7];
}PROINFO;
typedef struct{
short year;
char month;
char day;
char hour;
char min;
char dummy[2];
}PROGRAM_DATE;
My function
[DllImport(#".\IFDLL.dll", EntryPoint = "GetProgramFileList", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern int GetProgramFileListTest(ulong proNum, ref PROFILE_INFO pro);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROFILE_INFO
{
[MarshalAs(UnmanagedType.Struct)]
public PROINFO proInfo; // WNo/name/type
public long proSize; // Program size
[MarshalAs(UnmanagedType.Struct)]
public PROGRAM_DATE createDate; // Program creating date
[MarshalAs(UnmanagedType.Struct)]
public PROGRAM_DATE writeDate; // Program updating date
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string wno; // WNo.
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy; // dummy
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)] public string comment; // program name
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy2; // dummy
public char type; // program type
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy3; // dummy
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROGRAM_DATE
{
public short year; // Date (Year) 4-digit
public char month; // Date (Month)
public char day; // Date (Day)
public char hour; // Date (Time)
public char min; // Date (Minutes)
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] private char dummy; // Dummy
}
The PROGRAM_DATE createDate in C# struct PROFILE_INFO will throw:
Cannot marshal field 'createDate' of type 'CClient.Models.PROFILE_INFO': The type definition of this field has layout information but has an invalid managed/unmanaged type combination or is unmarshalable.
Changing PROGRAM_DATE fields to string makes it accept it but the function returns an argument(-60) error instead. Though not sure if I'm closer to success.
Other attempts, including attempts to get PROFILE_INFO to return as an array (ref PROFILE_INFO[]), landed in:
A call to PInvoke function has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature.
I have got these descriptions following the C dll:
Explanation
Obtain the program list in the standard area
Argument
proNum [in]
Specify the number of the data to be obtained.
proFile [out]
Store the program information in the standard area, in PROFILE_INFO type.
Before executing this function, make sure to ensure the data area for the number of the data to be obtained.
Return value
If it succeeded, “0” is returned. If there is an error, a value other than “0” is returned.
Other functions I have working is GetProgramDirInfo, SendProgram, ReceiveProgram, SearchProgram, etc. but they don't return any arrays, so I assume marshaling arrays is my problem here. Also I'm trying to avoid using unsafe pointers and I'm unsure if I need to do the copying myself.
Any help is appreciated.

Few points when working with p/invoke:
don't add what's obvious for .NET (structs are structs, ...)
don't add pack if you're unsure, by default .NET should behave like C/C++ on that matter
don't add Ansi information if there's no string in the definition (only when there are strings or TStr, etc.). It doesn't cause problems but is useless
in general, don't add attributes if you don't know what they are used for
int and long in C/C++ are (in general) 32-bit. long is not 64-bit in C/C++
Here is a code that should be better:
[DllImport(#".\IFDLL.dll", EntryPoint = "GetProgramFileList")]
public static extern int GetProgramFileListTest(uint proNum, ref PROFILE_INFO pro);
[StructLayout(LayoutKind.Sequential)]
public struct PROFILE_INFO
{
public PROINFO proInfo; // WNo/name/type
public long proSize; // Program size
public PROGRAM_DATE createDate; // Program creating date
public PROGRAM_DATE writeDate; // Program updating date
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct PROINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string wno; // WNo.
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy; // dummy
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)] public string comment; // program name
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy2; // dummy
public char type; // program type
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy3; // dummy
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct PROGRAM_DATE
{
public short year; // Date (Year) 4-digit
public char month; // Date (Month)
public char day; // Date (Day)
public char hour; // Date (Time)
public char min; // Date (Minutes)
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] private string dummy; // Dummy
}

Related

How can I get struct char[] field value use DllImport function?

In DLL struct is:
typedef struct tagEKIDinfo{
short usbNo;
short printerID;
CHAR serialNo[6];
WORD mediaType;
} EKIDinfo, *PEKIDinfo;
In C# :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public int usbNo;
public int printerID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
And the function is:
DLL:
DWORD WINAPI EKSearchPrinters( PEKIDinfo pIDInfo, DWORD infoSize, LPDWORD pSizeNeeded, LPDWORD pinfoNum )
C#:
[DllImport("EKUSB.dll", EntryPoint = "EKSearchPrinters", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int EKSearchPrinters(IntPtr hPrinter, Int32 infoSize, ref Int32 pSizeNeeded, ref Int32 pNumber);
The serialNo true value is "218699", but I got "99?" with C#.
Why about this? Can anyone help me? Thans a lot!
Kind of a guess, but if you're off by four bytes, maybe the first two fields are too big? Try this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public System.Int16 usbNo; //<--- changed
public System.Int16 printerID; //<--- changed
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
I think you've got wrong types. DLL interface says
struct tagEKIDinfo {
short usbNo; <- short (2 bytes), not int (4bytes)
short printerID; <- short (2 bytes), not int (4bytes)
CHAR serialNo[6];
WORD mediaType;
}
Note the short. This is likely to be int16 on C# side, not int as you have now. Since there are 2 such fields, all before char-array, 2x(4-2)=4 and that would account exactly to the 218699->99 = 4 chars missing
The struct is declared incorrectly. The first two members should be short.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public short usbNo;
public short printerID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
Rather than manually marshal the struct I'd pass it by reference.
[DllImport("EKUSB.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint EKSearchPrinters(
ref EKIDinfo pIDInfo,
uint infoSize,
ref uint pSizeNeeded,
ref uint pNumber
);
I'm not sure why you named the first parameter to suggest a handle, but it certainly doesn't look like one to me.
It's entirely possible that some of the ref parameters should be out depending on the data flow intent which has not been shown.
The other potential issue is how you call the function. You didn't show the call or any details of how it must be done.

C# <-> C++ : Marshaling an [In,Out] struct containing Char[]

I have a C++ .dll (unable to make changes to the code) from which I am trying to call a function that takes a reference parameter of a Struct type (also defined in the .dll).
The function requires the Struct to have the 'paramName' and 'groupName' fields populated, and based on those fields, it will return the Struct with the remaining fields populated.
I am not getting any marshaling errors, however, the library call returns an error code and a debug log shows that it is receiving an empty string for the two string fields (see below for how fields are set). My assumption is that the size of this struct is not aligning between the managed and unmanaged representation, and thus the struct is not blittable.
Here is the C++ method signature:
int GetConfigs(int contextHandle, Configs* configs);
And the C++ Configs struct:
struct Configs {
int myInt;
float myFloat;
bool flag;
char name[64];
char group[64];
}
The C# function wrapper:
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int GetConfigs(int contextHandle, [MarshalAs(UnmanagedType.Struct), In, Out] ref Configs configs);
The C# struct definition:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Configs
{
public int myInt;
public float myFloat;
[MarshalAs(UnmanagedType.U1)]
public bool flag;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string group;
}
As per the Microsoft documentation I have declared the C++ char[]'s to be represented in C# by strings and marshaled as ByValTStr with a set size.
Also from Microsoft documentation:
bool is not blittable, so I mark it with an explicit MarshalAs.
float is also not blittable, but declaring these fields as a float or a double made no difference in the original issue.
Lastly, the C# calling code:
var configs = new Library.Configs
{
name = "testName",
group = "testGroup"
};
var returnCode = Library.GetConfigs(GetContextId(), ref configs);
The return code comes back as a failure code and the library debug output file shows that the struct argument had:
name = []
(name is clearly set to what I expect it to be at the time of the call when I debug through the C# code)
Instead of declaring the string fields as
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
I tried:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] name;
but that also made no difference.
The float/double was what was throwing off the struct byte size- the library was expecting a 4 byte floating point number but the data marshaler by default keeps those as 8 bytes. Fixing that along with the boolean as mentioned in the comments above solved the problem:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Configs
{
public int myInt;
[MarshalAs(UnmanagedType.R4)]
public float myFloat;
[MarshalAs(UnmanagedType.U1)]
public bool flag;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string group;
}

char[] array equivalent in c#

i have a c++ struct which has char[20] like below, and it is packed.
#pragma pack(push, temp_aion_packed, 1)
struct temp
{
char x[20];
char y[20];
};
#pragma pack(pop, temp_aion_packed)
now how can i write this struct in c# so that both are same. i have written like this in c#
[DataContract]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public class temp
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string x;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string y;
}
below is the pinvoke declaration in c#
[DllImport("rmsCAPI.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, EntryPoint = "OrderRequirement")]
public static extern int OrderRequirement(ref temp tmp);
c++ function that calls this struct as parameter
long __stdcall OrderRequirement(struct temp *tmp)
{
string p="";
string q="";
p=temp->x;
q=temp->y;
char buff[2048];
sprintf(buff,"p: %s\n q: %s\n x: %s\n y: %s\n",p,q,temp->x,temp->y);
}
but when i do like this in c# it is giving me the junk data in c++, when i assign values for them in c#. Can any one please assist.
Thank you all for help on the above one, but now i got a new problem which is an extension of this, i am providing everything in detail below.
My structures in c++
#pragma pack(push, temp_aion_packed, 1)
struct temp
{
long req;
struct type m_type;
short id;
char x[20];
char y[20];
};
#pragma pack(pop, temp_aion_packed)
#pragma pack(push, type_aion_packed, 1)
struct type
{
short i;
};
#pragma pack(pop, type_aion_packed)
i have written equivalent c# structs like this
[DataContract]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public struct temp
{
[DataMember]
public long req;
[DataMember]
[MarshalAs(UnmanagedType.Struct)]
public type m_type;
[DataMember]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string x;
[DataMember]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string y;
}
[DataContract]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public struct type
{
[DataMember]
public short i;
}
below is my c# pinvoke
[DllImport("rmsCAPI.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, EntryPoint = "OrderRequirement")]
public static extern int OrderRequirement(ref temp tmp);
below is my c++ method that calls struct as parameter
long __stdcall OrderRequirement(struct temp *tmp)
{
char buff[2048];
sprintf(buff,"req: %ld \n id: %d \n x: %s\n",tmp->req,tmp->id,tmp->x);
}
now the problem i am getting is as i have structure variable m_type(of struct "type") declared in struct temp, the variables declared before (long req)that are printing fine in my c++ program but the variable declared after that are not giving me any output. So i think the declaration of structures in c# is messing up and i am unable to figure it out so can any one please help.
You declared the struct as a class in C#. That's fine, but it means that any variable of that type is already a reference. So you don't need to pass by ref. When you pass a class by ref you end up passing a pointer to a pointer to the object. That's one level of indirection too many.
The P/invoke in the C# code should therefore be like this:
public static extern int OrderRequirement(temp tmp);
The other way to fix it is to leave the ref in the function declaration, but to declare the temp type as a struct rather than a class. That's because a struct is a value type. A variable whose type is a struct is a value rather than a reference.
Both solutions work, it's up to you which you choose.
There is another problem in your C++ code.
sprintf(buff,"p: %s\n q: %s\n x: %s\n y: %s\n",p,q,temp->x,temp->y);
You are passing p and q, which are of type std::string to printf and expecting the %s format string to print them. That's an error. You need to call c_str() on the strings. Like this:
sprintf(
buff,
"p: %s\n q: %s\n x: %s\n y: %s\n",
p.c_str(),q.c_str(),temp->x,temp->y
);
The problem with your update is that long is 32 bits in Windows C++ and 64 bits in C#. You need to declare it as int in C#. And you missed the id field altogether.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1),Serializable]
public struct temp
{
[DataMember]
public int req;
[DataMember]
public type m_type;
[DataMember]
public short id;
[DataMember]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string x;
[DataMember]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string y;
}

How to convert a C++ Struct with Union into C#?

Guys I am having difficulties on retrieving struct member values after calling a function in the DLL. I tried to convert the C++ codes into C# but I’m not sure if it is correct or not. Please help me understand my mistakes here (if there is) and how to correct.
My problem here is I can’t correctly retrieved the values of the INNER STRUCTS (Union) after I called the ReceiveMessage function from the DLL. Like for example m_objMsg.MsgData.StartReq.MsgID is always 0.
But when I try to use the C++ .exe program, the MsgID has a correct value. (not 0)
C++ Code:
extern int ReceiveMessage(SESSION, int, Msg*);
typedef struct
{
char SubsId[15];
int Level;
char Options[12];
} ConxReq;
typedef struct
{
char MsgId[25];
} StartReq;
typedef struct
{
long Length;
short Type;
union
{
ConxReq oConxReq;
StartReq oStartReq;
} Data;
} Msg;
/////////////////////////////////////////////////////
Msg oMsg;
int rc=ReceiveMessage(Session, 0, &oMsg);
switch(rc)
{
case 0:
switch(oMsg.Type)
{
case 0: // ConxReq
…
break;
case 1: // StartReq
…
break;
…
}
And here is my attempt to convert this into c#:
[DllImport("MyDLL.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
protected static extern Int32 ReceiveMessage(IntPtr session,
Int32 nTimeOut,
[MarshalAs(UnmanagedType.Struct)] ref Msg ptrMsg);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public StartReq oStartReq;
}
Msg m_objMsg = new Msg();
m_objMsg.MsgData = new Data();
m_objMsg.MsgData.oConxReq = new ConxReq();
m_objMsg.MsgData.oStartReq = new StartReq();
int rc = ReceiveMessage(m_Session, nTimeOut, ref m_objMsg);
then the SWITCH Condition
And If I add this struct inside the UNION for c++ and c#...
I've got an error stating the "... incorrectly align" or "...overlapped..."
c++
ConxNack oConxNack;
typedef struct
{
int Reason;
} ConxNack;
[StructLayout(LayoutKind.Sequential)]
public struct ConxNack
{
public int nReason;
}
[FieldOffset(0)]
public ConxNack oConxNack;
Thank you so much in advance for your time and help...
Akash is right, have a look here: http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3
Another option is create two structs and use an appropriate cast once you know which type it is.
hth
Mario
In C++, we know that all members of UNION shared the same memory chunk and can only have one member of an object at a time.
In order to implement this in C#, we need to use the LayoutKind to Explicit and set all the starting point of each member to 0.
In my previous example, An error message is displayed stating that the offset of an object type is incorrectly aligned or overlapped by a non-object type.
Answer is we cannot set all the members to FieldOffSet to 0 since it is not allowed to combine the reference type with the value type.
- Thanks to the explanation of Hans Passant
What I did is to create a copy of the UNION Member Structs and change the type of all the String Member Variables to bytes.
I used bytes since this is a value type so I can put this struct into FieldOffSet(0).
Take note, i adjust the FieldOffSet of the next member variable so i can still get the same size of my string variable.
And also for the struct size since i have byte member at the last.
Thanks to Akash Kava and Mario The Spoon for giving me an idea and providing me a useful link.
After calling the function in the DLL and passed this Struct Obj (ref m_objMsg) as a paramter, I need to extract the values.
One way is to have a pointer that points to the address of the struct in the unmanaged memory and convert this pointer a new
Struct with the corresponding member variables (my original structs).
NEW STRUCTS (BYTES)
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 31)]
public struct ConxReq
{
[FieldOffSet(0)]
public byteSubsId;
[FieldOffSet(15)]
public Int32 Level;
[FieldOffSet(19)]
public byte Options;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ConxNack
{
[FieldOffSet(0)]
public int nReason;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 25)]
public struct StartReq
{
[FieldOffSet(0)]
public byte MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public ConxNack oConxNack;
[FieldOffset(0)]
public StartReq oStartReq;
}
////////////////////////////////////////////////////////////////
MY ORIGINAL STRUCTS
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyConxNack
{
public int nReason;
}
///////////////////////////////////////////////////////////////
Since I have a Msg.Type, i know what kind of struct (type) I could cast the object.
Like for example
ReceiveMessage(m_Session, nTimeOut, ref oMsg);
switch (oMsg.Type)
{
case 0: // ConxReq
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(oMsg.MsgData.ConxReq); // use the new struct (bytes)
Marshal.StructureToPtr(oMsg.MsgData.ConxReq, ptr, false);
MyConxReq oMyConxReq = new MyConxReq;
oMyConxReq = (MyConxReq) Marshal.PtrToStructure(ptr, typeof(MyConxReq)); // convert it to the original struct
Marshal.FreeHGlobal(ptr);
Then you can use now the oMyConxReq object to acccess the member variables directly.
Please let me know if you have other or better way to do this...
Kindly advise if what I did is correct or if I missed something.
Thank you so much!!! :)
You have to use StructLayout(LayoutKind.Explicit) and FieldOffsets to make union.

Converting this C signature to C# for P/Invoke

I have the following C function:
int w_ei_connect_init(ei_cnode* ec, const char* this_node_name,
const char *cookie, short creation);
ei_cnode looks like this:
typedef struct ei_cnode_s {
char thishostname[EI_MAXHOSTNAMELEN+1];
char thisnodename[MAXNODELEN+1];
char thisalivename[EI_MAXALIVELEN+1];
char ei_connect_cookie[EI_MAX_COOKIE_SIZE+1];
short creation;
erlang_pid self;
} ei_cnode;
Which I have converted to C#:
[StructLayout(LayoutKind.Sequential)]
public struct cnode {
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_HOSTNAME_LEN + 1)]
public string thishostname;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_NODE_LEN + 1)]
public string thisnodename;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_ALIVE_LEN + 1)]
public string thisalivename;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_COOKIE_SIZE + 1)]
public string ei_connect_cookie;
public short creation;
public erlang_pid self;
}
I'm not good with pointers or C in general, so I'm not sure how I'm supposed to supply a cnode to ei_connect_init.
What would the equivalent C# signature be for the C function above?
Whenever you want to pass a C# struct to a parameter value containing the equivalent native struct but with a pointer, the typical method is to label the parameter as "ref". This causes the PInvoke layer to essentially pass the address in.
[DllImport("somedll")]
public static extern w_ei_connect_init(
ref cnode v,
[In] string this_node_name,
[In] string cookie,
int16 creation);
Something like this should work:
int w_ei_connect_init(ref cnode ec,
[MarshalAs(UnmanagedType.LPStr)] string this_node_name,
[MarshalAs(UnmanagedType.LPStr)] string cookie, short creation);
You should also consider marking your struct with
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
attribute, so those TStr will be ansi-strings, not unicode.

Categories

Resources