we are currently digging through some really old C++/CLI-Code (Old Syntax .NET Beta) and were a bit surprised to see something like this:
System::String ^source("Test-String");
printf("%s", source);
The program correctly outputs
Test-String
We are wondering, why is it possible to pass the managed string source to printf
- and more importantly: Why does it work? I don't expect it to be some convenience-feature by the compiler because the following doesn't work:
System::String ^source("Test-String");
char pDest[256];
strcpy(pDest, source);
This produces a (somehow expected) compiling error saying that System::String^
can't be converted to const char*
. So my only real explanation is that passing a managed reference to a va_list surpasses all compiler-checks and tricks the native code into using a pointer into the managed heap. Since System::String
is represented similar to a char
-Array in memory, printf
may work. Or the compiler converts to a pin_ptr
and passes that to printf
.
I don't expect it to automatically marshal the String^
to char*
, because that would result in a bad memory leak without any reference to the actual memory address.
We know that this isn't a good solution and the various marshalling methods introduced by the later Visual Studio-Versions provide a way better approach but it would be very interesting to understand what is actually happening here.
Thanks!
Example 1: C++ printf() In this program, we have used the printf() function to print the integer num and the C-string my_name . printf("num = %d \n", num); printf("My name is %s", my_name); Here, %d is replaced by the num variable in the output.
We can print the string using %s format specifier in printf function. It will print the string from the given starting address to the null '\0' character. String name itself the starting address of the string. So, if we give string name it will print the entire string.
I believe it is because the compiler is turning it into this IL:
call vararg int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) printf(int8 modopt([mscorlib]System.Runtime.CompilerServices.IsSignUnspecifiedByte) modopt([mscorlib]System.Runtime.CompilerServices.IsConst)*, ..., string)
Which ends up as a pinvoke call to printf
, so the runtime is being a little sneaky by marshalling it for you. You are still in a managed runtime, and the runtime will provide marhsalling as a service when it's needed.
Some notes:
It seems that clr!GenericPInvokeCalliHelper
is doing this lifting on the x86 .NET 4 Workstation CLR.
the following doesn't work
That's because that is straight C++. It has no chance to go through marshalling because it isn't needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With