Click here to Skip to main content
15,881,172 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm attempting to finish up a project in C#, but I'm having problems with the C#'s calling conventions that calls "C" functions. I'm also dealing with char and integer,
and bools datatypes.

Within my C# program, I've been using the following declaration(1):
1) [DllImport("mydllfile.dll")] static extern
int ACeeFunction(StringBuilder aBuffer, ref int iostatz, int nSize);
but I've also seen two others:
2) [DllImport("mydllfile.dll")] static extern
int ACeeFunction(String aBuffer, ref int iostatz, int nSize);
and
3) [DllImport("mydllfile.dll")] static extern
int ACeeFunction(char aBuffer, ref int iostatz, int nSize);

I'll be using these "C" function declarations(below), as an example for my next few questions:

int ACeeFunctOne( int nsize, char * charbuff, int * iostatz ) {
}
int ACeeFunctTwo( char * newbuff, int * iostatz, int * nsize, char * charbuff) {
}
int ACeeFunction( char * charbuff, int * iostatz, int nsize) {
}

In all my DLL "C" functions, I use a variety of these " char * charbuff " declarations in all my function parameter lists and also in the .def file. Like above.

Which of the three "DllImport" statements, using the first paramter of each; match the "ACeeFunction"'s first parameter??
Does it match correctly in form of size, data type, length, and so forth?

As of right now:
1) While in debug mode, these two programs work together PERFECTLY with no
exceptions.
2) I can passed data back and forth, between C# and the DLL's C routines.
3) But, when both are changed from debug to release x86 mode, things get
a little nick-picky and errors/exceptions start popping up, and sometimes
the C# program will unknowly abend.
4) I can pass numerical data by reference or by value in c#.
I can also pass bool values.

So, which of the three C# "DllImport" statements is the right one to use?

I hope this all makes sense, cause this is the best way I could put it all down.

Oh OriginalGriff, RSVP
I need help with this problem, It's like writing a new language parser. Remember.

Thanks, If you have questions, email me.

What I have tried:

I've written a complete c# program, along with a windows dll file. They communicate quite will. I have written a big C# program, along with a Windows DLL, filled with many "C" routines.
1) While in debug mode, these two programs work together PERFECTLY with no exceptions.
2) I can passed data back and forth, between my C# program and the DLL's C routines.
3) But when both are changed from debug to release x86 mode, things get a little nick-picky and errors/exceptions
start popping up, and sometimes the C# program will unknowly abend.
4) ) I can pass numerical data by reference or by value in c#. I can also pass bool values back and forth.
Posted
Updated 12-Oct-21 8:11am

Native interoperability best practices - .NET | Microsoft Docs[^]

If your C function is not modifying the charbuff, then declaring the parameter as string is the right thing to do. If you can make it use Unicode (wchar_t*), and mark the P/Invoke function argument with [MarshalAs(UnmanagedType.LPWSTR)], that will give you the best performance, since the string won't need to be copied at all.

If your C function is modifying the charbuff, then passing a string is definitely the wrong thing to do. You would need to mark it as ref or out to see the changes from C#, and that would destabilize the runtime.

The recommendation is also to avoid using a StringBuilder. It will work, but it will involve a lot of copying between the C# and C memory. Instead, Microsoft recommend renting a shared char[] array of sufficient size from an array pool, and passing that in. Unfortunately, I couldn't find an example of that in the documentation - the links for "more information" only show string and StringBuilder examples, and include examples which explicitly violate their "best practices" recommendations.


int parameters are a lot simpler: a C int needs to be declared as int in C#, whereas a C int* needs to be either ref int or out int in C#.
 
Share this answer
 
Comments
Member 15028314 8-Oct-21 0:56am    
Yes, my "C" functions send and receive data and even modify
char, ints, real, and bool data. Cause that's what they're built for.
SO avoid Stringbuilder, and strings. Hum... Ok, now what? I'm still looking
for the correct C# calling conventions that calls "C" routines, that allows me
to send and receive a variety of data types. Such as (*)char *, ints, reals, and
bools information. My two big projects are sitting idle. I need some solid
examples showing me what needs to be done. Cause if I run it release mode, my
program runs and then abends when it feels like. Which is not very good. Do
you know of any persons who has the knowledge that I'm looking for. For C#
to call many DLL "C" routines? I'd appreciate any solid help.
(*)- char * datatypes. Not strings. RSVP, Thanks. Thanks for fast initial response.
Member 15028314 11-Oct-21 0:17am    
I do offer Apologies for my rudeness and other bad traits. Sorry.
If your "int *" is simply a pointer to a single int that is going to change, use "ref int" in your C# DllImport statement and "int *" in your C function.

Passing arrays back and forth between C# and C requires explicit marshalling. An example for an array of int32_t (when marshalling, it is always advisable to use explicity-sized things):

in C#:
[[DllImport ("dll.dll", Calling Convention = CallingConvention.Cdecl)]
private static extern void A_Function (IntPtr arrayPtr, int arraySize);

IntPtr unmanArray = Marshal.AllocHGlobal (300 *sizeof (Int32));
// loop and use Marshal.WriteInt32 or unsafe pointers to fill your array
A_Function (unmanArray, 300);

Int32[] manArray = new Int32[300]; // or reuse the managed array that was the data source if it's big enough
Marshal.Copy (unmanArray, manArray, 0, 300);

Marshal.FreeHGlobal (unmanArray);

// now use the managed array manArray in your C# code

The corresponding C code is the expected
void A_Function (int32_t * arrayInts, int arraySize);


I always pass an array size when going between C and C# -- it avoids buffer overruns to have an explicit size in the C code.

The same principle applies to an array of anything other base type (float, double, byte).

please note - if you have any more questions, be warned that my responses will be slow in
coming but i'll try to help.
 
Share this answer
 
v3
Comments
Member 15028314 24-Oct-21 19:40pm    
A comment please... Within this website, it was mentioned that I should set the c# application's environment
and windows dll application's environment, each to the release/x86 mode.
I did it, and my c# program and my windows dll file routines communicates quite well. Yet we still have problems.
Yet many times before, I was able to have my C# program call a bunch of DLL "C" routines to perform many
different process conversions. Much over a 100 different conversions, to say the least. Now, I can only run less
than 61 contiguous process', otherwise the C# program just silently abends and disappears. Are there any "C" call
layering restrictions or what?? I know the "C" routines work, cause they have proformed themselves excellent
before. Are there still other C# and "C" hidden caveats that I don't about?
Any suggestions? cause I'm lost for words.
McGruff...
JudyL_MD 26-Oct-21 10:22am    
I always set my C# and C to the environment on which the app will run. If you're actually using a 32-bit machine, yes use the x86 mode. If you're running on a 64-bit machine, set your C# to 64-bit (AnyCPU for the target and _uncheck_ Prefer 32-bit) and use x64 for your C code. If needed, I deliver two versions in my installer. I don't trust 32- to 64-bit thunking so I _always_ match my C code to my machine hardware.

You're getting absolutely no indications why the C# processes are aborting? You may need to add your own logging to see where the problems are. Make sure your error checking is robust -- if it can error, even for the most obscure reasons, check those return codes! My only other thought is to check that your C routines are truly re-entrant.

Good luck.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900