System::Runtime::InteropServices
Chapter 22/23 of the ‘Pro Visual C++/CLI and the .NET 3.5 Platform’ book is an excellent resource for understanding P/Invoke (and an excellent VC++ book generally).
Used when making calls out of the .NET managed environment to unmanaged .DLLs. P/Invoke finds the DLL, loads it into memory, marshals its arguments (converts from managed to native) so the DLL can understand the call, makes the call to the DLL function and than marshals the return value (converting from native to managed).
Good Resources
http://msdn.microsoft.com/en-us/library/aa719104%28VS.71%29.aspx
http://msdn.microsoft.com/en-us/library/eyzhw3s8.aspx
DllImport
(Same as DllImportAttribute)
DllImport provides a simple way to make calls to native code from a managed application. Copy the .dll file into your project exe directory.
DllImport(L"MyDllName.dll")]
//Create the function prototype here
Reference Values
Some functions might take arguments that return a value. Just use the ‘%’ as you normally would, but make sure you are not passing a handle (e.g. int %value is fine, but String ^%text is not).
Keywords such as ‘out’ don’t work for managed C – the % character is how you tell the compiler a value is coming returned.
When porting VB code look for ByRef. Usually values are passed as ByVal, but if ByRef is used then it means this is a returned variable.
Data Marshalling
Usually when using P/Invoke you don’t have to worry about marshalling as in most cases the managed and unmanaged formats of data types are the same. However there is nothing stopping you specifically defining how parameters are to be marshalled using MarshalAsAttribute in the function prototype you create for the DLL import.
Variables
bool – Bool (Win32 BOOL type)
char – Char (1 byte signed integer)
short – Int16 (2 byte signed integer)
long – Int32 (4 byte signed integer)
__int64 – Int64 (8 byte signed integer)
unsigned char – Byte (1 byte unsigned integer)
unsigned short – UInt16 (2 byte unsigned integer)
unsigned int or unsigned long – UInt32 (4 byte unsigned integer)
unsigned __int64 – UInt64 (8 byte unsigned integer)
Strings
If you use String^ when defining the function prototype then this correctly maps to null terminated wchar_t arrays – i.e. you can use String^. However as an example here’s how you would do it if you used System::String in the function prototype, effectively doing the data marshalling yourself:
String ^MyString = L"Hello";
pin_ptrmy_string = &(MyString->ToCharArray()[0]);
SomeDllFunction(my_string);
Some Examples
Forcing our application form to the foreground
Outside of function / in header file:
[System::Runtime::InteropServices::DllImport(L"user32.dll")]
static System::IntPtr SetForegroundWindow(System::IntPtr hWnd);
In function:
SetForegroundWindow(this->Handle); //Can test return bool value if we want
Misc
[System::Runtime::InteropServices::DllImport(L"ivbind.dll")]
//int IvBindRegisterEvents (const char *pszIpAddress);
static System::Int32 IvBindRegisterEvents (String ^pszIpAddress);
[System::Runtime::InteropServices::DllImport(L"ivbind.dll")]
//int IvBindSendEvent (const char *pszIpAddress, int nPort, const FILETIME *pTime, int nEventNum, const char *pData, int nSize);
static System::Int32 IvBindSendEvent (String ^pszIpAddress, System::Int32 nPort, System::DateTime ^pTime,
System::Int32 nEventNum, String ^pData, System::Int32 nSize);
MSDN C++ Examples
public:
[DllImport(S"KERNEL32.DLL", EntryPoint=S"MoveFileW", SetLastError=true,
CharSet=CharSet::Unicode, ExactSpelling=true,
CallingConvention=CallingConvention::StdCall)]
static bool MoveFile(String* src, String* dst);
[DllImport(L"winspool.Drv", EntryPoint="StartDocPrinterW", SetLastError=true, CharSet=CharSet::Unicode, ExactSpelling=true, CallingConvention=CallingConvention::StdCall)]
System::Boolean StartDocPrinter(
System::IntPtr hPrinter,
int level,
[MarshalAs(UnmanagedType::LPWStr)] String ^pDocName,
[MarshalAs(UnmanagedType::LPWStr)] String ^pOutputFile,
[MarshalAs(UnmanagedType::LPWStr)] String ^pDataType);
[DllImport(L"winmm.dll")]
static System::IntPtr timeSetEvent(
UInt32 uDelay,
UInt32 uResolution,
[MarshalAs(UnmanagedType::FunctionPtr)] MMTimerProc ^lpTimeProc,
UInt32 dwUser,
Int32 fuEvent
);