具有 gcServer = true 和 gcAllowVeryLargeObjects = true 的 C# COM DLL

C# COM DLL with gcServer = true and gcAllowVeryLargeObjects = true

假设我

  • a C# DLL called managed.dll which is COM visible.
  • a C# EXE called magaged.exe which uses the managed.dll and which has an app.config called managed.exe.config.
  • a C++ EXE called unmanaged.exe which calls the managed.dll over COM and which has the same app.config as the C# EXE but called unmanaged.exe.config in this case.

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; }
    }
}

两个 EXE 的 app.config 看起来像这样:

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

对于 managed.exe,一切都按预期进行。但对于 unmanaged.exe<gcServer enabled="true"/> 设置将被忽略。我不明白为什么?

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

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

知道为什么在 COM 上使用 managed.dll 时忽略 <gcServer enabled="true"/> 设置吗?

问候 沃尔米奇

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

解决方法 1(COM Interop 之前的主机 CLR)

我必须更改以下内容:

  • the C++ EXE called unmanaged.exe has to host the CLR first and set the startup flags before calling the managed.dll over COM.

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 Interop 之前使用 C++/CLI DLL)

我必须更改以下内容:

  • add a C++/CLI DLL called helper.dll which has an app.config called helper.dll.config. The app.config is the same as for the C++ EXE.
  • the C++ EXE called unmanaged.exe has to call first a function from the C++/CLI helper.dll before calling the managed.dll over COM.

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 文件丢失,<gcServer enabled="true"/> 设置将再次被忽略。其他设置,例如 <gcAllowVeryLargeObjects enabled="true" /> 设置,在 unmanaged.exe.config 文件中定义。

我仍然不清楚为什么当我不使用 CLR 托管或解决方法 helper.dll 时忽略 <gcServer enabled="true"/> 设置?

问候 沃尔米奇

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

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

COMPlus_BuildFlavor=SVR

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