如何调试加载有自定义 .NET Core 2 主机的程序集?
How to debug assemblies loaded with a custom .NET Core 2 host?
为了使用托管附加功能扩展本机应用程序,我创建了一个自定义 .NET Core 2.0 主机,与 official docs.
中描述的非常相似
到目前为止它运行良好并加载了我的程序集。它还成功运行了 ICLRRuntimeHost2.CreateDelegate
委托给的方法;为了进行测试,我将 "Hello World" 写入了一个带有 System.IO.File.WriteAllText
的新文件,该文件按预期显示了该内容。
但是,我对如何实际调试我加载的程序集中的托管代码感到有点困惑。
- 简单地在此处设置断点不会触发。
- 我还尝试搜索特定的与调试相关的 属性 值对以传递给
ICLRRuntimeHost2.CreateAppDomainWithManager
方法,但没有找到。
- 调用
Debugger.Break
会导致 MSVC++ 调试器中断 ("bla.exe has triggered a breakpoint."),但不会进入我的托管代码或理解其中的任何内容.我想这只是破坏调试器的一种常见的系统本机方法。
- 或者是否有类似 Python 的 "egg file" 之类的东西来使用调试器将 Visual Studio 到 "connect" 托管代码?
如前所述,我的代码与文档非常相似,在此供参考。 Run
基本上加载 coreclr 库,从中获取 GetClrRuntimeHost 函数,实例化应用程序域并运行委托托管代码。如果一些被调用的额外方法或使用的成员的实现有任何问题,我会根据要求添加它们。
void ClrHost::Run(wstring const& assembly, wstring const& type, wstring const& method)
{
// Load the CoreCLR.dll and retrieve the GetCLRRuntimeHost function.
wstring coreClrFilePath = _runtimeFolder / "coreclr.dll";
HMODULE coreClr = LoadLibraryExW(coreClrFilePath.c_str(), NULL, 0);
if (!coreClr)
throw new ClrHostException(L"Could not load CoreCLR.dll.");
FnGetCLRRuntimeHost fnGetClrRuntimeHost = (FnGetCLRRuntimeHost)GetProcAddress(coreClr, "GetCLRRuntimeHost");
if (!fnGetClrRuntimeHost)
throw new ClrHostException(L"Could not find GetCLRRuntimeHost function.");
// Instantiate and set up a runtime host.
if (FAILED(fnGetClrRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&_runtimeHost)))
throw new ClrHostException(L"Could not retrieve ICLRRuntimeHost2 instance.");
STARTUP_FLAGS startupFlags = static_cast<STARTUP_FLAGS>(
STARTUP_FLAGS::STARTUP_CONCURRENT_GC
| STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN
| STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN);
if (FAILED(_runtimeHost->SetStartupFlags(startupFlags)))
throw new ClrHostException(L"Could not set runtime host startup flags.");
if (FAILED(_runtimeHost->Start()))
throw new ClrHostException(L"Could not start runtime host.");
// Instantiate the AppDomain with the configured settings.
int appDomainFlags = APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS
| APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP
| APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT;
wstring tpaAssemblies = ConcatenatePaths(GetAssembliesFromFolder(_runtimeFolder));
wstring assemblyFolders = ConcatenatePaths(_assemblyFolders);
wstring nativeLibFolders = ConcatenatePaths(_nativeLibFolders);
LPCWSTR propertyKeys[] = {
L"TRUSTED_PLATFORM_ASSEMBLIES",
L"APP_PATHS",
L"APP_NI_PATHS",
L"NATIVE_DLL_SEARCH_DIRECTORIES",
L"PLATFORM_RESOURCE_ROOTS",
L"AppDomainCompatSwitch"
};
LPCWSTR propertyValues[] = {
tpaAssemblies.c_str(),
assemblyFolders.c_str(),
assemblyFolders.c_str(),
nativeLibFolders.c_str(),
assemblyFolders.c_str(),
L"UseLatestBehaviorWhenTFMNotSpecified"
};
if (FAILED(_runtimeHost->CreateAppDomainWithManager(L"IDA.NET AppDomain", appDomainFlags, NULL, NULL,
sizeof(propertyKeys) / sizeof(LPCWSTR), propertyKeys, propertyValues, &_appDomainID)))
{
throw new ClrHostException(L"Could not create AppDomain.");
}
// Get a delegate for the managed static method.
void *fnDelegate = NULL;
HRESULT hr = _runtimeHost->CreateDelegate(_appDomainID, assembly.c_str(), type.c_str(), method.c_str(),
(INT_PTR*)&fnDelegate);
if (FAILED(hr))
throw new ClrHostException(L"Could not run " + type + L"." + method + L" in " + assembly + L".");
// Execute the managed code.
((RunSignature*)fnDelegate)();
}
我才发现它比想象的要简单!
现在,我总是使用我的原生 C++ 项目(托管 .NET Core 的项目)作为 Visual Studio 中的启动项目。
但是,通过创建启动配置文件启动外部本机可执行文件而不是 "launching" 我的库,将启动项目更改为托管 C# 库,可以调试和逐步执行托管代码。
您可以在 "Debug" 下的项目属性中设置这样的启动配置文件,或者将典型的 .NET Core Properties\launchSettings.json
添加到您的托管项目根目录,存储如下内容:
{
"profiles": {
"Any profile name (typically the project name)": {
"commandName": "Executable",
"executablePath": "C:\FullNativeExecutablePath\AndFileName.exe",
"workingDirectory": "C:\FullNativeExecutablePath"
}
}
}
为了使用托管附加功能扩展本机应用程序,我创建了一个自定义 .NET Core 2.0 主机,与 official docs.
中描述的非常相似到目前为止它运行良好并加载了我的程序集。它还成功运行了 ICLRRuntimeHost2.CreateDelegate
委托给的方法;为了进行测试,我将 "Hello World" 写入了一个带有 System.IO.File.WriteAllText
的新文件,该文件按预期显示了该内容。
但是,我对如何实际调试我加载的程序集中的托管代码感到有点困惑。
- 简单地在此处设置断点不会触发。
- 我还尝试搜索特定的与调试相关的 属性 值对以传递给
ICLRRuntimeHost2.CreateAppDomainWithManager
方法,但没有找到。 - 调用
Debugger.Break
会导致 MSVC++ 调试器中断 ("bla.exe has triggered a breakpoint."),但不会进入我的托管代码或理解其中的任何内容.我想这只是破坏调试器的一种常见的系统本机方法。 - 或者是否有类似 Python 的 "egg file" 之类的东西来使用调试器将 Visual Studio 到 "connect" 托管代码?
如前所述,我的代码与文档非常相似,在此供参考。 Run
基本上加载 coreclr 库,从中获取 GetClrRuntimeHost 函数,实例化应用程序域并运行委托托管代码。如果一些被调用的额外方法或使用的成员的实现有任何问题,我会根据要求添加它们。
void ClrHost::Run(wstring const& assembly, wstring const& type, wstring const& method)
{
// Load the CoreCLR.dll and retrieve the GetCLRRuntimeHost function.
wstring coreClrFilePath = _runtimeFolder / "coreclr.dll";
HMODULE coreClr = LoadLibraryExW(coreClrFilePath.c_str(), NULL, 0);
if (!coreClr)
throw new ClrHostException(L"Could not load CoreCLR.dll.");
FnGetCLRRuntimeHost fnGetClrRuntimeHost = (FnGetCLRRuntimeHost)GetProcAddress(coreClr, "GetCLRRuntimeHost");
if (!fnGetClrRuntimeHost)
throw new ClrHostException(L"Could not find GetCLRRuntimeHost function.");
// Instantiate and set up a runtime host.
if (FAILED(fnGetClrRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&_runtimeHost)))
throw new ClrHostException(L"Could not retrieve ICLRRuntimeHost2 instance.");
STARTUP_FLAGS startupFlags = static_cast<STARTUP_FLAGS>(
STARTUP_FLAGS::STARTUP_CONCURRENT_GC
| STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN
| STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN);
if (FAILED(_runtimeHost->SetStartupFlags(startupFlags)))
throw new ClrHostException(L"Could not set runtime host startup flags.");
if (FAILED(_runtimeHost->Start()))
throw new ClrHostException(L"Could not start runtime host.");
// Instantiate the AppDomain with the configured settings.
int appDomainFlags = APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS
| APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP
| APPDOMAIN_DISABLE_TRANSPARENCY_ENFORCEMENT;
wstring tpaAssemblies = ConcatenatePaths(GetAssembliesFromFolder(_runtimeFolder));
wstring assemblyFolders = ConcatenatePaths(_assemblyFolders);
wstring nativeLibFolders = ConcatenatePaths(_nativeLibFolders);
LPCWSTR propertyKeys[] = {
L"TRUSTED_PLATFORM_ASSEMBLIES",
L"APP_PATHS",
L"APP_NI_PATHS",
L"NATIVE_DLL_SEARCH_DIRECTORIES",
L"PLATFORM_RESOURCE_ROOTS",
L"AppDomainCompatSwitch"
};
LPCWSTR propertyValues[] = {
tpaAssemblies.c_str(),
assemblyFolders.c_str(),
assemblyFolders.c_str(),
nativeLibFolders.c_str(),
assemblyFolders.c_str(),
L"UseLatestBehaviorWhenTFMNotSpecified"
};
if (FAILED(_runtimeHost->CreateAppDomainWithManager(L"IDA.NET AppDomain", appDomainFlags, NULL, NULL,
sizeof(propertyKeys) / sizeof(LPCWSTR), propertyKeys, propertyValues, &_appDomainID)))
{
throw new ClrHostException(L"Could not create AppDomain.");
}
// Get a delegate for the managed static method.
void *fnDelegate = NULL;
HRESULT hr = _runtimeHost->CreateDelegate(_appDomainID, assembly.c_str(), type.c_str(), method.c_str(),
(INT_PTR*)&fnDelegate);
if (FAILED(hr))
throw new ClrHostException(L"Could not run " + type + L"." + method + L" in " + assembly + L".");
// Execute the managed code.
((RunSignature*)fnDelegate)();
}
我才发现它比想象的要简单!
现在,我总是使用我的原生 C++ 项目(托管 .NET Core 的项目)作为 Visual Studio 中的启动项目。
但是,通过创建启动配置文件启动外部本机可执行文件而不是 "launching" 我的库,将启动项目更改为托管 C# 库,可以调试和逐步执行托管代码。
您可以在 "Debug" 下的项目属性中设置这样的启动配置文件,或者将典型的 .NET Core Properties\launchSettings.json
添加到您的托管项目根目录,存储如下内容:
{
"profiles": {
"Any profile name (typically the project name)": {
"commandName": "Executable",
"executablePath": "C:\FullNativeExecutablePath\AndFileName.exe",
"workingDirectory": "C:\FullNativeExecutablePath"
}
}
}