struct _FooInterface : IUnknown
{
virtual
HRESULT __stdcall HelloWorld(LPWSTR name, LPWSTR* result, void* ptr) = 0;
};
extern "C"
{
void __cdecl test_func(int);
}
void main()
{
Microsoft::WRL::ComPtr<ICLRMetaHost> pMetaHost;
Microsoft::WRL::ComPtr<ICLRRuntimeInfo> pRuntimeInfo;
HRESULT hr;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(pMetaHost.GetAddressOf()));
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(pRuntimeInfo.GetAddressOf()));
hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(pRuntimeHost.GetAddressOf()));
SampleHostControl* hostControl = new SampleHostControl();
hr = pRuntimeHost->SetHostControl((IHostControl *)hostControl);
ICLRControl* pCLRControl = nullptr;
hr = pRuntimeHost->GetCLRControl(&pCLRControl);
LPCWSTR assemblyName = L"mesh_managed";
LPCWSTR appDomainManagerTypename = L"mesh_managed.CustomAppDomainManager";
hr = pCLRControl->SetAppDomainManagerType(assemblyName, appDomainManagerTypename);
hr = pRuntimeHost->Start();
LPWSTR text;
_FooInterface* appDomainManager = hostControl->GetFooInterface();
hr = appDomainManager->HelloWorld(L"Player One", &text, &test_func);
hr = pRuntimeHost->Stop();
}
For reference the code of host control class is :
class SampleHostControl : IHostControl
{
public:
SampleHostControl()
{
m_refCount = 0;
m_defaultDomainManager = NULL;
}
virtual ~SampleHostControl()
{
if (m_defaultDomainManager != NULL)
{
m_defaultDomainManager->Release();
}
}
HRESULT __stdcall SampleHostControl::GetHostManager(REFIID id, void **ppHostManager)
{
*ppHostManager = NULL;
return E_NOINTERFACE;
}
HRESULT __stdcall SampleHostControl::SetAppDomainManager(DWORD dwAppDomainID, IUnknown *pUnkAppDomainManager)
{
HRESULT hr = S_OK;
hr = pUnkAppDomainManager->QueryInterface(__uuidof(_FooInterface), (PVOID*)&m_defaultDomainManager);
return hr;
}
_FooInterface* GetFooInterface()
{
if (m_defaultDomainManager)
{
m_defaultDomainManager->AddRef();
}
return m_defaultDomainManager;
}
HRESULT __stdcall QueryInterface(const IID &iid, void **ppv)
{
if (!ppv) return E_POINTER;
*ppv = this;
AddRef();
return S_OK;
}
ULONG __stdcall AddRef()
{
return InterlockedIncrement(&m_refCount);
}
ULONG __stdcall Release()
{
if (InterlockedDecrement(&m_refCount) == 0)
{
delete this;
return 0;
}
return m_refCount;
}
private:
long m_refCount;
_FooInterface* m_defaultDomainManager;
};
(took it from a blog post)In my C# code I declare the following interface that mirror the _FooInterface one :
[ComImport, Guid("A15DDC0D-53EF-4776-8DA2-E87399C6654D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface Interface1
{
[return: MarshalAs(UnmanagedType.LPWStr)]
string HelloWorld([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr test_function);
}
Then I'm implementing the appDomain and the helloWorld function as :namespace mesh_managed
{
public sealed class CustomAppDomainManager : AppDomainManager, Interface1
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe public delegate void test_function_ptr(int i);
public CustomAppDomainManager()
{
}
public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
{
this.InitializationFlags = AppDomainManagerInitializationOptions.RegisterWithHost;
}
[STAThread]
public string HelloWorld(string name, IntPtr ptr)
{
var test_function = Marshal.GetDelegateForFunctionPointer<test_function_ptr>(ptr);
test_function(0);
return "Hello " + name;
}
}
}
(it's just a test function)Unfortunatly the test_function(0) call doesn't work here in the assembly : an exception "An unhandled exception of type 'System.AccessViolationException' occurred in mesh_managed.dll" is thrown. If I remove the test_function(0) call everything seem to work as expected, the string is correctly return in the C++ side.
What am I doing wrong ? I'd like to expose an API to the CLR so that I can manipulate data with C#. There is no dll so I can't use DLLImport feature and I prefer to have C++ embed C# than the opposite.
Regards, Vincent.