extern "C"
__declspec(dllexport)
bool FillString(LPWSTR OutName)
{
LPWSTR out = L"TheName\0";
int len = wcslen(out);
memcpy(
OutName,
out,
len * sizeof(wchar_t));
return true;
}
That is function in my c/c++ dll, the following is my call from c#...
[DllImport(#"My.dll", EntryPoint = "FillString", CallingConvention = CallingConvention.Cdecl)]
public static extern bool MyFunction([MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder Name);
var fromdll = new StringBuilder(64);
// I do not know length of out string (Name), but it is null terminated
bool IsFilled = MyFunction(fromdll);
Console.WriteLine(fromdll);
The output is
TheName???
Can anyone help me to get the output...?
TheName
You need to copy one more character. '\0' is called string terminator. Without it C, C++ and PInvoke in .NET do not recognize end of string. If you are copying wcslen characters, zero is not copied. There are many solutions:
Use mentioned wcscpy to copy string with \0
Copy one more
character with memcpy(OutName, out, (len + 1) * sizeof(wchar_t)).
Moreover, it's a good idea to pass buffer (StringBuilder) size. C/C++ lets you write outside variable boudaries. This size enables to avoid that.
I have two c++ functions that I want to DllImport:
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
There are a lot of articles that write how to DllImport strings.
Alas quite often I see different replies to the same question.
For instance some say if a c++ function returns a a char* and an int* strLen, then some people say I should use a StringBuilder in my dllImport statement and others say return byte[], some have a marshall statement in the dllImport, some don't. Some answers seem needed because of old C# / .net versions.
So the question is: If the dll call from c++ is fairly straightforward, without strange calling conventions, or other strange items, what should the corresponding DllImport functions be if you have output char* and size or input char * and size?
c++ .h
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
What are the corresponding DllImports? replace the instr and outstr with string? stringbuilder? char[]? byte[]? Is any marshal statement needed?
bool SendString(const char* pSendStr, long strSize);
This function is the easy one. The text is sent from the caller to the callee. The p/invoke is declared like this:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr, int Len);
Note that I'm assuming the cdecl calling convention since that is the default for C++ code. And also do note that long in C++ on Windows is 32 bits wide. So it matches int in C#.
When you call the function you need to pass the string and its length. However, the normal convention is for null-terminated strings to be used so the length parameter is not needed. I'd declare the unmanaged function like this:
bool SendString(const char* pSendStr);
And the p/invoke like this:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr);
The other function is a little more complex. You've declared it like this:
bool ReadString(char* pReadStr, long& readStrSize);
Here the caller allocates the buffer which is populated by the callee. You can use StringBuilder for the text and let the marshaler do the work for you. The p/invoke is:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool ReadString(StringBuilder ReadStr, ref int Len);
The convention is that you supply the length of the provided buffer. In turn the function will let you know how many characters it wrote. You'd call the function like this:
int len = 256;
StringBuilder ReadStr = new StringBuilder(len);
bool succeeded = ReadString(ReadStr, ref len);
if (succeeded)
{
string str = ReadStr.ToString();
}
As leppie wrote, what you usually want is:
[DllImport(my.dll)]
static extern bool SendString(string sendString, int stringSize);
[DllImport(my.dll)]
static extern bool ReadString(StringBuilder readString, ref int readStringSize);
This would do automatic conversion to Unicode (and back) for you.
If you want precise access to your char*, you would use byte[]. This way no conversion is done and you have more control on what is going on. Usually you won't need that. One use case might by when your char* can include \0 chars.
I've been trying to invoke a method that have been created in Delphi in the following way:
function _Func1(arrParams: array of TParams): Integer;stdcall;
type
TParams = record
Type: int;
Name: string;
Amount : Real;
end;
My code is:
[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)
And the struct is:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
public int Type;
[MarshalAs(UnmanagedType.AnsiBStr)]
public string Name;
public double Amount;
}
When I am calling this method I'm getting the error:
Cannot marshal field 'Name' of type 'TParams': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).
However none of those combinations works, as Delphi's strings are prefixed with its length and it is Ansi for sure (I've tried it with other string parameters). Does anyone have a clue how to solve this?
There are two main problems with this, use of open arrays and use of Delphi string.
Open arrays
Delphi open arrays are implemented by passing a pointer to the first element of the array and an extra parameter specifying the index of the last item, high in Delphi terminology. For more information see this answer.
Delphi strings
The C# marshaller cannot interop with a Delphi string. Delphi strings are private types, only to be used internally to a Delphi module. Instead you should use a null-terminated string, PAnsiChar.
Putting it all together you can write it like this:
Delphi
type
TParams = record
_Type: Integer;//Type is a reserved word in Delphi
Name: PAnsiChar;
Amount: Double;
end;
function Func(const arrParams: array of TParams): Integer; stdcall;
C#
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
public int Type;
public string Name;
public double Amount;
}
[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);
TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);
To compliment David's answer, you can marshal to a Delphi string, but it's ugly. In C#, you have to replace all of your strings in the struct with IntPtr.
private static IntPtr AllocDelphiString(string str)
{
byte[] unicodeData = Encoding.Unicode.GetBytes(str);
int bufferSize = unicodeData.Length + 6;
IntPtr hMem = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value
for (int i = 0; i < unicodeData.Length; i++)
Marshal.WriteByte(hMem, i + 4, unicodeData[i]);
Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate
return new IntPtr(hMem.ToInt64() + 4);
}
This can directly be sent to Delphi, where it'll properly be read as a string.
Remember that you must free this string when you're done with it. However, GlobalFree() can't be called directly on the pointer to the string, because it doesn't point to the start of the allocation. You'll have to cast that pointer to a long, then subtract 4, then cast it back to a pointer. This compensates for the length prefix.
I have a C++ dll that has a function that returns a c string and I have a C# program that calls this function and returns the data to a C# string. Here's what I mean
__declspec(dllexport) const char* function (const char* str) {
std::string stdString( str );
std::cout << stdString.c_str() << std::endl; // this prints fine, no data loss
return stdString.c_str();
}
And here's the C# code
[DllImport("MyDLL.dll")]
public static extern string function(string data);
string blah = function("blah");
Console.WriteLine(blah); // doesn't print anything...
When I look into the locals it says variable 'blah' is equal to "".
What happened to the data?
Your C++ code is broken. You are returning a pointer to a local variable. It no longer exists after the function returns. This tends to work by accident in a C++ program but is strong Undefined Behavior. It cannot possibly work in an interop scenario, the pinvoke marshaler's use of the stack will overwrite the string.
A declaration that could work:
void function (const char* str, char* output, size_t outputLength)
Use a StringBuilder in the [DllImport] declaration for the output argument and pass an initialized one with sufficient Capacity.
I hope someone can assist me with the problem I'm currently experiencing. We have a lot of Delphi legacy code, and need to convert some of our Delphi applications to C#.
The legacy code I'm currently struggling with is that of calling a function from a 3rd party application's non-COM DLL.
Here is the C-style header and struct used for the specific function:
/*** C Function AwdApiLookup ***/
extern BOOL APIENTRY AwdApiLookup( HWND hwndNotify, ULONG ulMsg,
BOOL fContainer, CHAR cObjectType,
SEARCH_CRITERIA* searchCriteria,
USHORT usCount, USHORT usSearchType,
VOID pReserved );
/*** C Struct SEARCH_CRITERIA ***/
typedef struct _search_criteria
{
UCHAR dataname[4];
UCHAR wildcard;
UCHAR comparator[2];
UCHAR datavalue[75];
} SEARCH_CRITERIA;
In our Delphi code, we have converted the above function and structure as:
(*** Delphi implementation of C Function AwdApiLookup ***)
function AwdApiLookup(hwndNotify: HWND; ulMsg: ULONG; fContainer: Boolean;
cObjectType: Char; pSearchCriteria: Pointer; usCount: USHORT;
usSearchType: USHORT; pReserved: Pointer): Boolean; stdcall;
external 'AWDAPI.dll';
(*** Delphi implementation of C Struct SEARCH_CRITERIA ***)
TSearch_Criteria = record
dataname: array [0..3] of char;
wildcard: char;
comparator: array [0..1] of char;
datavalue: array [0..74] of char;
end;
PSearch_Criteria = ^TSearch_Criteria;
and the way we call the above mentioned code in Delphi is:
AwdApiLookup(0, 0, true, searchType, #criteriaList_[0],
criteriaCount, AWD_USE_SQL, nil);
where criteriaList is defined as
criteriaList_: array of TSearch_Criteria;
After all that is said and done we can now look at the C# code, which I cannot get to work. I'm sure I'm doing something wrong here, or my C header is not translated correctly. My project does compile correctly, but when the function is called, I get a "FALSE" value back, which indicates that the function did not execute correctly in the DLL.
My C# code thus far:
/*** C# implementation of C Function AwdApiLookup ***/
DllImport("awdapi.dll", CharSet = CharSet.Auto)]
public static extern bool AwdApiLookup(IntPtr handle, ulong ulMsg,
bool fContainer, char cObjectType,
ref SearchCriteria pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
/*** C# implementation of C Struct SEARCH_CRITERIA ***/
[StructLayout(LayoutKind.Sequential)]
public struct SearchCriteria
{
private readonly byte[] m_DataName;
private readonly byte[] m_Wildcard;
private readonly byte[] m_Comparator;
private readonly byte[] m_DataValue;
public SearchCriteria(string dataName, string comparator, string dataValue)
{
m_DataName = Encoding.Unicode.GetBytes(
dataName.PadRight(4, ' ').Substring(0, 4));
m_Wildcard = Encoding.Unicode.GetBytes("0");
m_Comparator = Encoding.Unicode.GetBytes(
comparator.PadRight(2, ' ').Substring(0, 2));
m_DataValue = Encoding.Unicode.GetBytes(
dataValue.PadRight(75, ' ').Substring(0, 75));
}
public byte[] dataname { get { return m_DataName; } }
public byte[] wildcard { get { return m_Wildcard; } }
public byte[] comparator { get { return m_Comparator; } }
public byte[] datavalue { get { return m_DataValue; } }
}
My C# call to the C# function looks like this
var callResult = UnsafeAwdApi.CallAwdApiLookup(IntPtr.Zero, 0, true, 'W',
ref searchCriteria[0], criteriaCount,
66, null);
where searchCriteria and criteriaCount is defined as
List<SearchCriteria> criteriaList = new List<SearchCriteria>();
var searchCriteria = criteriaList.ToArray();
var criteriaCount = (ushort)searchCriteria.Length;
and adding data to searchCriteria:
public void AddSearchCriteria(string dataName, string comparator, string dataValue)
{
var criteria = new SearchCriteria();
criteria.DataName = dataName;
criteria.Wildcard = "0";
criteria.Comparator = comparator;
criteria.DataValue = dataValue;
criteriaList.Add(criteria);
}
Like I said, my code compiles correctly, but when the function executes, it returns "FALSE", which should not be the case as the Delphi function does return data with the exact same input.
I know I'm definitely doing something wrong here, and I've tried a couple of things, but nothing seems to be working.
Any assistance or nudge in the right direction would be greatly appreciated.
Thanks, Riaan
Several things here.
First of all C++ ULONG is a 32-bit integer, and becomes uint in C# - ulong is 64-bit.
For the struct, you don't need to mess with byte arrays. Use strings, and ByValTStr. Also, it's not really worth bothering with readonly and properties for interop structs. Yes, mutable value types are generally bad in a pure .NET API, but in this case it's the existing API, there's no point in masking it. So:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SearchCriteria
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4]
public string m_DataName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1]
public string m_Wildcard;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2]
public string m_Comparator;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 75]
public string m_DataValue;
}
If you really want to do all the string conversions yourself, it may be easier to just use unsafe and fixed-size arrays:
public unsafe struct SearchCriteria
{
public fixed byte m_DataName[4];
public byte m_Wildcard;
public fixed byte m_Comparator[2];
public fixed byte m_DataValue[75];
}
[EDIT] Two more things.
CHAR cObjectType should become byte cObjectType, and not char cObjectType that you currently use.
Also, yes, there is a problem with array marshaling in your example. Since your P/Invoke declaration is ref SearchCriteria pSearchCriteria - i.e. a single value passed by reference - that's precisely what P/Invoke mashaler will do. Keep in mind that, unless your struct only has fields of unmanaged types in it (the one with fixed arrays above is that, the one with string is not), the marshaler will have to copy the structs. It won't just pass address to the first element of the array directly. And in this case, since you didn't tell it it's an array there, it will only copy the single element you reference.
So, if you use the version of the struct with string, fields, you need to change the P/Invoke declaration. If you only need to pass SEARCH_CRITERIA objects into the function, but won't need to read data from them after it returns, just use an array:
public static extern bool AwdApiLookup(IntPtr handle, uint ulMsg,
bool fContainer, byte cObjectType,
SearchCriteria[] pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
And call it like this:
var callResult = UnsafeAwdApi.CallAwdApiLookup(
IntPtr.Zero, 0, true, (byte)'W',
searchCriteria, criteriaCount,
66, null);
If function writes data into that array, and you need to read it, use [In, Out]:
[In, Out] SearchCriteria[] pSearchCriteria,
If you use the version with fixed byte[] arrays, you can also change the P/Invoke declaration to read SearchCriteria* pSearchCriteria, and then use:
fixed (SearchCriteria* p = &searchCriteria[0])
{
AwdApiLookup(..., p, ...);
}
This will require unsafe as well, though.