从非托管 C++ 配置 .NET 库
Posted
技术标签:
【中文标题】从非托管 C++ 配置 .NET 库【英文标题】:Configuring .NET library from unmanaged C++ 【发布时间】:2011-01-20 19:39:41 【问题描述】:我有一个名为 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>
我想做的是从名为 Bar 的非托管 C++ 程序调用此 .NET 库中的函数,并能够从非托管 C++ 应用程序更改其配置。通过在 Foo 中将类/接口属性 ComVisible 设置为 true,我可以从 Bar 调用 Foo 函数。但是,我无法从非托管 C++ 应用程序更改 .config 文件的值 -- 服务器始终是“localhost”。
我正在尝试做的事情————甚至可能吗?
【问题讨论】:
【参考方案1】:嗯,这是可能的,但不是少量的工作。通常更容易从托管世界创建 appdomain 和设置配置,然后在托管代码中创建一个小的“thunking”层来为非托管客户端包装它。但由于这不是您所要求的,因此您可以通过以下方式实现目标:
首先您需要导入 mscorelib:
#include <mscoree.h>
#import <mscorlib.tlb> raw_interfaces_only no_smart_pointers high_property_prefixes("_get","_put","_putref")
然后绑定到运行时:
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
);
现在启动 CLR 并创建一个 AppDomainSetup 实例。
hr = runtimeHost->Start();
IAppDomainSetup pSetup;
hr = runtimeHost->CreateDomainSetup(&pSetup);
现在根据需要填写设置信息:
hr = pSetup->put_ApplicationBase(_bstr_t(thisFile));
hr = pSetup->put_ConfigurationFile(_bstr_t(configFile));
最后创建域:
hr = __gRuntimeHost->CreateDomainEx(L"ISAPI.Net", pSetup, NULL, &pDomain);
现在您可以选择实例化一个对象并调用方法:
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
);
显然上面的代码片段是不完整的;但是,如果您对 C++ COM 足够熟练,以上内容应该为您提供足够的信息来解决问题。您应该注意,这是我可以证明在 3.5 中继续工作的“旧”(1.x)托管接口,我不知道这些接口如何/是否在 4.0 上工作。从.Net 2.0 new hosting interfaces 开始介绍。除了 1.x 版本之外,我从不需要任何东西,所以我从不费心升级任何托管代码。
另见:Hosting Overview
【讨论】:
@csharptest.net:谢谢,目前正在尝试这个,并且还在考虑thunking 解决方案。我一定会让你知道我最终会采用哪种解决方案。 我都做过,我现在告诉你我更喜欢托管代码中的thunking layer;但是,两者都是可行的解决方案。 非常有趣。恐怕我不熟悉创建这个托管的 thunking 层。如果我创建一个包含托管配置的 C++/CLI 层(然后从非托管 C++ 调用该 C++/CLI 层上的方法),是否会被认为是 thunking,或者我只是错了这个? @GP 您目前如何通过 COM InterOp 或其他方式从 C++ 激活托管代码? 通过 COM 互操作。你给了我一个想法。我目前正在将您上面的代码转换为 C# 方法,该方法将共享 C# 程序集加载到临时 AppDomain 中,提供编辑后的配置文件等。这将是从非托管 C++ 调用的那个方法。我希望这行得通。【参考方案2】:.Net 配置系统为 EXE 加载 .config
文件; DLL 没有配置文件。
你需要找到不同的方法。
编辑:您还可以创建HostApp.exe.config
形式的配置文件,其中HostApp
是您的本地EXE 的完整路径。
【讨论】:
相反,dll 确实有配置文件。例如,如果我在上面给出的示例中的 .NET 库的使用者从非托管 C++ exe 更改为托管 C# exe,则可以通过复制 .config 值来更新共享库 Foo.dll 的服务器Foo 进入 Bar.exe.config。因此,我最初的问题是:如果 .NET 使用者可以更新 .NET dll 的配置文件,那么非托管 C++ 使用者是否能够更新 .NET dll 的配置文件? @GP:这是一个 EXE 的配置文件。 DLL 本身没有配置文件。 .Net 框架将从不查看Foo.dll.config
。
我明白你想说什么,虽然我同意共享库 dll 的配置文件不会加载到 AppDomain 中,但这并不是我在评论中所建议的。我要说的是,如果您将 C# Foo 的配置移植(重新:复制和粘贴)到 C# Bar 的 exe.config 文件中,那么这使得 C# Bar 能够在运行时更新 C# Foo 的配置值。 (我会留给你自己测试。)所以,如果可以通过 C# Bar 配置 C# Foo 的配置,非托管 C++ Bar 也可以吗?
我很困惑你在上面的编辑中所说的“完整路径”是什么意思,看到文件名不能包含路径字符。无论如何,我认为它是 HostApp 的名称,但我不敢说您在上面编辑中的建议在非托管 C++ 主机调用 C# .NET 库的情况下不起作用。服务器仍然是本地主机。以上是关于从非托管 C++ 配置 .NET 库的主要内容,如果未能解决你的问题,请参考以下文章