passing char*[] from c++ dll Struct to c# - c#

I'm trying to pass the following structure through c++ DLL to c#:
struct name
{ char* myArray[3];
char firstname[100];
char lastname[100];
};
void Caller(struct name * demo)
{
strcpy(demo->firstname,"hello");
demo->myArray[0]="hello";
demo->myArray[1]="hello";
demo->myArray[2]="hello";
ping(demo); //call to c# function
}
Below is my c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct name
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string firstname;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string lastname;
//what should i marshal here for char* myArray[3];
} ;
static void Main(string[] args)
{
name myname = new name();
ping( ref myname);
}
public static void ping(int a,ref name myname)
{
Console.WriteLine(myname.firstname+"\n");
}
I am able to import first and last name from c++ dll.
What should I do to import char pointer array form c++?

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Name
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string FirstName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string LastName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public string[] Array;
};
for my complete solution check Foo.cpp and Program.cs: https://gist.github.com/3779066

Related

String values in struct getting truncated after call to dll

I am trying to use PInvoke to send a structure containing strings to a DLL written in Microfocus COBOL using net7.0 (x86) and some strings appear to be get truncated while marshalling. The structure are read from and written to by the DLL. I don't have access to the source.
The struct looks like
using System.Runtime.InteropServices;
namespace DllImport.Test;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TransactionHeader
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string Filler1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string TransactionCode; //4
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string FunctionCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string JuryPopSystemId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string SystemId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string tchSystemUserId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string Filler2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string SystemPassword;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string DbConnectFlag;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string Jid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string Pin;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string AltReturnCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string AltInfoCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string Filler3;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string Filler4;
[MarshalAs(UnmanagedType.I4)] public int tchRtnCdNum;
[MarshalAs(UnmanagedType.I4)] public int tchInfCdNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 35)]
public string ErrorMsg;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Reserved;
}
The call is
[DllImport("XXXX.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern void THECALL([In,Out] ref TransactionHeader hdr, [In, Out] ref udtString128 request,
[In, Out] ref udtCommArea commArea);
After the call, the header has the strings truncated by 1 character. For example, I set the SystemId to "DEVL", but the value is "DEV" after the call. I feel as though I am running to a null terminated string problem, but I wanted to check with people more experienced with PInvoke.
Declaring the struct members as UnmanagedType.LPStr makes no difference.
Thanks.

How to marshal wchar[]

I've found plenty of information about how to marshal wchar_t and wchar* on the internet, but right now I am trying to marshal the WINBIO_STRING type, which is defined as wchar[256]. Marshalling it as an array of char[] works, but then every other element in the array is \0, and I would like to avoid this. Is there a more proper way to marshal this data (which is a member of a struct, the WINBIO_UNIT_SCHEMA struct in particular). This is my code:
[StructLayout(LayoutKind.Sequential)]
public struct BiometricUnitSchema
{
public int UnitId;
public BiometricPoolType PoolType;
public BiometricType BiometricFactor;
public BiometricSubtype SensorSubType;
public BiometricCapabilities Capabilities;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] DeviceInstanceId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] Description;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] Manufacturer;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] Model;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] SerialNumber;
public BiometricVersion FirmwareVersion;
}
I also have the problem of the FirmwareVersion field always containing MajorVersion and MinorVersion values of 0, but I am not sure if this result is incorrect or simply misleading.
I fixed this problem by marshaling WINBIO_STRING as a string in C# instead of a char[]. I also had to add a Unicode charset to my structure.
My new structure:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct BiometricUnitSchema
{
public int UnitId;
public BiometricPoolType PoolType;
public BiometricType BiometricFactor;
public BiometricSubtype SensorSubType;
public BiometricCapabilities Capabilities;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string DeviceInstanceId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Description;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Manufacturer;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Model;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string SerialNumber;
public BiometricVersion FirmwareVersion;
}

C# Marshal typedef char T_STRING[MAX_STRING_SIZE]

