I'm posting here relevant code snippets.
InvokerSimple
DWORD WINAPI DoSomething( LPVOID lpParam )
{
HMODULE h=LoadLibraryA("PathTo\\CSProxyInterface.dll"); //this is Proxy!
initType init=(initType)GetProcAddress(h,"init");
releaseType release=(releaseType)GetProcAddress(h,"release");
handleEventType handleEvent=(handleEventType)GetProcAddress(h,"handleEvent");
init(0,NULL);
int r;
for (int i=0;i<50;i++)
{
r=handleEvent(0,0,NULL); //causes just Assembly.SomeFunction invoke
r=handleEvent(0,0,NULL); //causes just Assembly.SomeFunction invoke
r=handleEvent(0,0,NULL); //causes just Assembly.SomeFunction invoke
r=handleEvent(0,4,NULL); //causes Assembly reload (inside the Proxy)
}
release(0);
FreeLibrary(h);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
bool Threaded=true; //tried both Threaded and unThreaded. They work fine!
if (Threaded)
{
DWORD threadID;
HANDLE threadHNDL;
threadHNDL=CreateThread(NULL,0,&DoSomething,NULL,0, &threadID);
WaitForSingleObject(threadHNDL,INFINITE);
}
else
{
DoSomething(NULL);
}
return 0;
}
CSProxyInterface (the Proxy)
stdafx.h
int Safe_init(void);
int Safe_release(void);
extern "C" __declspec(dllexport) int __cdecl init(int teamId, const void* callback);
extern "C" __declspec(dllexport) int __cdecl release(int teamId);
extern "C" __declspec(dllexport) int __cdecl handleEvent(int teamId, int topic, const void* data);
stdafx.cpp
#include "stdafx.h"
#include "WrapperClass.h"
#include <vcclr.h>
using namespace System;
using namespace System::Diagnostics;
using namespace System::Threading;
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
#define CSMAXPATHLEN 8192
static gcroot<WrapperClass^> wc;
static char* my_filename="PathTo\\Assembly.dll";
int __cdecl init( int teamId, const void* callback )
{
return Safe_init();
}
int Safe_init( void )
{
Safe_release();
wc=gcnew WrapperClass(gcnew String(my_filename));
return 0;
}
int __cdecl release( int teamId )
{
return Safe_release();
}
int Safe_release( void )
{
if (static_cast<WrapperClass^>(wc)!=nullptr)
{
delete wc;
wc=nullptr;
}
return 0;
}
int __cdecl handleEvent( int teamId, int topic, const void* data )
{
if (topic!=4)
{
int r=wc->Do(topic);
return r;
}
else
{
Safe_init();
return 0;
}
}
WrapperClass.h
#pragma once
using namespace System;
using namespace System::Reflection;
#include "SomeInterface.h"
ref class WrapperClass
{
private:
ResolveEventHandler^ re;
Assembly^ assembly;
static Assembly^ assembly_interface;
SomeInterface^ instance;
private:
Assembly^ LoadAssembly(String^ filename_dll);
static Assembly^ MyResolveEventHandler(Object^ sender, ResolveEventArgs^ args);
static bool MyInterfaceFilter(Type^ typeObj, Object^ criteriaObj);
public:
int Do(int i);
public:
WrapperClass(String^ dll_path);
!WrapperClass(void);
~WrapperClass(void) {this->!WrapperClass();};
};
WrapperClass.cpp
#include "StdAfx.h"
#include "WrapperClass.h"
WrapperClass::WrapperClass(String^ dll_path)
{
re=gcnew ResolveEventHandler(&MyResolveEventHandler);
AppDomain::CurrentDomain->AssemblyResolve +=re;
assembly=LoadAssembly(dll_path);
array<System::Type^>^ types;
try
{
types=assembly->GetExportedTypes();
}
catch (Exception^ e)
{
throw e;
}
for (int i=0;i<types->Length;i++)
{
Type^ type=types[i];
if ((type->IsClass))
{
String^ InterfaceName = "SomeInterface";
TypeFilter^ myFilter = gcnew TypeFilter(MyInterfaceFilter);
array<Type^>^ myInterfaces = type->FindInterfaces(myFilter, InterfaceName);
if (myInterfaces->Length==1) //founded the correct interface
{
Object^ tmpObj=Activator::CreateInstance(type);
instance = safe_cast<SomeInterface^>(tmpObj);
}
}
}
}
bool WrapperClass::MyInterfaceFilter(Type^ typeObj, Object^ criteriaObj)
{
return (typeObj->ToString() == criteriaObj->ToString());
}
WrapperClass::!WrapperClass(void)
{
AppDomain::CurrentDomain->AssemblyResolve -=re;
instance=nullptr;
assembly=nullptr;
}
int WrapperClass::Do( int i )
{
return instance->Do();
}
Assembly^ WrapperClass::MyResolveEventHandler(Object^ sender, ResolveEventArgs^ args)
{
Assembly^ return_=nullptr;
array<Assembly^>^ assemblies=AppDomain::CurrentDomain->GetAssemblies();
for (int i=0;i<assemblies->Length;i++)
{
if (args->Name==assemblies[i]->FullName)
{
return_=assemblies[i];
break;
}
}
return return_;
}
Assembly^ WrapperClass::LoadAssembly(String^ filename_dll)
{
Assembly^ return_=nullptr;
String^ filename_pdb=IO::Path::ChangeExtension(filename_dll, ".pdb");
if (IO::File::Exists(filename_dll))
{
IO::FileStream^ dll_stream = gcnew IO::FileStream(filename_dll, IO::FileMode::Open, IO::FileAccess::Read);
IO::BinaryReader^ dll_stream_bytereader = gcnew IO::BinaryReader(dll_stream);
array<System::Byte>^ dll_stream_bytes = dll_stream_bytereader->ReadBytes((System::Int32)dll_stream->Length);
dll_stream_bytereader->Close();
dll_stream->Close();
if (IO::File::Exists(filename_pdb))
{
IO::FileStream^ pdb_stream = gcnew IO::FileStream(filename_pdb, IO::FileMode::Open, IO::FileAccess::Read);
IO::BinaryReader^ pdb_stream_bytereader = gcnew IO::BinaryReader(pdb_stream);
array<System::Byte>^ pdb_stream_bytes = pdb_stream_bytereader->ReadBytes((System::Int32)pdb_stream->Length);
pdb_stream_bytereader->Close();
pdb_stream->Close();
try
{
//array<Assembly^>^ asses1=AppDomain::CurrentDomain->GetAssemblies();
return_=Assembly::Load(dll_stream_bytes,pdb_stream_bytes);
//array<Assembly^>^ asses2=AppDomain::CurrentDomain->GetAssemblies();
}
catch (Exception^ e)
{
//array<Assembly^>^ asses3=AppDomain::CurrentDomain->GetAssemblies();
throw e;
}
}
else
{
try
{
return_=Assembly::Load(dll_stream_bytes);
}
catch (Exception^ e)
{
throw e;
}
}
}
return return_;
}
And finally Assembly.dll
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Assembly
{
public class Class1:SomeInterface
{
private int i;
#region SomeInterface Members
public int Do()
{
return i--;
}
#endregion
}
}
If one manage to compile all 3 project it would get it work. It's simple invoker scenario.
Also I have the open-source game which do exactly the same as in Simple Invoker. But after reload is requested (handleEvent(0, 4, NULL) is invoked) I'm getting an error inside the Assembly::Load(Byte[],Byte[]) <-- you can find it in proxy code
The exception is shown below:
"Could not load file or assembly '4096 bytes loaded from CSProxyInterface, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Exception from HRESULT: 0x800703E6"
InnerException:
0x1a158a78 { "Could not load file or assembly 'sorttbls.nlp' or one of its dependencies. Exception from HRESULT: 0x800703E6"}
Just to be as more precise as possible this code does work with simple Invoker scenario and does work once(first Init) in complex one.
Ready to answer you questions and provide all sources, project files, etc!!!
VBR, Ivan