诀窍是不要使用VDMEnumProcessWOW http://msdn.microsoft.com/en-us/library/bb963830(VS.85).aspx(它提供了 VDM),但要使用VDMEnumTasksWOW http://msdn.microsoft.com/en-us/library/bb963831(VS.85).aspx。将为指定 VDM 中的每个 16 位任务调用您传递给此函数的枚举器函数。
我自己没有检查过,但根据文档,这个CodeProject 库 http://www.codeproject.com/KB/threads/enumprocess.aspx?msg=598133如果您传入 PROC16 枚举值,则确实会执行此操作。它是 C++,如果您需要编译该代码并从 C# 调用它的帮助,请告诉我,我会给您一个示例。
使用这种技术的程序是流程大师 http://www.angelfire.com/biz/rhaminisys/procmstr.html,它带有完整的源代码。我建议你运行它来看看它是否提供了你需要的信息,如果是的话,你可以将此方法应用到你自己的应用程序中(它不能在Windows Vista或7上运行,它使用旧的VB5代码,显然它不是兼容。它应该在 XP 上运行)。
如果这些功能的情况没有按计划进行,您可能使用的是 Vista,并且可能需要此中描述的修补程序StackOverflow问题 https://stackoverflow.com/questions/457709/vdmenumprocesswow-returns-no-processes-on-vista,这指向,依次是此处描述 http://support.microsoft.com/kb/953347:
“一个应用程序使用
VDMEnumProcessWOW 函数
枚举 DOS 虚拟机返回
无输出或输出不正确
运行 32 位的计算机
Windows Vista 版本”
Update:虽然这看起来很有希望,但我应用了补丁,运行了多个版本的代码,包括 Microsoft 的代码,虽然它们都可以在 XP 上运行,但在 Vista 上它们会默默地失败(没有错误或错误的返回值)。
“有点”工作代码
Update:我(除其他外)尝试了以下代码,该代码在 C# 中编译得很好(并且可以写得更简单,但我不想冒编组错误的风险)。当你添加这些函数时,你可以调用Enum16BitProcesses
,它将把 16 位进程的 EXE 文件的文件名写入控制台。
我无法在 Vista 32 位上运行它。但也许其他人可以尝试编译它,或者找到代码中的错误。很高兴知道它是否适用于其他系统:
public class YourEnumerateClass
{
public static void Enum16BitProcesses()
{
// create a delegate for the callback function
ProcessTasksExDelegate procTasksDlgt =
new ProcessTasksExDelegate(YourEnumerateClass.ProcessTasksEx);
// this part is the easy way of getting NTVDM procs
foreach (var ntvdm in Process.GetProcessesByName("ntvdm"))
{
Console.WriteLine("ntvdm id = {0}", ntvdm.Id);
int apiRet = VDMEnumTaskWOWEx(ntvdm.Id, procTasksDlgt, IntPtr.Zero);
Console.WriteLine("EnumTaskWOW returns {0}", apiRet);
}
}
// declaration of API function callback
public delegate bool ProcessTasksExDelegate(
int ThreadId,
IntPtr hMod16,
IntPtr hTask16,
IntPtr ptrModName,
IntPtr ptrFileName,
IntPtr UserDefined
);
// the actual function that fails on Vista so far
[DllImport("VdmDbg.dll", SetLastError = false, CharSet = CharSet.Auto)]
public static extern int VDMEnumTaskWOWEx(
int processId,
ProcessTasksExDelegate TaskEnumProc,
IntPtr lparam);
// the actual callback function, on Vista never gets called
public static bool ProcessTasksEx(
int ThreadId,
IntPtr hMod16,
IntPtr hTask16,
IntPtr ptrModName,
IntPtr ptrFileName,
IntPtr UserDefined
)
{
// using PtrToStringAnsi, based on Matt's comment, if it fails, try PtrToStringAuto
string filename = Marshal.PtrToStringAnsi(ptrFileName);
Console.WriteLine("Filename of WOW16 process: {0}", filename);
return false; // false continues enumeration
}
}
Update: 有趣的阅读 http://www.microsoft.com/msj/0898/hood0898.aspx由著名的马特·皮特雷克 (Matt Pietrek) 创作。注意靠近结尾的那句话:
“对于初学者来说,基于 MS-DOS 的程序
似乎总是在单独的 NTVDM 中运行
会议。我从来没能得到
基于 MS-DOS 的程序运行在
与基于 16 位 Windows 的会话相同
程序。我也没能得到两个
独立启动基于MS-DOS
在同一个 NTVDM 中运行的程序
会议。事实上,NTVDM 会话
正在运行的 MS-DOS 程序不显示
在 VDMEnumProcessWOW 枚举中。”
看来,要找出加载了哪些进程,您需要在 NTVDM 中编写一个挂钩或编写一个监听器来监视对文件的访问。当尝试读取某个 DOS 文件的应用程序是 NTVDM.exe 时,那就是宾果游戏。您可能想编写一个仅附加到 NTVDM.exe 的 DLL,但现在我们有点超前了。长话短说:这次 NTVDM 的小旅程展示了最终出现真正骗局的“可能性”。
还有另一种方法,但时间太短,无法创建示例。您可以查看 DOS 内存段,EXE 通常加载在同一段。但我不确定这最终是否会带来同样的结果,以及是否值得付出努力。