Хост CLR не выполняет приложения WPF, но выполняет WinForms - PullRequest
2 голосов
/ 09 июня 2019

Я сделал код, очень похожий на 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

Код здесь:

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

Я провел небольшое исследование и понял, что должен изменить 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...