Click here to Skip to main content
Click here to Skip to main content

Mixing Managed and Unmanaged code

By , 30 May 2001
 

Abstract

C++ managed code introduced us a new string type, namely System.String. You can imagine though, that some conversion functions are needed to work with this new string type when mixing managed and unmanaged code in your project.

Introduction

Managed code runs under the .NET runtime Execution Engine, which gives you the advantage of garbage collection, JIT, etc. Currently, as a programmer you can choose between C#, C++ managed extensions and VB.NET to write your managed code (there are also .NET versions of FORTRAN and COBOL, however, these are not widely available at the time of writing - ed. ). However, in some cases, you do want to mix your managed code with unmanaged code : either you still want to continue using your current source code base and plug it in .NET, maybe you want to build .NET interfaces around your existing code, or you decide to write some routines unmanaged because of performance issues.

Mixing managed and unmanaged code

/CLR

If you want to mix managed and unmanaged code in the same sources, your code should be compiled with the /CLR compiler option. This means that - by default – all your functions are managed. For compilation: members of managed classes as well as functions who use managed data types only compile as managed.

#pragma managed
#pragma unmanaged

Using the managed and unmanaged pragmas enables function-level control for compiling functions managed or unmanaged.

Strings

Unmanaged C++ applications use string types composed of ASCII/UNICODE characters ( char* , wchar_t* , std::string , CString ). Managed objects can’t handle these types, therefore a new string type System.String is introduced.

In the MSDN documentation you will find that :

"Regular C++ wide-character string literals (prefixed by L) and managed string literals (prefixed by S) can be used interchangeably where String types are expected. However, the reverse is not true. Managed string literals can not be used where C++ string types are expected."

So, in functions where a char* argument is expected, a conversion from String to char* will need to happen. In the System::Runtime::InteropServices namespace (assembly: mscorlib.dll ), a Marshal class is available to do such conversions. This Marshal class takes care of unmanaged memory allocation, copying unmanaged memory blocks, and managed to unmanaged type conversions.

For example, consider this piece of code (written in C++) :

#pragma unmanaged

#include "Windows.h"

class CUnmanagedClass
{
public:
	void ShowMessageBox(char* szMessage)
	{
		::MessageBox(NULL, szMessage , "CUnmanagedClass", MB_OK);
	}
};

#pragma managed

#using <mscorlib.dll>

using namespace System;
using namespace System::Runtime::InteropServices;

__gc class CManagedClass
{
public:
	void ShowMessage(System::String* strMessage)
	{
     	System::Console::WriteLine(strMessage);
	}
};

int main(void) 
{
	String* strMessage = S"Hello world";
	
	CManagedClass* pCManagedClass = new CManagedClass();
	pCManagedClass->ShowMessage(strMessage);
	
	char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage);
	CUnmanagedClass cUnmanagedClass; cUnmanagedClass.ShowMessageBox(szMessage);
	Marshal::FreeHGlobal((int)szMessage);
	
	return 0;
}

The unmanaged class CUnManagedClass , contains one method which expects a char* as argument. If you a want to call that method ( ShowMessageBox ) from a managed piece of code, you would need to do a conversion like this:

char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage);

And freeing the memory again with FreeHGlobal:

 Marshal::FreeHGlobal((int)szMessage);

In the sample, I used StringToHGlobalAnsi to do the conversion. StringToHGlobalAnsi copies the contents of a managed String object into native heap and converts it into ASCII. The result of StringToHGlobalAnsi is an integer value, which should be cast to a char pointer.

Data marshaling when making calls between managed and unmanaged code when using P/Invoke in C#

In C#, unmanaged methods are called by using the Runtime's Platform Invocation Service (P/Invoke). Your data also needs to be marshaled then : the marshalling attributes can be specified in the dllimport statement. Consider the following lines of code (written in C#):

[DllImport("user32.dll")] 
public static extern int MessageBoxA(int h,  
            [MarshalAs(UnmanagedType.LPStr)]string m,  
            [MarshalAs(UnmanagedType.LPStr)]string c,  
            int type); 
 
public static int Main(string args [])
{
	MessageBoxA(0, "Hello World!", "My Message Box", 0); 
    return 0; 
} 

The function MessageBoxA expects 2 char pointer arguments (these are non-supported types in a managed environment). By specifying the attribute MarshalAs(UnmanagedType.LPStr) , the runtime's marshaling service will take care of transferring the data of our strings from the managed environment to the unmanaged environment.

Conclusion

Mixing managed and unmanaged code can be very powerful, and it gives us – programmers - a lot more freedom to decide on how to code. However, we should be aware that mixing these technologies introduces some extra difficulties and should be taken care off from the beginning, when starting to design your .NET applications.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Nick Van den Abbeele
Web Developer
Belgium Belgium
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralC2440 Error when convert to char *memberkosmas26 Apr '07 - 20:26 
Hello Sir,
 
I tried the code you mention like:
 
char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage);
 
this will cause a compile error with C2440: Can not convert from 'System::IntPtr' to 'char *'.
 
Is there any idea to solve this error? Thank you.D'Oh! | :doh:
GeneralRe: C2440 Error when convert to char *memberVertexwahn2 May '07 - 21:01 
char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage).ToPointer();
GeneralRe: C2440 Error when convert to char *groupvukovicg10 Jul '11 - 20:53 
char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage).ToPointer();
GeneralConvert a Managed class to an Unmanaged typememberTrankil31 May '05 - 7:15 

