Generic Thunks and PInvoke Type Marshaling

So far, I have discussed P/Invoke metadata and thunks only from a performance point of view. If you call managed functions in a context that is not performance critical, you probably prefer convenience over performance. C++/CLI interoperability already provides a lot of convenience—you only need normal function declarations to call a managed function from native code. However, depending on the argument types of the target method, it is still possible that you have to write some code yourself to marshal managed types to native argument types manually.

In the following code sample, the managed class System:Environment is used to get the name of the user that executes the current thread. To pass the content of the managed string returned by Environment::UserName to a function like MessageBoxA, which expects a native null-terminated ANSI code string, the managed string must be marshaled first. Therefore, Marshal::StringToCoTaskMemAnsi is called. To clean up the native string returned by Marshal::StringToCoTaskMemAnsi, the helper function Marshal::FreeCoTaskMem is used:

// ManualMarshaling.cpp

// build with "CL /clr ManualMarshaling.cpp"

#include <windows.h>

#pragma comment(lib, "user32.lib")

using namespace System;

using namespace System::Runtime::InteropServices;

StringA strUserName = Environment::UserName;

IntPtr iptrUserName = Marshal::StringToCoTaskMemAnsi(strUserName); const char* szUserName = static_cast<const char*>(iptrUserName.ToPointer()); MessageBoxA(NULL, szUserName, "Current User", 0); Marshal::FreeCoTaskMem(iptrUserName);

Instead of writing explicit code to marshal managed string arguments to native strings passed to the target function, you can write a custom P/Invoke function that benefits from P/Invoke type marshaling:

// PInvokeMarshaling.cpp"

// build with "CL /clr PInvokeMarshaling.cpp"

#include <windows.h>

using namespace System;

using namespace System::Runtime::InteropServices;

namespace PInvoke {

[DllImport("user32.dll",

CharSet=CharSet::Ansi, // marshal StringA to LPCSTR

EntryPoint = "MessageBoxA", ExactSpelling = true)] UINT MessageBoxA(HWND, StringA, StringA, UINT);

StringA strUserName = Environment::UserName; PInvoke::MessageBoxA(NULL, strUserName, "Current User", 0);

Was this article helpful?

0 0

Post a comment