VirtualAllocEx内存预留未返回基地址问题求助
桌面图标名称提取项目中VirtualAllocEx返回0的问题
我正在开发一个C#小项目,目标是把桌面图标的名称保存到文本文件里。入门时参考了两篇关于桌面图标操作的技术文章,目前已经通过FindWindow、FindWindowEx和SendMessage成功获取到桌面图标数量(实际是115个),也用GetWindowThreadProcessId拿到了线程进程ID,OpenProcess也返回了有效值。
但现在遇到的问题是VirtualAllocEx始终返回0。根据VirtualAllocEx的官方文档说明,函数成功时会返回分配内存区域的基地址,失败则返回NULL。调用GetLastError却没有返回任何错误码。
我了解到自Windows Vista起内存管理机制有变化,部分访问参数的使用不像以前那么简单,会不会是这个原因导致的问题?
系统环境
- Windows 10 22H2
- Visual Studio 17.11.4(C#工具4.11.0)
- .NET 8.0
代码
using System.Runtime.InteropServices; namespace DesktopIcon { partial class DesktopIcon { internal const int LVM_GETITEMCOUNT = 0x00001000 + 4; internal const int LVIF_TEXT = 0x00000001; internal const int PROCESS_VM_OPERATION = 0x00000008; internal const int PROCESS_VM_READ = 0x00000010; internal const int PROCESS_VM_WRITE = 0x00000020; internal const int MEM_RESERVE = 0x00002000; internal const int MEM_COMMIT = 0x00001000; internal const int PAGE_READWRITE = 0x00001000; [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern uint GetLastError(); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern void SetLastError(uint dwErrorCode); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.DLL", SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, uint processId); [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); static void Main(string[] args) { GetDesktopIcons(); } public static void GetDesktopIcons() { // Reset last error SetLastError(0); // This buffer size will be enough. const int BUFFER_SIZE = 0x110; uint vProcessId = 0; try { // Find window handles via process names. IntPtr vHandle = FindWindow("Progman", "Program Manager"); vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null); vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", "FolderView"); Console.WriteLine($"vHandle: {vHandle}"); // Count subwindows of desktop => count of icons. int vIconCount = SendMessage(vHandle, LVM_GETITEMCOUNT, 0, 0); Console.WriteLine($"Count desktop icons (vIconCount): {vIconCount}"); // Get the desktop window's process to enumerate child windows. int threadProcessId = GetWindowThreadProcessId(vHandle, out vProcessId); Console.WriteLine($"ThreadProcessId: {threadProcessId}"); IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, vProcessId); Console.WriteLine($"vProcess: {vProcess}"); // Allocate memory in the desktop process. IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); Console.WriteLine($"vPointer: {vPointer}"); } catch (Exception) { throw; } Console.WriteLine($"GetLastError: {GetLastError()}"); } } }
解决建议
- 权限问题:Windows Vista及之后的UAC机制限制了普通权限进程对系统进程(如桌面所属的explorer.exe)的内存操作。需确保程序以管理员权限运行,可右键程序选择“以管理员身份运行”测试。
- OpenProcess权限补充:在
OpenProcess的权限参数中添加PROCESS_QUERY_INFORMATION,部分场景下需要该权限才能正常完成内存分配。 - 错误检查时机修正:不要统一在最后调用
GetLastError,应在VirtualAllocEx调用后立即使用Marshal.GetLastWin32Error()获取错误码(.NET的P/Invoke可能会在中间调用其他API覆盖错误码),同时移除自定义的GetLastError导入,改用.NET内置的方法。 - 内存大小类型显式转换:将
BUFFER_SIZE显式转换为uint类型后传入VirtualAllocEx的dwSize参数,避免隐式转换可能的问题。 - 进程权限验证:确认当前进程是否真的拥有对explorer.exe的内存操作权限,可通过
GetLastWin32Error()获取准确错误码后对照官方文档排查。
内容的提问来源于stack exchange,提问作者user27741222
相关产品推荐
相关产品推荐