Hi !
 
Creating a thread, I want to pass a parameter (this), which is a pointer to my managed class to the working function of the thread;
 
this->m_hReadThread = CreateThread(NULL, 0,(LPTHREAD_START_ROUTINE)(CEngine::ReadThreadFunc),this,CREATE_SUSPENDED,&dwReadThreadId);
 
but the error is :
'CreateThread' : cannot convert the parameter 4 from 'CEngine __gc *const ' to 'LPVOID'
 
Another problem is in the static working function
void CEngine::ReadThreadFunction(LPVOID lpData)
{
CEngine* Manager = (CEngine*)lpData;
...
}
 
Here the error is
cannot convert 'LPVOID' to 'CEngine __gc *' Cannot convert an unmanaged type to a managed type
 
could you help me to do the convertion ?
 

GeneralRe: Convert a Managed class to an Unmanaged typememberBhagirathi Lakshmivarahan19 Jul '07 - 7:28 
Hi,
 
It must be a long time since you had that problem. I have a similar situation. Do you think you can help?
 
Thanks
Bhagi.
Generalconvert std::string to System::String*memberEmiliano30 Mar '05 - 12:11 
Hello i read your article, I want to convert std::string to System::String*
 
i have experimented a few ways of doing it, i found the following one to be good
 
const char* str = fName->c_str();
System::String* gcStr = str;
 
is there a better one?
GeneralRe: convert std::string to System::String*memberqjdnnbfjqyyk23 Aug '06 - 9:42 
std::string a
System::String^ b = gcnew String(a.c_str());
QuestionHow can I convert a Gdiplus.Graphic* to System.Drawing.Graphics*?sussAnonymous24 Jan '05 - 20:10 
I need to call a .net assembly from an ATL project using Gdiplus. I can get a Gdiplus.Graphic* pointer, but the function call need a System.Drawing.Graphics*, what should I do?
GeneralError C3633memberbamoo18 Jan '05 - 8:34 
Hello Smile | :) ,
I want to put a unmanaged class in a managed class. Is it possible ?
In fact, I want to put the unmanaged class in Form1 :
public __gc class Form1 : public System::Windows::Forms::Form
 
Here is the code :
#pragma unmanaged
 
#include "windows.h"
#include "stdafx.h"
 
class CUnmanagedClass
{
private :
int p;
public:
void ShowMessageBox(char* szMessage)
{
//MessageBox(0, szMessage , "CUnmanagedClass", MB_OK);
}
CUnmanagedClass(int a)
{
p=a;
}
CUnmanagedClass(){}
};
 
#pragma managed
 
#using
 
using namespace System;
using namespace System::Runtime::InteropServices;
 
__gc class CManagedClass
{
CUnmanagedClass cUnmanagedClass3(5);
public:
void ShowMessage(System::String* strMessage)
{
System::Console::WriteLine(strMessage);
}
};
 
I have an error :
error C3633: cannot define 'cUnmanagedClass3' as a
member of managed 'cUnmanagedClass'
 
Thanks Smile | :)

GeneralUn managed CString to managed String*memberharanath5 May '03 - 6:46 
Hi
I would like to know,
How to transform the un manged CString (C++ world)to the manged String*(MC++/C# world).
 
Here is the example:
//I have the following CString in C++ code
CString aFileName;
 
//I need to convet it into String* so that I can use this in C#
I need the string in some variable of type,
String*
 

Thanks
 

 
Siemens
Generalsmall questionsussAnonymous26 Jul '02 - 10:46 
I have a dll written in VC 6.0
One of the exported function takes std::string as a parameter.
I am using VC 7.0 to write an application and I use the exported function. For some reason, whatever value I pass from my application , in the dll, the value is always null..
How can I get around this?
GeneralOne small problem...memberAnonymous20 Jun '02 - 6:47 
In order to get this to work I need to make a small change:
 
char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage);
 
To:
 
char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage).ToPointer();
 


GeneralRe: One small problem...sussAnonymous24 Jul '02 - 10:52 
Is this really true? The documentation states the following for the Return Value of the Marshal.StringToHGlobalAnsi Method.
 
"The address, in native heap, to where s was copied, or 0 if a null reference (Nothing in Visual Basic) string was supplied."
GeneralRe: One small problem...sussAnonymous13 Oct '02 - 14:33 
Yup... It does not compile without that change. This should be corrected in the article.Frown | :(
GeneralRe: One small problem...memberratty15 Nov '02 - 9:15 
test
GeneralRe: One small problem...memberratty15 Nov '02 - 9:16 
In managed code do
 
System::IntPtr intPtr = Marshal::StringToHGlobalAnsi("foo");
char* p = (char*)intPtr.ToPointer();
 
Now you have a char* to "foo"...
GeneralRe: One small problem...memberboaza25 Jan '07 - 2:34 
...and don't forget the free as well:
From:
Marshal::FreeHGlobal((int)szMessage);
To:
Marshal::FreeHGlobal(IntPtr(fileNameBuf));

 
bav delux
GeneralRe: One small problem...memberJeff Lehmer24 Sep '09 - 5:08 
I think it is better to hold on to the handle than recast it back to an IntPtr later. I also prefer to use reinterpret_cast:
 
System::IntPtr handle = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(s);
std::string str = reinterpret_cast<char*>(handle.ToPointer());
System::Runtime::InteropServices::Marshal::FreeHGlobal(handle);

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 31 May 2001
Article Copyright 2001 by Nick Van den Abbeele
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid