【问题标题】:C# COM DLL with gcServer = true and gcAllowVeryLargeObjects = trueC# COM DLL 与 gcServer = true 和 gcAllowVeryLargeObjects = true
【发布时间】:2017-07-01 05:03:22
【问题描述】:

假设我已经

  • 一个名为 managed.dll 的 C# DLL,它是 COM 可见的。
  • 一个名为magaged.exe 的C# EXE,它使用managed.dll 并有一个名为managed.exe.config 的app.config。
  • 一个名为 unmanaged.exe 的 C++ EXE,它通过 COM 调用 managed.dll,它与 C# EXE 具有相同的 app.config,但在本例中称为 unmanaged.exe.config

managed.dll 具有以下两个测试属性:

public bool IsServerGC
{
    get { return System.Runtime.GCSettings.IsServerGC; }
}

public bool AreVeryLargeObjectsAllowed
{
    get
    {
        try
        {
            long l = 20000;
            double[,] d = new double[l, l];
            return l * l == d.LongLength;
        }
        catch { return false; }
    }
}

app.config 对于两个 EXE 看起来都是这样的:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
    <!--<gcConcurrent enabled="false"/>-->
    <gcServer enabled="true"/>
  </runtime>
</configuration>

对于managed.exe,一切都按预期工作。但是对于unmanaged.exe&lt;gcServer enabled="true"/&gt; 设置被忽略。我不明白为什么?

我可以看到 unmanaged.exe.config 在创建 COM 对象的第一个实例时被加载。它也很好用,通过更改 &lt;gcAllowVeryLargeObjects enabled="true" /&gt; 设置进行了测试。

我使用的是 Visual Studio 2013、Windows 7(64 位)和 .NET 4.6.1。一切都是为 x64 编译的。

知道为什么在 COM 上使用 managed.dll 时会忽略 &lt;gcServer enabled="true"/&gt; 设置吗?

问候 沃尔米希

【问题讨论】:

  • 不为人知的事实是 [ComVisible] 服务器仍然使用 .config 文件。但它必须命名为client.exe.config 并复制到与client.exe 相同的目录中。相当不愉快,因为您并不总是控制客户端应用程序,但它比编写自己的 CLR 主机更容易维护,因此您可以在 CLR 开始执行代码之前对其进行配置。
  • @HansPassant,即使 client.exe 不是托管 exe,是否会加载 client.exe.config 文件?是否在进程中加载​​托管 dll 后立即加载?
  • @HansPassant:我试过了。但它似乎不起作用。在我的 C# DLL 中有一个方法调用 System.Runtime.GCSettings.IsServerGC 来检查垃圾收集模式。 Managed.exe 和 Managed.exe.config 正在工作。 Unmanaged.exe 和 Unmanaged.exe.config 不起作用。
  • 是的,如果 client.exe 不受管理,它可以正常工作。他们很少。 “不起作用”并不能帮助我帮助你。名称和位置很重要。当宿主应用程序在 32 位模式下运行时,您无法获取大对象。 SysInternals 的 Process Monitor 可能会带您到某个地方,假设它仍然在您的计算机上正常工作,您应该会看到 CLR 正在搜索 .config 文件。
  • @HansPassant:我现在使用了 SysInternals 进程监视器,我可以看到 Unmanaged.exe.config 在 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\ 之前加载机器配置。我的非托管运行在 64 位模式下。但是在我的 C# DLL 中调用 System.Runtime.GCSettings.IsServerGC 的方法仍然返回 false。我的配置文件如下所示: runtime> 还有其他想法吗?

标签: c# .net visual-studio garbage-collection com-interop


【解决方案1】:

我找到了两种不同的解决方法:

解决方法 1(在 COM 互操作之前托管 CLR)

我必须更改以下内容:

  • 名为 unmanaged.exe 的 C++ EXE 必须先托管 CLR 并设置启动标志,然后才能通过 COM 调用 managed.dll

unmanaged.exe的源文件是这样的:

#include "stdafx.h"
#include <metahost.h>
#include <mscoree.h>
#pragma comment(lib, "mscoree.lib")
#import "..\managed\bin\Release\managed.tlb"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    // Init .NET
    ICLRMetaHost    *pMetaHost = nullptr;
    ICLRRuntimeInfo *pRuntimeInfo = nullptr;
    HRESULT hr;
    hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
    hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
    hr = pRuntimeInfo->SetDefaultStartupFlags(STARTUP_SERVER_GC, NULL);
    // Init COM
    hr = CoInitialize(NULL);
    IManagedTestClassPtr pManagedTestClass(__uuidof(ManagedTestClass));
    // and so on ...

    // End COM
    CoUninitialize();
    // END .NET
    if (pMetaHost)
    {
        pMetaHost->Release();
        pMetaHost = NULL;
    }
    if (pRuntimeInfo)
    {
        pRuntimeInfo->Release();
        pRuntimeInfo = NULL;
    }
    return 0;
}

这是我个人首选的解决方法,因为不需要额外的 helper.dll。

解决方法 2(在 COM 互操作之前使用 C++/CLI DLL)

我必须更改以下内容:

  • 添加一个名为helper.dll 的C++/CLI DLL,其中有一个名为helper.dll.config 的app.config。 app.config 与 C++ EXE 相同。
  • 名为 unmanaged.exe 的 C++ EXE 必须先从 C++/CLI helper.dll 调用一个函数,然后才能通过 COM 调用 managed.dll

helper.dll 有以下头文件:

#pragma once
#ifdef TEST_HELPER_EXPORTS
#define DLLAPI  __declspec(dllexport)
#else
#define DLLAPI  __declspec(dllimport)
#pragma comment (lib, "..\\x64\\Release\\helper.lib") // if importing, link also
#endif
DLLAPI int IsServerGC();

以及以下源文件:

#include "stdafx.h"
#define TEST_HELPER_EXPORTS
#include "helper.h"
int IsServerGC()
{
    return System::Runtime::GCSettings::IsServerGC ? 1 : 0;
}

unmanaged.exe的源文件是这样的:

#include "stdafx.h"
#include "..\helper\helper.h"
#import "..\managed\bin\Release\managed.tlb"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    int isServerGC = IsServerGC(); // Init .NET with the C++/CLI helper.dll
    HRESULT hr = CoInitialize(NULL); // Init COM
    IManagedTestClassPtr pManagedTestClass(__uuidof(ManagedTestClass));
    // and so on ...
}

如果helper.dll.config 文件丢失,&lt;gcServer enabled="true"/&gt; 设置将再次被忽略。其他设置,例如&lt;gcAllowVeryLargeObjects enabled="true" /&gt; 设置,在unmanaged.exe.config 文件中定义。

对我来说,当我不使用 CLR 托管或首先使用解决方法 helper.dll 时,为什么会忽略 &lt;gcServer enabled="true"/&gt; 设置?

问候 沃尔米希

【讨论】:

    【解决方案2】:

    这确实是一个错误,很可能是在 .NET Framework 4.0 中引入的。虽然我们无法确定时间表,但我们计划在即将发布的 .NET Framework 版本中解决此问题。

    同时,您可以通过设置以下环境变量来解决此问题:

    COMPlus_BuildFlavor=SVR
    

    请注意,所有COMPlus_* 环境变量均不受支持,仅用于调试/开发 CLR 本身。因此,请记住这一点,并做出对您的环境有意义的决定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-27
      • 1970-01-01
      • 2020-12-10
      • 2010-10-22
      • 1970-01-01
      • 2012-09-22
      • 2014-12-06
      • 2010-10-01
      相关资源
      最近更新 更多