从非托管 C++ 配置 .NET 库

GP.*_*GP. 1 c# c++ com configuration unmanaged

我有一个名为 Foo 的 C# .NET 库,它生成一个 Foo.dll 文件。它也可以通过 .config 文件进行配置,如下所示:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <section name="Foo.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
    </sectionGroup>
  </configSections>
  <applicationSettings>
    <Foo.Properties.Settings>
      <setting name="Server" serializeAs="String">
        <value>localhost</value>
      </setting>
    </Foo.Properties.Settings>
  </applicationSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
Run Code Online (Sandbox Code Playgroud)

我想要做的是从名为 Bar 的非托管 C++ 程序调用此 .NET 库中的函数,并能够从非托管 C++ 应用程序更改其配置。通过在 Foo 中将类/接口属性 ComVisible 设置为 true,我能够从 Bar 调用 Foo 函数。但是,我无法从非托管 C++ 应用程序更改 .config 文件值——服务器始终为 "localhost"

我正在尝试做什么——从非托管 C++ 配置 .NET 库——甚至可能吗?

csh*_*net 5

嗯,这是可能的,但工作量不小。从托管世界创建应用程序域和设置配置,然后在托管代码中创建一个小的“thunking”层来为非托管客户端包装它通常更容易。但由于这不是您所要求的,以下是您实现目标的方法:

首先,您需要导入 mscorelib:

#include <mscoree.h>
#import <mscorlib.tlb> raw_interfaces_only no_smart_pointers high_property_prefixes("_get","_put","_putref")
Run Code Online (Sandbox Code Playgroud)

然后绑定到运行时:

ICorRuntimeHost runtimeHost;
hr = CorBindToRuntimeEx(
    NULL,   //Retrieve latest version by default
    L"wks", //Request a WorkStation build of the CLR
    STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC, 
    CLSID_CorRuntimeHost,
    IID_ICorRuntimeHost,
    (void**)&runtimeHost
);
Run Code Online (Sandbox Code Playgroud)

现在启动 CLR 并创建一个 AppDomainSetup 实例。

hr = runtimeHost->Start();
IAppDomainSetup pSetup;
hr = runtimeHost->CreateDomainSetup(&pSetup);
Run Code Online (Sandbox Code Playgroud)

现在根据需要填写设置信息:

hr = pSetup->put_ApplicationBase(_bstr_t(thisFile));
hr = pSetup->put_ConfigurationFile(_bstr_t(configFile));
Run Code Online (Sandbox Code Playgroud)

最后创建域:

hr = __gRuntimeHost->CreateDomainEx(L"ISAPI.Net", pSetup, NULL, &pDomain);
Run Code Online (Sandbox Code Playgroud)

现在您可以选择实例化一个对象并调用方法:

hr = pDomain->CreateInstanceFrom(_bstr_t(assemblyFile), _bstr_t("Namespace.ClassName"), &pObjectHandle);
VARIANT vtUnwrapped;
hr = spObjectHandle->Unwrap(&vtUnwrapped);
IDispatch pDisp = vtUnwrapped.pdispVal;
DISPID dispid;
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
hr = pDisp->GetIDsOfNames (
        IID_NULL, 
        szMemberName,
        1,
        LOCALE_SYSTEM_DEFAULT,
        &dispid
    );

hr = pDisp->Invoke (
          dispid,
          IID_NULL,
          LOCALE_SYSTEM_DEFAULT,
          DISPATCH_METHOD,
          &dispparamsNoArgs,
          NULL,
          NULL,
          NULL
      );
Run Code Online (Sandbox Code Playgroud)

显然上面的代码片段是不完整的;但是,如果您对 C++ COM 足够熟练,上述内容应该为您提供足够的信息来解决它。您应该注意,这是我可以证明在 3.5 中继续工作的“旧”(1.x)托管接口,我不知道这些接口如何/是否在 4.0 上工作。从.Net 2.0 开始,引入了新的托管接口。我从来不需要 1.x 版本以外的任何东西,所以我从不费心升级任何托管代码。

另请参阅:托管概述