考虑下一种情况。我已经使用 EasyHook 将我的托管 dll 注入到进程中。EasyHook 使用单独的 AppDomain 注入 dll。现在我需要一种方法来获取有关在当前进程中创建新 AppDomain 的通知。所以问题是当在这个过程中创建一个新的 AppDomain 时,有没有办法获得通知?
1 回答
没有事件或简单的方法可以做到这一点,有一个 COM 中断允许您获取加载的应用程序域列表,但任何事件等都在私有接口上对我们隐藏。
有两种方法可以做到这一点,但都需要您积极寻找信息,即也没有要注册的事件。
- 使用性能计数器。
- 使用 mscoree COM 中断。
这两个选项可以相互补充,但这取决于您需要什么级别的信息。
使用性能计数器
CLR 有许多可用的性能计数器,但我们关心的一个位于“.Net CLR Loading”类别中,它是称为“Total Appdomains”的计数器。
使用 System.Diagnostics 命名空间,您可以获得机器中运行的每个实例/进程的应用程序域数。
就像下面的代码:
PerformanceCounter toPopulate = new PerformanceCounter(".Net CLR Loading", "Total Appdomains", "ConsoleApplication2.vshost", true);
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
(请注意,如果您创建自己的应用程序,请注意该示例需要应用程序实例名称,请务必更改此名称)
您可以将其包装在一个循环中,并在数字更改时为您的应用程序提高一个偶数。(不优雅,但目前没有办法绕过它)
使用 mscoree COM 中断
此外,如果要列出进程中的所有应用程序域,则需要使用 MSCOREE.TBL 库,它是 CLRHost 使用的 COM 库。
您可以使用 mscoree 在 C:\WINDOWS\Microsoft.NET\Framework\vXXXXXX\mscoree.tlb 找到库;
如果您在窗口 7 或更高版本上使用它,则必须确保参考属性中的嵌入程序集类型已关闭,因为该程序集不能像那样嵌入。请参阅此堆栈帖子的更多信息:无法嵌入互操作类型
请参阅下面的代码以了解如何返回和列出进程中的所有应用程序域(这将返回每个应用程序域的实际 AppDomain 实例)。可以在此处找到原始堆栈帖子:List AppDomains in Process
public static List<AppDomain> GetAppDomains()
{
List<AppDomain> _IList = new List<AppDomain>();
IntPtr enumHandle = IntPtr.Zero;
CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
try
{
host.EnumDomains(out enumHandle);
object domain = null;
while (true)
{
host.NextDomain(enumHandle, out domain);
if (domain == null) break;
AppDomain appDomain = (AppDomain)domain;
_IList.Add(appDomain);
}
return _IList;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
finally
{
host.CloseEnum(enumHandle);
Marshal.ReleaseComObject(host);
}
}
现在您可以看到一个进程中存在多少个应用程序域并将它们列出来进行测试。
下面是一个使用这两种技术的完整示例。
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Printing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Xps.Packaging;
using System.Runtime.InteropServices;
using mscoree;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication2
{
class AppDomainWorker
{
public void DoSomeWork()
{
while (true)
{
for (int i = 0; i < 1000; i++)
{
var hello = "hello world".GetHashCode();
}
Thread.Sleep(500);
}
}
}
class Program
{
[STAThread]
static void Main(string[] args)
{
PerformanceCounter toPopulate = new PerformanceCounter(".Net CLR Loading", "Total Appdomains", "ConsoleApplication2.vshost", true);
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
for (int i = 0; i < 10; i++)
{
AppDomain domain = AppDomain.CreateDomain("App Domain " + i);
domain.DoCallBack(() => new Thread(new AppDomainWorker().DoSomeWork).Start());
Console.WriteLine("App domains listed = {0}", toPopulate.NextValue().ToString());
}
Console.WriteLine("List all app domains");
GetAppDomains().ForEach(a => {
Console.WriteLine(a.FriendlyName);
});
Console.WriteLine("running, press any key to stop");
Console.ReadKey();
}
public static List<AppDomain> GetAppDomains()
{
List<AppDomain> _IList = new List<AppDomain>();
IntPtr enumHandle = IntPtr.Zero;
CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
try
{
host.EnumDomains(out enumHandle);
object domain = null;
while (true)
{
host.NextDomain(enumHandle, out domain);
if (domain == null) break;
AppDomain appDomain = (AppDomain)domain;
_IList.Add(appDomain);
}
return _IList;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
finally
{
host.CloseEnum(enumHandle);
Marshal.ReleaseComObject(host);
}
}
}
}
我希望这会有所帮助,如果您需要任何进一步的帮助,请告诉我们。