具有 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 本身。因此,请牢记这一点,并做出对您的环境有意义的决定。
假设我
- a C# DLL called
managed.dll
which is COM visible.- a C# EXE called
magaged.exe
which uses themanaged.dll
and which has an app.config calledmanaged.exe.config
.- a C++ EXE called
unmanaged.exe
which calls themanaged.dll
over COM and which has the same app.config as the C# EXE but calledunmanaged.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 themanaged.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 calledhelper.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++/CLIhelper.dll
before calling themanaged.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 本身。因此,请牢记这一点,并做出对您的环境有意义的决定。