Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does printf work with managed Strings?

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!

like image 553
Excelcius Avatar asked Aug 06 '12 15:08

Excelcius


People also ask

How to print string using printf in c++?

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.

How do I print a printf string?

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.


1 Answers

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.

like image 82
vcsjones Avatar answered Oct 10 '22 04:10

vcsjones