Windows 平台 C++ 开发实战

简介

在 Windows 平台上进行 C++ 开发,需要掌握许多平台特定的知识和技巧。本文聚焦于 Windows 平台开发的核心主题,包括 DLL 与静态库的制作与调试、Windows 服务编程、内存泄漏检测,以及 Bloom 过滤器等实用工具类。这些内容在实际项目开发中具有很高的实用价值。

一、DLL与静态库制作

1.1 Windows 下制作 DLL(VS2017)

  1. 新建项目 -> Visual C++ -> Windows桌面 -> 动态链接库
  2. 在要导出的类或函数加上 __declspec(dllexport)
  3. 导入时使用 __declspec(dllimport)
  4. 生成的 .lib 文件是导入库(符号表),不是静态库
// 导出
class __declspec(dllexport) MyClass {
    // ...
};

// 导入
class __declspec(dllimport) MyClass {
    // ...
};

1.2 Windows 下制作静态库

  1. 新建项目 -> 静态库
  2. 在需导出的函数或类加上 extern 声明
  3. 生成 .lib 文件

1.3 Linux 下制作动态库

g++ -fPIC -c source.cpp           # 生成 .o 文件
g++ -shared -o libname.so source.o # 生成动态库
# 或者合并为一步
g++ -fPIC -shared -o libname.so source.cpp

1.4 Linux 下制作静态库

ar -crv libname.a source1.o source2.o

1.5 VS 跨平台编译 Linux 动态库

  1. 属性 -> 常规 -> 目标文件名:改为 lib$(ProjectName)
  2. 属性 -> 常规 -> 目标扩展名:改为 .so
  3. 属性 -> C/C++ -> 附加选项:添加 -fPIC
  4. 属性 -> 链接器 -> 附加选项:添加 -shared

1.6 调试 DLL

在 Windows 下调试 DLL 代码时,需要将生成 DLL 工程的 .pdb.lib.dll 文件拷贝至当前工程的 Debug 目录下。

1.7 在 C++ 中调用 C 编写的库

// 必须在头文件中添加 extern "C"
extern "C" {
    #include "c_library.h"
}

否则由于 C++ 的名称修饰(name mangling),链接器无法找到正确的符号。

二、Windows 服务编程

#include <Windows.h>

HANDLE hSvcStopEvent = nullptr;
SERVICE_STATUS serviceParam;
SERVICE_STATUS_HANDLE serviceHandle;
static char name[64]{ "ClearService" };

void WINAPI CtrlHandler(DWORD request) {
    switch (request) {
    case SERVICE_CONTROL_STOP:
        SetEvent(hSvcStopEvent);
        serviceParam.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus(serviceHandle, &serviceParam);
        break;
    case SERVICE_CONTROL_SHUTDOWN:
        serviceParam.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus(serviceHandle, &serviceParam);
        SetEvent(hSvcStopEvent);
        break;
    }
}

void WINAPI ServiceMain(int argc, char** argv) {
    // 初始化服务...
    hSvcStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    serviceParam.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(serviceHandle, &serviceParam);

    // 自定义服务入口
    // ...

    WaitForSingleObject(hSvcStopEvent, INFINITE);  // 等待停止信号
}

int main(int argc, char* argv[]) {
    SERVICE_TABLE_ENTRY entrytable[2];
    entrytable[0].lpServiceName = name;
    entrytable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
    entrytable[1].lpServiceName = nullptr;
    entrytable[1].lpServiceProc = nullptr;
    StartServiceCtrlDispatcher(entrytable);
    return 0;
}

三、内存泄漏检测

3.1 Visual C++ 内存泄漏检测(crtdbg.h)

#define _CRTDBG_MAP_ALLOC  // 用于定位产生内存泄漏的文件和行号
#include <crtdbg.h>

int main() {
    // 方法1:在程序退出前调用
    _CrtDumpMemoryLeaks();

    // 方法2:自动检测(多个退出点时使用)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    // 方法3:在指定编号处断点
    _CrtSetBreakAlloc(96);  // 在编号96的内存分配处断点
}

输出示例

Dumping objects ->
{96} normal block at 0x006B9090, 20 bytes long.
Data: <                > 00 00 00 00 ...
Object dump complete.

3.2 广义内存泄漏

不仅包含堆内存泄漏,还包括系统资源泄漏:

  • 内核态 HANDLE
  • GDI Object
  • SOCKET
  • Interface 等

系统资源泄漏可能比堆内存泄漏更严重,因为核心态内存泄漏会导致操作系统不稳定。

四、Bloom 过滤器

Bloom 过滤器用于快速判断某个 key 是否可能存在:

// 本质是一个 bitset
// 插入:将 key 通过多个哈希函数映射到多个 bit 位,置为 1
// 查询:检查所有映射的 bit 位是否都为 1
//   - 全为 1:key 可能存在(可能有碰撞)
//   - 不全为 1:key 一定不存在

// 优化碰撞:
// 1. 增大 bits_per_key
// 2. 使用更复杂的哈希函数
// 参考:leveldb/util/bloom_test.cc

总结

Windows 平台 C++ 开发涉及许多平台特有的工具和 API。掌握 DLL/静态库的制作与调试是跨平台开发的基础,Windows 服务编程是后台服务开发的核心技能,内存泄漏检测则是保障软件质量的关键手段。

关键实战要点:

  • 熟练制作和使用 DLL 与静态库,理解导入库与静态库的区别
  • 掌握 Windows 服务编程模型(ServiceMain + CtrlHandler)
  • 使用 crtdbg.h 等工具定位和修复内存泄漏
  • 关注广义内存泄漏(HANDLE、GDI、SOCKET 等系统资源)
  • 了解 Bloom 过滤器等实用数据结构的应用场景

原始笔记来源: hhjt/c++11learn.cpp

更多推荐