杨大毛Windows核心编程视频课程第二部分课程分享

43 阅读5分钟

微信图片_20251005163348_1_114.jpg

杨大毛Windows核心编程视频课程第二部分课程分享---youkeit.xyz/13981/

在Windows系统级工具开发的殿堂里,如果说掌握API调用是入门的钥匙,那么深刻理解并精通内存管理与注册表操作,则是从“使用者”迈向“掌控者”的关键一步。它们如同操作系统的“中枢神经”与“记忆宫殿”,决定了你开发的工具是稳定高效,还是脆弱不堪。杨大毛核心编程的第二部分,正是聚焦于这两大基石,带你深入Windows内核,进行一场硬核的实战演练。

第一部分:内存管理——精准掌控程序的“生命线”

在C/C++的Windows编程中,内存管理是一把双刃剑。它赋予了你无与伦比的灵活性,也带来了悬垂指针、内存泄漏等致命风险。一个优秀的系统工具,必须对内存的分配、使用和释放了如指掌。

1. 核心API:VirtualAlloc 与 VirtualFree

不同于C语言的malloc和C++的new,Windows提供了更底层的内存管理API——VirtualAlloc。它直接从进程的虚拟地址空间中预订(Reserve)和提交(Commit)内存页,粒度更大,控制力更强。

  • 预订内存:仅仅在虚拟地址空间中划出一块区域,并不占用物理内存或页文件。
  • 提交内存:将预订的区域映射到物理内存,此时才可以进行读写。

这种两步走的方式,在需要处理大型数据块或实现自定义内存池时非常有用。

实战代码示例:使用 VirtualAlloc 分配并使用内存块

下面的代码演示了如何分配一块可读写的内存,向其中写入数据,读取并最终释放它。

cpp

复制

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

int main() {
    // 1. 预订并提交一块 4KB 的内存页
    // MEM_COMMIT | MEM_RESERVE 表示立即预订并提交
    // PAGE_READWRITE 表示该内存页可读可写
    LPVOID pMemory = VirtualAlloc(
        NULL,                   // 让系统决定分配地址
        4096,                   // 分配大小 (通常为页大小的整数倍,一页为4KB)
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );

    if (pMemory == NULL) {
        _tprintf(_T("内存分配失败,错误代码: %d\n"), GetLastError());
        return 1;
    }

    _tprintf(_T("成功分配内存,地址: 0x%p\n"), pMemory);

    // 2. 向分配的内存写入数据
    strcpy_s((char*)pMemory, 4096, "Hello, Windows System Programming!");

    // 3. 从内存中读取并打印数据
    _tprintf(_T("内存中的内容: %s\n"), (char*)pMemory);

    // 4. 释放内存块
    // MEM_RELEASE 表示释放整个区域
    BOOL result = VirtualFree(pMemory, 0, MEM_RELEASE);

    if (!result) {
        _tprintf(_T("内存释放失败,错误代码: %d\n"), GetLastError());
    } else {
        _tprintf(_T("内存成功释放。\n"));
    }

    return 0;
}

2. 内存泄漏的“天敌”:调试工具

在复杂的程序中,手动追踪内存泄漏无异于大海捞针。Windows提供了强大的工具来辅助我们。最简单直接的方式是使用CRT(C运行时库)的调试堆功能。通过在包含头文件前定义_CRTDBG_MAP_ALLOC,并在程序退出时调用_CrtDumpMemoryLeaks(),所有未释放的内存分配都会在Visual Studio的输出窗口中报告,并精确到文件名和行号。

第二部分:注册表操作——与Windows配置核心的“对话”

Windows注册表是存储系统配置、用户设置、应用程序信息的层次化数据库。开发系统级工具,经常需要读写注册表来实现配置保存、开机自启、文件关联等功能。

1. 核心API:RegOpenKeyExRegSetValueExRegQueryValueExRegCloseKey

操作注册表通常遵循“打开-操作-关闭”的模式。

  • RegOpenKeyEx: 打开一个指定的注册表项。
  • RegSetValueEx: 在打开的项下设置一个值。
  • RegQueryValueEx: 查询一个指定值的名称和数据。
  • RegCloseKey: 关闭打开的注册表项句柄。

