I have the following test function set up in a C project:
__declspec(dllexport) int test(char *str, int strlen){
char* h = "Hello";
int length = 5;
for(int i = 0; i < length; i++){
str[0] = h[0];
}
return strlen;
}
And in my C# project I declare the method as follows:
[DllImport("solver.dll", CharSet = CharSet.Unicode ,CallingConvention = CallingConvention.Cdecl)]
public static extern int test(StringBuilder sol, int len);
And I try to use it in my project like so:
StringBuilder sol = new StringBuilder(15);
int t = test(sol, sol.Capacity);
string str = sol.ToString();
I'd like to pass "Hello" back to the C# code as a test, but when I run the code the StringBuilder stays empty, and even though 15 is passed to the C function as the length, when the C function returns the length it returns a 'random' large number like 125822695. What am I missing?
A number of problems:
You state CharSet.Unicode in the C#, but use ANSI in the unmanaged code.
You only write to the first character.
You ignore the value of strlen passed to the function.
You don't attempt to write a null-terminator.
As for the value returned from the function, that cannot be explained by the code that you present. There is something missing from your question that we need to see in order to explain that.
It is quite common when we see these questions, that the unmanaged code is clearly broken. It's hard enough to write correct p/invokes to correct unmanaged code. Trying to debug the p/invoke and the unmanaged code at the same time is so much harder. Test your unmanaged code in an unmanaged setting first. Only when you are confident it is correct should you move to write your p/invoke.
Related
Despite all the questions, I can't find a suitable answer for doing this.
My goal is to fill a string[] with the use of a DLL that returns a char**.
DLL Declaration :
extern "C" SHTSDK_EXPORT int GetPeerList(SHTSDK::Camera *camera, int* id, int id_size, char** name, int name_size, int* statut, int statut_size);
My import :
[DllImport(libName)]
static public extern int GetPeerList(IntPtr camera, IntPtr id, int id_size, IntPtr name, int name_size, IntPtr statut, int statut_size);
My use in C# code :
StringBuilder[] name = new StringBuilder[nbPeer];
for (int i = 0; i < nbPeer; i++)
{
name[i] = new StringBuilder(256);
}
//Alloc peer name array
GCHandle nameHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
IntPtr pointeurName = nameHandle.AddrOfPinnedObject();
int notNewConnection = APIServices.GetPeerList(cameraStreaming, pointeurId,
nbPeer, pointeurName, nbPeer, pointeurStatut, nbPeer);
// Now I'm supposed to read string with name[i] but it crashes
What did I miss? I really searched on the other topics, I thought this one could work, but still crashing.
Thanks.
I suggest you developing a tiny C++/CLI bridging layer. The purpose of this C++/CLI bridge is to take the string array returned by the DLL in the form of char** raw pointers, and convert it to a .NET string array, that can be consumed in your C# code as a simple string[].
The C++/CLI version of C# string[] (string array) is array<String^>^, e.g.:
array<String^>^ managedStringArray = gcnew array<String^>(count);
You can use the usual syntax with operator[] (i.e. managedStringArray[index]) to assign each string to the array.
You may write some code like this:
// C++/CLI wrapper around your C++ native DLL
ref class YourDllWrapper
{
public:
// Wrap the call to the function of your native C++ DLL,
// and return the string array using the .NET managed array type
array<String^>^ GetPeerList( /* parameters ... */ )
{
// C++ code that calls your DLL function, and gets
// the string array from the DLL.
// ...
// Build a .NET string array and fill it with
// the strings returned from the native DLL
array<String^>^ result = gcnew array<String^>(count);
for (int i = 0; i < count; i++)
{
result[i] = /* i-th string from the DLL */ ;
}
return result;
}
...
}
You may find this article on CodeProject on C++/CLI arrays an interesting reading as well.
P.S. The strings returned from your native DLL are in the form of char-strings. On the other hand, .NET strings are Unicode UTF-16 strings. So you need to clarify what encoding is used to represent text in your native strings, and convert to UTF-16 for .NET strings.
I am currently testing some PInvoke stuff and wrote a short C function to try some different things out. I successfully managed to pass Ints and return an addition, but I am having some trouble when it comes to strings.
Here is the C function:
__declspec(dllexport) int test(char *str, int slen){
for(int i = 0; i < slen; i++){
str[i] = 'a';
}
return slen;
}
And here is the C# function declaration and usage:
[DllImport("solver.dll", CharSet = CharSet.Ansi ,CallingConvention = CallingConvention.Cdecl)]
public static extern int test(StringBuilder sol, int len);
StringBuilder sol = new StringBuilder(15);
int ret = test(sol, sol.Capacity);
string str = sol.ToString();
I've been researching this for most of the day and I've seen several posts about simply passing a StringBuilder, filling it on the C end and it should be accessible when the function finishes. However I am currently getting an AccessViolation error in the C code as if the Memory hadn't been allocated, but I definitely allocate the Memory with new StringBuilder(15)
The C function definitely works if I allocate a piece of memory in the C code and pass it.
Is there something I am missing?
Sounds like you are missing to NUL-terminate the string buffer.
You may want to update your code like this:
for (int i = 0; i < (slen-1); i++) {
str[i] = 'a';
}
str[slen-1] = '\0'; // Add a NUL-terminator
Note that in this case I'm assuming the buffer length passed by the caller is the total length, including room for the NUL-terminator.
(Other conventions are possible as well, for example assuming the length passed by the caller excludes the NUL-terminator; the important thing is clarity of the interface documentation.)
I'd also like to add that a usual calling convention for those exported functions is __stdcall instead of __cdecl (although that's not correlated to your problem).
interoping nim dll from c# i could call and execute the code below
if i will add another function (proc) that Calls GetPacks() and try to echo on each element's buffer i could see the output in the C# console correctly
but i could not transfer the data as it is, i tried everything but i could not accomplish the task
proc GetPacksPtrNim(parSze: int, PackArrINOUT: var DataPackArr){.stdcall,exportc,dynlib.} =
PackArrINOUT.newSeq(parSze)
var dummyStr = "abcdefghij"
for i, curDataPack in PackArrINOUT.mpairs:
dummyStr[9] = char(i + int8'0')
curDataPack = DataPack(buffer:dummyStr, intVal: uint32 i)
type
DataPackArr = seq[DataPack]
DataPack = object
buffer: string
intVal: uint32
when i do same in c/c++ the type i am using is either an IntPtr or char*
that is happy to contain returned buffer member
EXPORT_API void __cdecl c_returnDataPack(unsigned int size, dataPack** DpArr)
{
unsigned int dumln, Index;dataPack* CurDp = {NULL};
char dummy[STRMAX];
*DpArr = (dataPack*)malloc( size * sizeof( dataPack ));
CurDp = *DpArr;
strncpy(dummy, "abcdefgHij", STRMAX);
dumln = sizeof(dummy);
for ( Index = 0; Index < size; Index++,CurDp++)
{
CurDp->IVal = Index;
dummy[dumln-1] = '0' + Index % (126 - '0');
CurDp->Sval = (char*) calloc (dumln,sizeof(dummy));
strcpy(CurDp->Sval, dummy);
}
}
c# signature for c code above
[DllImport(#"cdllI.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
private static extern uint c_returnDataPack(uint x, DataPackg.TestC** tcdparr);
C# Struct
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
public IntPtr StrVal;
}
}
finally calling the function like so:
public static unsafe List<DataPackg.TestC> PopulateLstPackC(int ArrL)
{
DataPackg.TestC* PackUArrOut;
List<DataPackg.TestC> RtLstPackU = new List<DataPackg.TestC>(ArrL);
c_returnDataPack((uint)ArrL, &PackUArrOut);
DataPackg.TestC* CurrentPack = PackUArrOut;
for (int i = 0; i < ArrL; i++, CurrentPack++)
{
RtLstPackU.Add(new DataPackg.TestC() { StrVal = CurrentPack->StrVal, Id = CurrentPack->Id });
}
//Console.WriteLine("Res={0}", Marshal.PtrToStringAnsi((IntPtr)RtLstPackU[1].StrVal));//new string(RtLstPackU[0].StrVal));
return RtLstPackU;
}
how could i produce similar c code as above from Nim ?
it doesn't have to be same code, but same effect, that in c# i would be able to read the content of the string. for now, the int is readable but the string is not
Edit:
this is what i tried to make things simple
struct array of int members
Update:
it seem that the problem is to do with my settings of nim in my windows OS.
i will be updating as soon as i discover what exactly is wrong.
The string type in Nim is not equivalent to the C's const char* type. Strings in Nim are represented as pointers, pointing into a heap-allocated chunk of memory, which has the following layout:
NI length; # the length of the stored string
NI capacity; # how much room do we have for growth
NIM_CHAR data[capacity]; # the actual string, zero-terminated
Please beware that these types are architecture specific and they are really an implementation detail of the compiler that can be changed in the future. NI is the architecture-default interger type and NIM_CHAR is usually equivalent to a 8-bit char, since Nim is leaning towards the use of UTF8.
With this in mind, you have several options:
1) You can teach C# about this layout and access the string buffers at their correct location (the above caveats apply). An example implementation of this approach can be found here:
https://gist.github.com/zah/fe8f5956684abee6bec9
2) You can use a different type for the buffer field in your Nim code. Possible candidates are ptr char or the fixed size array[char]. The first one will require you to give up the automatic garbage collection and maintain a little bit of code for manual memory management. The second one will give up a little bit of space efficiency and it will put hard-limits on the size of these buffers.
EDIT:
Using cstring may also look tempting, but it's ultimately dangerous. When you assign a regular string to a cstring, the result will be a normal char * value, pointing to the data buffer of the Nim string described above. Since the Nim garbage collector handles properly interior pointers to allocated values, this will be safe as long as the cstring value is placed in a traced location like the stack. But when you place it inside an object, the cstring won't be traced and nothing prevents the GC from releasing the memory, which may create a dangling pointer in your C# code.
Try to change your struct to:
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
[MarshalAs(UnmanagedType.LPStr)]
public String StrVal;
}
}
I need some help with the following. I've got a c++ API (no access to source) and I'm struggling with the methods returning char* attributes, or returned structures containing char* attributes. According to the API's documentation the return value is as follows:
Return Values
If the function succeeds, the return value is a pointer to a series of null-terminated strings, one for each project on the host system, ending with a second null character. The following example shows the buffer contents with <null> representing the terminating null character:
project1<null>project2<null>project3<null><null>
If the function fails, the return value is NULL
The problem I'm having is that the returned pointer in C# only contains the first value... project1 in this case. How can I get the full list to be able to loop through them on the managed side?
Here's the c# code:
[DllImport("vmdsapi.dll", EntryPoint = "DSGetProjectList", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr DSGetProjectList();
Calling method:
IntPtr ptrProjectList = DSAPI.DSGetProjectList();
string strProjectList = Marshal.PtrToStringAnsi(ptrProjectList).ToString();
strProjectList only contains the first item.
Here's the info from the API's header file...
DllImport char *DSGetProjectList dsproto((void));
Here's some sample code from a c++ console app which I've used for testing purposes...
char *a;
a = DSGetProjectList( );
while( *a ) {
printf("a=%s\n", a);
a += 1 + strlen(a);
}
Each iteration correctly displays every project in the list.
The problem is that when converting the C++ char* to a C# string using Marshal.PtrToStringAnsi, it stops at the first null character.
You shouldn't convert directly the char* to a string.
You could copy the char* represented by an IntPtr to a byte[] using Marshal.Copy and then extract as many string as necessary (see Matthew Watson's answer for extracting strings from a managed array), but you'll need to get the multi-string size first.
As leppie suggest you can also extract the first string using Marshal.PtrToStringAnsi then increment the pointer by this string size and extract the next string and so on. You stops when is extracts an empty string (from the last NULL character).
Something like :
IntPtr ptrProjectList = DSAPI.DSGetProjectList();
List<string> data;
string buffer;
do {
buffer = Marshal.PtrToStringAnsi(ptrProjectList);
ptrProjectList += buffer.size() + 1;
data.Add(buffer);
}while(buffer.size() > 0)
This kind of string is called a Multi-String, and it's quite common in the Windows API.
Marshaling them is fiddly. What you have to do is marshal it as a char[] array, rather than a string, and then convert the char[] array to a set of strings.
See here for an example solution. I've copied the relevant code into this answer, but it is copied from the link I gave:
static List<string> MultiStringToList(char[] multistring)
{
var stringList = new List<string>();
int i = 0;
while (i < multistring.Length)
{
int j = i;
if (multistring[j++] == '\0')
break;
while (j < multistring.Length)
{
if (multistring[j++] == '\0')
{
stringList.Add(new string(multistring, i, j - i - 1));
i = j;
break;
}
}
}
return stringList;
}
I have declared a DLL import in my C# program that looks like this:
[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode",
CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr generateKeyCode(char[] serial, char[] option, char c_type);
It references the function generateKeyCode() inside of my DLL.
Here is the code that is causing an error (used breakpoints):
const char* generateKeyCode(char serial[],
char option[],
char c_type)
{
returnBufferString = "";
SHA1_CTX context;
int optionLength = 0;
#ifdef WIN32
unsigned char buffer[16384] = {0};
#else
unsigned char buffer[256] = {0};
#endif
//char output[80];
char keycode[OPTION_KEY_LENGTH+1] = "";
int digest_array_size = 10; //default value for digest array size
unsigned char digest[20] = {0};
char optx[24] = {0};
char c_type_upper;
// Combine serial # and Option or Version number
char str1[30] = {0};
int i;
int size = 0;
int pos = 0;
...
...
}
Basically, I imported this DLL so I could pass the function parameters and it could do its algorithm and simply return me a result. I used this marshaler function...
public static string genKeyCode_marshal(string serial, string option, char type)
{
return Marshal.PtrToStringAnsi(generateKeyCode(serial.ToCharArray(),
option.ToCharArray(), type));
}
...so I could make the call properly. Inside of my C++ header file, I have defined a string, as indicated is helpful in the answer to this question (it is the returnBufferString variable present at the top of the C/C++ function).
I make this function call several times as I use a NumericUpDown control to go from 1.0 to 9.9 in increments of 0.1 (each up or down accompanies another function call), and then back down again. However, every time I try to do this, the program hitches after a seemingly set number of function calls (stops at 1.9 on the way back down if I just go straight up and down, or earlier if I alternate up and down a bit).
Please note that it works and gives me the value I want, there are no discrepancies there.
I changed the buffer size to some smaller number (5012) and when I tried to run the program, on the first function call it threw the AccessViolationException. However, doubling the buffer size to twice (32768) the original had no effect in comparison to the original -- going straight up to 9.9 from 1.0 and down back again, it stops at 1.9 and throws the exception.
EDIT: Default is ANSI, so it is ANSI. No problems there. Is this a memory allocation issue??
I would suggest trying the following:
[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr generateKeyCode(string serial, string option, char c_type);
Note the new CharSet field of DllImport attribute.
Next idea is to use MarshalAs attribute explicitely:
[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
static extern IntPtr generateKeyCode([MarshalAs(UnmanagedType.LPTStr)] string serial, [MarshalAs(UnmanagedType.LPTStr)] string option, char c_type);
I know this may be unsatisfactory, but once I removed the output redirection I was using to debug from within my C/C++ DLL, the problem stopped. Everything works now, so I guess that's essentially equivalent to answering my own question. Thanks to everyone for the replies.