CLR主机不执行WPF应用程序,但执行WinForms

时间:2019-06-09 19:36:32

标签: c++ wpf clr

我编写的代码与https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0非常相似。两者之间的区别在于,我从内存中加载可执行文件,而他是从文件中加载可执行文件。另一个不同之处是,他在我要调用Main方法时调用了一些随机方法。

我的目标是成为.NET Reactor的本机加载器。

该代码可与Console Apps和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;
}

编辑: 我试图使用System.Reflection从C#加载WPF应用程序,但是它没有用。除了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

我做了一些研究,我意识到我必须像他们在这里一样更改ResourceAssemblyLoad WPF application from the memory。新代码如下所示:

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

0 个答案:

没有答案