Я сделал код, очень похожий на https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0. Разница между ними заключается в том, что я загружаю исполняемый файл из памяти, а он делает это из файла.Другое отличие состоит в том, что он вызывает некоторый случайный метод, а я хочу вызвать метод Main.
Моя цель - быть чем-то вроде собственного загрузчика .NET Reactor.
Код отлично работает с консольными приложениями и WinForms.Единственная проблема заключается в том, что он не будет загружать приложения WPF и, более конкретно, в этой строке произойдет сбой: pMethodInfo->Invoke_3(v2, p2, &v);
с hr = 0x80131604 (COR_E_TARGETINVOCATION)
.Проект WPF был создан в VS 2019, и архитектура таргетинга (x86) и .NET Framework (4.0) совпадают с загрузчиками.
На рисунке ниже представлена основная функция приложения WPF.Я буквально не знаю, почему это может быть причиной TargetInvocation.
![enter image description here](https://i.stack.imgur.com/TZ272.png)
Код здесь:
int LoadAssembly(LPVOID bytes, DWORD size)
{
HRESULT hr;
ICLRMetaHost* pMetaHost = NULL;
ICLRRuntimeInfo* pRuntimeInfo = NULL;
ICorRuntimeHost* pCorRuntimeHost = NULL;
IUnknownPtr UnkAppDomain = NULL;
_AppDomainPtr AppDomain = NULL;
_AssemblyPtr pAssembly = NULL;
_MethodInfoPtr pMethodInfo = NULL;
SAFEARRAY* params = NULL;
SAFEARRAY* sa = NULL;
bool bSuccess = false;
while (!bSuccess)
{
// STAThread
CoInitialize(NULL);
// Load and start the .NET runtime.
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if (FAILED(hr))
break;
// Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
// supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
if (FAILED(hr))
break;
// Check if the specified runtime can be loaded into the process. This
// method will take into account other runtimes that may already be
// loaded into the process and set pbLoadable to TRUE if this runtime can
// be loaded in an in-process side-by-side fashion.
BOOL fLoadable;
hr = pRuntimeInfo->IsLoadable(&fLoadable);
if (FAILED(hr) || !fLoadable)
break;
// Load the CLR into the current process and return a runtime interface
// pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
// interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost
// interface that was provided in .NET v1.x, and is compatible with all
// .NET Frameworks.
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(&pCorRuntimeHost));
if (FAILED(hr))
break;
// Start the CLR
hr = pCorRuntimeHost->Start();
if (FAILED(hr))
break;
// Get a pointer to the default AppDomain in the CLR
hr = pCorRuntimeHost->GetDefaultDomain(&UnkAppDomain);
if (FAILED(hr))
break;
hr = UnkAppDomain->QueryInterface(IID_PPV_ARGS(&AppDomain));
if (FAILED(hr))
break;
SAFEARRAYBOUND sab[1];
sab[0].lLbound = 0;
sab[0].cElements = size;
sa = SafeArrayCreate(VT_UI1, 1, sab);
if (!sa)
break;
void* sa_raw;
hr = SafeArrayAccessData(sa, &sa_raw);
if (FAILED(hr))
break;
memcpy(sa_raw, bytes, size);
SafeArrayUnaccessData(sa);
hr = AppDomain->Load_3(sa, &pAssembly);
if (FAILED(hr))
break;
hr = pAssembly->get_EntryPoint(&pMethodInfo);
if (FAILED(hr))
break;
SAFEARRAY* mtd_params;
hr = pMethodInfo->GetParameters(&mtd_params);
if (FAILED(hr))
break;
SAFEARRAY* p2;
if (mtd_params->rgsabound->cElements != 0)
{
INT argc;
WCHAR** _argv = CommandLineToArgvW(GetCommandLineW(), &argc);
params = SafeArrayCreateVector(VT_BSTR, 0, argc);
if (!params)
break;
for (int i = 0; i < argc; i++)
{
long lIndex = i;
hr = SafeArrayPutElement(params, &lIndex, SysAllocString(_argv[i]));
if (FAILED(hr))
break;
}
p2 = SafeArrayCreateVector(VT_VARIANT, 0, 1);
LONG l2 = 0;
VARIANT vp2;
vp2.vt = VT_ARRAY | VT_BSTR;
vp2.parray = params;
hr = SafeArrayPutElement(p2, &l2, &vp2);
if (FAILED(hr))
break;
}
else
{
SAFEARRAYBOUND sabc[1];
sabc[0].cElements = 0;
sabc[0].lLbound = 0;
p2 = SafeArrayCreate(VT_VARIANT, 1, sabc);
}
VARIANT v;
VARIANT v2;
VariantInit(&v);
VariantInit(&v2);
hr = pMethodInfo->Invoke_3(v2, p2, &v);
VariantClear(&v);
VariantClear(&v2);
if (FAILED(hr))
break;
bSuccess = true;
}
if (pMetaHost)
pMetaHost->Release();
if (pRuntimeInfo)
pRuntimeInfo->Release();
if (pCorRuntimeHost)
{
// Please note that after a call to Stop, the CLR cannot be
// reinitialized into the same process. This step is usually not
// necessary. You can leave the .NET runtime loaded in your process.
pCorRuntimeHost->Stop();
pCorRuntimeHost->Release();
}
if (sa)
SafeArrayDestroy(sa);
if (params)
SafeArrayDestroy(params);
return hr;
}
Редактировать: Я пытался загрузить приложение WPF из C #, используя System.Reflection
, но это не сработало.Есть код, который почти такой же, как мой C ++, за исключением того, что C ++ исправляет аргументы:
byte[] data = File.ReadAllBytes("Test2.exe");
Assembly assembly = Assembly.Load(data);
assembly.EntryPoint.Invoke(null, new object[0]);
Это та же ошибка, которую я получил в коде C ++, за исключением того, что я не совсем уверен, какпроверьте наличие внутренних исключений.
![enter image description here](https://i.stack.imgur.com/7rKYq.png)
Я провел небольшое исследование и понял, что должен изменить ResourceAssembly
, как они сделали здесь: Загрузка приложения WPF из памяти .Новый код выглядел так:
byte[] data = File.ReadAllBytes("Test2.exe");
Assembly assembly = Assembly.Load(data);
Type type = typeof(Application);
FieldInfo field = type.GetField("_resourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, assembly);
Type helper = typeof(BaseUriHelper);
PropertyInfo property = helper.GetProperty("ResourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
property.SetValue(null, assembly, null);
assembly.EntryPoint.Invoke(null, new object[0]);
В результате код загружал приложение WPF.Теперь вопрос в том, как я могу сделать это в своем коде C ++?Похоже, никто не занимался этим вопросом.Единственное решение, которое я нашел, было загрузить файл с диска, а не из памяти - http://sbytestream.pythonanywhere.com/blog/clr-hosting.