实战代码示例:创建、写入、读取并删除注册表项

下面的代码演示了如何在HKEY_CURRENT_USER下创建一个自定义项,写入一个字符串值和DWORD值,然后读取它们,最后清理创建的项。

cpp

复制

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#define MY_REG_KEY _T("SOFTWARE\MySystemTool")

int main() {
    HKEY hKey;
    DWORD dwDisposition;
    LONG lResult;

    // 1. 创建或打开注册表项
    // 如果不存在则创建,存在则打开
    lResult = RegCreateKeyEx(
        HKEY_CURRENT_USER,     // 根键
        MY_REG_KEY,            // 子键路径
        0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition
    );

    if (lResult != ERROR_SUCCESS) {
        _tprintf(_T("创建/打开注册表项失败,错误代码: %d\n"), lResult);
        return 1;
    }

    if (dwDisposition == REG_CREATED_NEW_KEY) {
        _tprintf(_T("成功创建新的注册表项。\n"));
    } else {
        _tprintf(_T("成功打开已存在的注册表项。\n"));
    }

    // 2. 设置一个字符串值 (REG_SZ)
    const TCHAR* szValue = _T("C:\Program Files\MyTool\tool.exe");
    lResult = RegSetValueEx(hKey, _T("InstallPath"), 0, REG_SZ, (const BYTE*)szValue, (DWORD)(_tcslen(szValue) + 1) * sizeof(TCHAR));
    if (lResult == ERROR_SUCCESS) {
        _tprintf(_T("成功设置字符串值 'InstallPath'。\n"));
    }

    // 3. 设置一个DWORD值 (REG_DWORD)
    DWORD dwVersion = 1;
    lResult = RegSetValueEx(hKey, _T("Version"), 0, REG_DWORD, (const BYTE*)&dwVersion, sizeof(DWORD));
    if (lResult == ERROR_SUCCESS) {
        _tprintf(_T("成功设置DWORD值 'Version'。\n"));
    }

    // --- 假设程序重启,我们需要读取这些值 ---
    
    // 4. 读取字符串值
    TCHAR szReadPath[MAX_PATH];
    DWORD dwBufferSize = sizeof(szReadPath);
    lResult = RegQueryValueEx(hKey, _T("InstallPath"), NULL, NULL, (LPBYTE)szReadPath, &dwBufferSize);
    if (lResult == ERROR_SUCCESS) {
        _tprintf(_T("读取到 'InstallPath': %s\n"), szReadPath);
    }

    // 5. 读取DWORD值
    DWORD dwReadVersion;
    dwBufferSize = sizeof(DWORD);
    lResult = RegQueryValueEx(hKey, _T("Version"), NULL, NULL, (LPBYTE)&dwReadVersion, &dwBufferSize);
    if (lResult == ERROR_SUCCESS) {
        _tprintf(_T("读取到 'Version': %d\n"), dwReadVersion);
    }

    // 6. 关闭注册表项句柄
    RegCloseKey(hKey);

    // 7. 清理:删除我们创建的整个项及其所有值
    _tprintf(_T("\n正在清理注册表...\n"));
    lResult = RegDeleteKey(HKEY_CURRENT_USER, MY_REG_KEY);
    if (lResult == ERROR_SUCCESS) {
        _tprintf(_T("成功删除注册表项 %s\n"), MY_REG_KEY);
    } else {
        _tprintf(_T("删除注册表项失败,错误代码: %d\n"), lResult);
    }

    return 0;
}

引用

结语:从API调用者到系统思考者

内存管理与注册表操作,是Windows系统级开发的内功心法。通过杨大毛核心编程的这部分学习,你不仅学会了如何调用几个关键的API,更重要的是,你开始理解Windows系统是如何管理其最宝贵的资源的。

你将懂得,每一次内存分配都伴随着责任,每一次注册表写入都影响着系统的全局。这种对系统底层机制的深刻洞察,将使你在开发系统工具时,能够写出更稳定、更高效、更安全的代码,真正从一个API的调用者,蜕变为一个具备系统思维的优秀开发者。