How to Marshal a:
[C++]
#define MAX_STRING_SIZE 255
typedef char T_STRING[MAX_STRING_SIZE];
typedef struct
{
unsigned long m_ID;
T_STRING m_name;
} Result;
In C#?
Currently I am doing this (but it does not work):
[C#]
[StructLayout(LayoutKind.Sequential)]
public struct Result
{
public uint m_ID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public char[] m_name;
}
I have tried to use a IntPtr instead of char[] with equal non-working result. Both with and without [MarshalAs(...)].
Marshal it like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Result
{
public uint m_ID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
public string m_name;
}

Another PInvoke - error message says source not found?

The struct that i have built in c# when run against the function does not give me one of the error message that tell me something is wrong with the struct. It says that a source file could not be found. Again, I don't know much about this DLL b/c of lack of documentation. But I am still thinking it is because I set the struct up wrong. Most likely i'm thinking the 3rd struct where it is referencing a struct within the struct.
i was hoping for some feedback on the work I did.
Thanks for the assistance.
This is what was provided in C:
int BatchTotal_Transactions(int transType, pTGiftCardReqBatchTotal req, pTGiftCardRespBatchTotal resp, int (*Com)(char *));
typedef struct _tagGiftCardReqBatchTotal
{
char Password[9];
char OperatorID[9];
char BatchNum[14];
char StartDate[11];
char EndDate[11];
unsigned char Type;
} TGiftCardReqBatchTotal, *pTGiftCardReqBatchTotal;
typedef struct _tagGiftCardRespBatchTotal
{
char Result;
char TerminalId[17];
unsigned char DispMsgControl;
char DispMsg[256];
char Display[41];
char Date[11];
char Time[9];
char RespCode[4];
char BatchNum[14];
char ErrorFlag;
char CustLang;
char UserLang;
char OpenDate[17];
char ClosedDate[17];
char StartDate[11];
char EndDate[11];
char BatchStatus;
int CardTypeNum;
TGiftCardTotals GctHost[MAX_CARD_CODES];
TGiftCardTotals GctTRS[MAX_CARD_CODES];
} TGiftCardRespBatchTotal, *pTGiftCardRespBatchTotal;
typedef struct _tagGiftCardTotals
{
unsigned short CardCode;
unsigned short PurchaseNum;
long PurchaseTotal;
unsigned short RefundNum;
long RefundTotal;
unsigned short RedemptionNum;
long RedemptionTotal;
unsigned short CorrectionNum;
long CorrectionTotal;
long PurchaseBenefitTotal;
long RefundBenefitTotal;
long RedemptionBenefitTotal;
} TGiftCardTotals, *pTGiftCardTotals;
And this is how I did it in C#:
[DllImport("batch.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int BatchTotal_Transactions(int transType, ref giftCardReqBatchTotal req, ref giftCardRespBatchTotal resp, IntPtr com);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct _tagGiftCardReqBatchTotal
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string Password;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string OperatorID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string BatchNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string StartDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string EndDate;
public byte Type;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct _tagGiftCardRespBatchTotal
{
public byte Result;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string TerminalId;
public byte DispMsgControl;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string DispMsg;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)]
public string Display;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string Date;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string Time;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string RespCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string BatchNum;
public byte ErrorFlag;
public byte CustLang;
public byte UserLang;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string OpenDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string ClosedDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string StartDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string EndDate;
public byte BatchStatus;
int CardTypeNum;
[MarshalAs(UnmanagedType.Struct, SizeConst = 1024)]
public _tagGiftCardTotals GctHost;
[MarshalAs(UnmanagedType.Struct, SizeConst = 1024)]
public _tagGiftCardTotals GctTRS;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct _tagGiftCardTotals
{
public UInt16 CardCode;
public UInt16 PurchaseNum;
public int PurchaseTotal;
public UInt16 RefundNum;
public int RefundTotal;
public UInt16 RedemptionNum;
public int RedemptionTotal;
public UInt16 CorrectionNum;
public int CorrectionTotal;
public int PurchaseBenefitTotal;
public int RefundBenefitTotal;
public int RedemptionBenefitTotal;
}
Are you sure the error isn't the following:
Specified module could not be found.
If this is the error then it is because the dll could not be found. Make sure the dll is in your path:
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.value.aspx

Using p/invoke to call a function from a C api

I have a C api and I am using p/invoke to call a function from the api in my C# application. The function signature is:
int APIENTRY GetData (CASHTYPEPOINTER cashData);
Type definitions:
typedef CASHTYPE* CASHTYPEPOINTER;
typedef struct CASH
{
int CashNumber;
CURRENCYTYPE Types[24];
} CASHTYPE;
typedef struct CURRENCY
{
char Name[2];
char NoteType[6];
int NoteNumber;
} CURRENCYTYPE;
How would be my C# method signature and data types?
Thank you.
You need to specify the array sizes using SizeConst:
using System;
using System.Runtime.InteropServices;
public static class MyCApi
{
[StructLayout(LayoutKind.Sequential)]
public struct CASHTYPE
{
public int CashNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
public CURRENCYTYPE[] Types;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CURRENCYTYPE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string NoteType;
public int NoteNumber;
}
[DllImport("MyCApi.dll")]
public static extern int GetData(ref CASHTYPE cashData);
}
I think it may look like this
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct CASH{
public int CashNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
public CURRENCY Types[24];
}
[ StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct CURRENCY {
[MarshalAs( UnmanagedType.ByValTStr, SizeConst=2 )]
public string Name;
[MarshalAs( UnmanagedType.ByValTStr, SizeConst=6 )]
public string NoteType;
public int NoteNumber;
}
class Wrapper {
[DllImport("my.dll")]
public static extern int GetData(ref CASH cashData}
}

Categories