C# -> C++/CLI -> 本机方法 - 需要 SafeProcessHandle?
C# -> C++/CLI -> native method - need SafeProcessHandle?
我的目标是 .NET 4.6.1,我有调用 C++/CLI 代码的 C# 代码调用本地 Win32 方法 EnumProcessModulesEx
,它需要一个 HANDLE 作为它的第一个输入参数:
// C#
System.Diagnostics.Process process = // ...
var allModuleNames = ProcessHelper.GetAllModuleNames(process);
// C++/CLI
#include <Psapi.h>
// ...
using namespace System;
using namespace System::Diagnostics;
// ...
array<String^>^ ProcessHelper::GetAllModuleNames(Process^ process)
{
// Should I use a SafeProcessHandle instead?
HANDLE processHandle = static_cast<HANDLE>(process->Handle.ToPointer());
BOOL result = EnumProcessModulesEx(processHandle, /*...*/);
// ...
}
我可以控制 C# 和 C++/CLI 代码,但我没有做任何事情 P/Invoke。我的 C++/CLI 方法目前接受 Process
parameter, but it's only using the Process.Handle
property (and doing a cast to obtain the necessary HANDLE value). Is this safe, or do I need a SafeProcessHandle
somewhere? If so, how do I pass the SafeProcessHandle
value to EnumProcessModulesEx
? Do I have to call SafeHandle.DangerousGetHandle
?
SafeHandle
的目的是确保句柄在 SafeHandle
结束时得到释放,因此没有资源泄漏。换句话说,它让 GC 管理句柄的生命周期,如果你曾经忽略手动释放它的话。 P/Invoke 理解它并确保它不会在 WinAPI 调用期间完成,即使您在其他任何地方都没有引用它也是如此。
直接使用 Win32 API 时,您不能真正使用它,您必须调用 DangerousGetHandle()
,这只是 returns 您已有的原始句柄。
在您的例子中,句柄是通过 OpenProcess
WinAPI 函数,由 Process
class 检索的。文档指出:
When you are finished with the handle, be sure to close it using the CloseHandle
function.
这意味着 Process
class 将释放它。它在 Dispose
调用和 Close
(Dispose
调用 Close
)中执行此操作。如果您不处理 Process
对象,GC 将为您关闭句柄。
所以你所要做的就是确保 GC 不会在你使用句柄时收集 process
对象,否则句柄将无效。确保这一点的一种简单方法是调用:
System::GC::KeepAlive(process);
在所有使用句柄的代码之后。当 JIT 看到此函数调用时,它将确保 process
引用将被标记为已使用 至少 直到该行(JIT will report 引用可达性GC).
如果您不这样做,and 如果您稍后在代码中不使用 process
对象,and 如果发生 GC,GC 将注意到对象无法访问并收集它(是的,即使您在代码中有一个局部变量引用它 - 如果您不在剩余的代码块中访问它 将 收集,GC 对此非常积极)。
有关详细信息,请参阅 my answer here,尽管它是关于 P/Invoke 用法的。
我的目标是 .NET 4.6.1,我有调用 C++/CLI 代码的 C# 代码调用本地 Win32 方法 EnumProcessModulesEx
,它需要一个 HANDLE 作为它的第一个输入参数:
// C#
System.Diagnostics.Process process = // ...
var allModuleNames = ProcessHelper.GetAllModuleNames(process);
// C++/CLI
#include <Psapi.h>
// ...
using namespace System;
using namespace System::Diagnostics;
// ...
array<String^>^ ProcessHelper::GetAllModuleNames(Process^ process)
{
// Should I use a SafeProcessHandle instead?
HANDLE processHandle = static_cast<HANDLE>(process->Handle.ToPointer());
BOOL result = EnumProcessModulesEx(processHandle, /*...*/);
// ...
}
我可以控制 C# 和 C++/CLI 代码,但我没有做任何事情 P/Invoke。我的 C++/CLI 方法目前接受 Process
parameter, but it's only using the Process.Handle
property (and doing a cast to obtain the necessary HANDLE value). Is this safe, or do I need a SafeProcessHandle
somewhere? If so, how do I pass the SafeProcessHandle
value to EnumProcessModulesEx
? Do I have to call SafeHandle.DangerousGetHandle
?
SafeHandle
的目的是确保句柄在 SafeHandle
结束时得到释放,因此没有资源泄漏。换句话说,它让 GC 管理句柄的生命周期,如果你曾经忽略手动释放它的话。 P/Invoke 理解它并确保它不会在 WinAPI 调用期间完成,即使您在其他任何地方都没有引用它也是如此。
直接使用 Win32 API 时,您不能真正使用它,您必须调用 DangerousGetHandle()
,这只是 returns 您已有的原始句柄。
在您的例子中,句柄是通过 OpenProcess
WinAPI 函数,由 Process
class 检索的。文档指出:
When you are finished with the handle, be sure to close it using the
CloseHandle
function.
这意味着 Process
class 将释放它。它在 Dispose
调用和 Close
(Dispose
调用 Close
)中执行此操作。如果您不处理 Process
对象,GC 将为您关闭句柄。
所以你所要做的就是确保 GC 不会在你使用句柄时收集 process
对象,否则句柄将无效。确保这一点的一种简单方法是调用:
System::GC::KeepAlive(process);
在所有使用句柄的代码之后。当 JIT 看到此函数调用时,它将确保 process
引用将被标记为已使用 至少 直到该行(JIT will report 引用可达性GC).
如果您不这样做,and 如果您稍后在代码中不使用 process
对象,and 如果发生 GC,GC 将注意到对象无法访问并收集它(是的,即使您在代码中有一个局部变量引用它 - 如果您不在剩余的代码块中访问它 将 收集,GC 对此非常积极)。
有关详细信息,请参阅 my answer here,尽管它是关于 P/Invoke 用法的。