Win32 Interop
 | Mixing C++/cli and C# into a single assembly |
 | Interior pointers for fast array iteration (non-verifiable) |
 | __clrcall improves efficiency of CLS calling virtual functions in native
code |
The System::Marshal
class provides methods from converting data between unmanaged and managed types.
The following code demonstrates some of the aspects of interop programming in
C++/cli.
// TestInterop.cpp : main project file.
#include "stdafx.h"
#include "string.h"
#include <vector>
#include <list>
using namespace std;
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Diagnostics;
typedef void (*CALLBACK)();
public class NativeServer {
private:
list<CALLBACK> lPointers;
public:
void Register(CALLBACK cb){
lPointers.push_back(cb);
}
void UnRegister(CALLBACK cb) {
lPointers.remove(cb);
}
void Broadcast() {
std::list<CALLBACK>::iterator iter;
iter= lPointers.begin();
while (iter != lPointers.end()) {
(static_cast<CALLBACK>(*iter))();
iter++;
}
}
void Clear() {
lPointers.clear();
}
};
public class Native {
public:
virtual int __clrcall Neg(int* pi) {
return -(*pi);
}
bool Copy(char* out,int size,const char * in) {
strcpy_s(out,size,in);
bool isNullTerminated= false;
if (in[size-1] == '\0')
isNullTerminated= true;
return isNullTerminated;
}
};
public class TestNotify {
public:
static void Notify() {
Console::WriteLine("Notify");
}
};
public ref class Interop {
public:
void Notify() {
Console::WriteLine("Notify");
}
int SumManaged(array<int>^ ai) {
int sum=0;
for(int i=0; i<ai->Length; i++) {
sum+= ai[i];
}
return sum;
}
int InteropClrCall(int i) {
pin_ptr<int> ppi= &i;
Native n;
return n.Neg(ppi);
}
int SumInteriorPointer(array<int>^ ai) {
interior_ptr<int> ipi= &ai[0];
int sum=0;
for(int i=0; i<ai->Length; i++) {
sum+= ipi[i];
}
return sum;
}
// exercise String to Ansi Ansi to String
String^ Echo(String^ in) {
Native n;
IntPtr ip(0);
String^ out=L"";
char* str= 0;
try {
str= new char[in->Length+1]; // null terminator
ip = Marshal::StringToHGlobalAnsi(in); // create char[] on unmanaged heap
n.Copy(str,in->Length+1,static_cast<const char*>(ip.ToPointer()));
out= Marshal::PtrToStringAnsi(static_cast<IntPtr>(str));
}
catch(...) {
out= L"exception thrown";
}
finally {
if (ip != IntPtr(0)) {
Marshal::FreeHGlobal(ip); // release char[] on unmanaged heap
}
if(str) {
delete [] str; // release char[] on unmanaged heap
}
}
return out;
}
// exercise marshal unmanaged array int to managed array int
// where pIn is from native dll
// ASSERT int [] of length size
array<int>^ IntArrayToManagedArray(const int* pIn, const int size) {
Debug::Assert(size>0,"Invalid size");
Debug::Assert(pIn != 0,"Invalid pointer");
array<int>^ pOut= nullptr;
if (pIn) {
IntPtr ipIn(const_cast<int*>(pIn));
pOut= gcnew array<int>(size);
Marshal::Copy(ipIn,pOut,0,size);
}
return pOut;
}
// exercise marshal managed array int to unmanaged array int
// ASSERT length pOut == size
// ASSERT managed array length same as unmanaged array length
void ManagedArrayToIntArray(array<int>^ pIn, const int size, int* pOut) {
Debug::Assert(size>0,"Invalid size");
Debug::Assert(pIn != nullptr,"Invalid pointer");
Debug::Assert(pOut != 0, "Invalid pointer");
Debug::Assert(pIn->Length == size, "Invalid size");
if ((pIn!= nullptr) &&(pIn->Length == size)){
IntPtr ipOut(pOut);
Marshal::Copy(pIn,0,ipOut,size);
}
}
};
public delegate void CallbackDelegate();
int main(array<System::String ^> ^args)
{
Interop^ interop= gcnew Interop();
array<int>^ ai= gcnew array<int>(10){1,2,3,4,5,6,7,8,9,10};
Console::WriteLine(interop->SumManaged(ai));
Console::WriteLine(interop->SumInteriorPointer(ai));
Console::WriteLine(interop->InteropClrCall(1));
Console::WriteLine(interop->Echo(L"Hello"));
int unManagedArrayInt[2]= {1,2};
array<int>^ managedArrayInt= interop->IntArrayToManagedArray(&unManagedArrayInt[0],2);
for each(int i in managedArrayInt) {
Console::Write(i+" ");
}
unManagedArrayInt[0]= 0;
unManagedArrayInt[1]= 0;
interop->ManagedArrayToIntArray(managedArrayInt,2,unManagedArrayInt);
for (int i=0; i<2; i++){
Console::Write(unManagedArrayInt[i]+" ");
}
// C style callback
NativeServer ns;
CALLBACK cb1= &TestNotify::Notify;
ns.Register(cb1);
ns.Broadcast();
ns.UnRegister(cb1);
// managed C style callback
CallbackDelegate^ cd= nullptr;
GCHandle gch;
CALLBACK cb2= 0;
try {
cd = gcnew CallbackDelegate(interop,&Interop::Notify);
gch = GCHandle::Alloc(cd);
IntPtr ip = Marshal::GetFunctionPointerForDelegate(cd);
cb2 = static_cast<CALLBACK>(ip.ToPointer());
ns.Register(cb2);
//GC::Collect(); // delegate cannot be collected
ns.Broadcast();
}
finally {
ns.UnRegister(cb2); // transaction safe
if (gch.IsAllocated) {
gch.Free(); // delegate can now be collected
}
}
ns.Broadcast(); // may be invalid if delegate garbage collected
Console::ReadLine();
return 0;
}