尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

VC++ Windows监控系统开发:截图、通信与静态部署

VC++ Windows监控系统开发:截图、通信与静态部署 简介这是一份基于Visual C开发的远程监控与控制软件完整源码包面向C初学者、Windows桌面应用开发者及网络编程学习者用于深入理解远程桌面类工具的核心实现机制。资源包含RemoteAdmin.exe可执行程序及配套源代码涵盖MFC界面模块、TCP/IP网络通信层、屏幕捕获与键盘鼠标模拟等关键功能逻辑适合开展远程运维工具二次开发或课程设计实践。压缩包为ZIP格式大小757KB虽未提供具体文件列表但根据典型VC项目结构应含.cpp/.h源文件、资源脚本及工程配置文件分别承担业务逻辑、接口定义与UI资源管理职责。目前已有474人学习下载读者可直接编译调试、分析多线程网络连接模型、研究屏幕图像压缩传输策略并借鉴其权限控制与双向指令交互设计思路是掌握Windows平台远程控制技术落地的优质教学级案例。1. 这不是“远程控制软件”而是一套基于 Visual C 的 Windows 端监控系统开发范式当你在资源站看到visual c vc远程监控软件 源代码.zip这个压缩包名时第一反应可能是“能直接运行的远程桌面工具”——但实际它大概率是一份面向企业内网或工业现场的轻量级监控客户端源码工程使用 MFC 或 Win32 API 编写核心功能聚焦于采集本机摄像头/屏幕/进程/网络连接状态通过 TCP/UDP 或 HTTP 协议上报至中心服务端并支持基础指令下发如截图、录屏启停、进程终止。它不依赖第三方 SDK不打包 WebRTC 或 FFmpeg 二进制而是用 GDI 截图、WMI 查询进程、Winsock 实现通信——这意味着你拿到的不是开箱即用的成品而是一个可深度定制、可嵌入到自有运维平台中的Windows 原生监控模块参考实现。适合需要自主可控、低延迟、免依赖部署的 IT 运维工程师、工控系统集成商以及正在学习 VC 网络编程与系统 API 调用的中级开发者。它不解决公网穿透问题也不提供手机 App但把 Windows 平台下“监控数据从哪来、怎么传、传什么”这三件事用标准 VC 工程结构讲清楚了。2. 解压后必须确认的 4 类文件结构与编译环境匹配逻辑拿到.zip包解压后目录结构往往暴露了它的技术代际和构建约束。常见组合有三类MFC 对话框工程含resource.hxxxDlg.cpp、ATL 服务型工程含ServiceMain.cppInstallUtil.cpp、纯 Win32 控制台工程含main.cppCapture.cpp。无论哪种都需先验证其与本地 Visual Studio 版本的兼容性——这不是简单“打开.sln 就能编译”的事。2.1 识别工程类型与 Visual Studio 版本映射关系打开.sln文件用记事本查看首行例如Microsoft Visual Studio Solution File, Format Version 12.00对应 VS 2012Format Version 14.00是 VS 2015Format Version 16.00是 VS 2019。若版本高于本地安装环境VS 会提示“需要升级”此时不能盲目点“确定”——因为升级会修改项目文件可能破坏原始逻辑。更稳妥的做法是用对应版本的 VS 打开或降级处理。例如若工程为 VS 2010Format Version 11.00而你只有 VS 2022则需手动修改.vcxproj中PlatformToolset标签值为v143VS 2022 工具集同时将WindowsTargetPlatformVersion改为10.0而非原始的7.0或8.1否则会报错error MSB8036: The Windows SDK version 8.1 was not found。提示不要试图用 VS 2022 直接编译标称“VC6.0”或“VC2008”的工程。这类老工程大量使用#include afxwin.h且未定义_CRT_SECURE_NO_WARNINGS强行编译会导致数百个C4996警告及C2065: LPCTSTR : undeclared identifier错误。正确做法是在 VS 2019 或更早版本中安装对应旧版工具集如v140_xp并在项目属性 → 通用属性 → 平台工具集中选择它。2.2 检查stdafx.h与预编译头配置一致性绝大多数 VC MFC 工程依赖stdafx.h作为预编译头。若解压后发现该文件缺失或内容为空编译时会出现fatal error C1010: unexpected end of file while looking for precompiled header。此时需确认两点1项目属性 → 配置属性 → C/C → 预编译头 → 预编译头选项是否设为使用预编译头Use2主.cpp文件如xxxDlg.cpp第一行是否为#include stdafx.h且无空行或注释干扰。若工程明确不需要预编译头如某些 Win32 控制台工程则应将预编译头选项改为不使用预编译头Not Using并删除所有#include stdafx.h行。否则 VS 会强制要求该头文件存在。2.3 验证第三方依赖库路径是否可重定位源码中常出现类似#pragma comment(lib, ws2_32.lib)或#pragma comment(lib, wmiutils.lib)的显式链接指令这是合法的。但若看到#pragma comment(lib, ..\\lib\\cv240.lib)或#include ..\\include\\opencv2\\core.hpp则说明工程绑定了绝对路径下的 OpenCV 或其他库。此时必须找到压缩包内是否附带lib/和include/文件夹若没有需自行下载对应版本注意 x86/x64 匹配在项目属性 → 配置属性 → 链接器 → 常规 → 附加库目录中填入本地路径如$(ProjectDir)..\opencv\build\x64\vc16\lib同时在 链接器 → 输入 → 附加依赖项 中补全opencv_core450.lib opencv_imgproc450.lib等具体文件名版本号需与实际一致。2.4 查看resource.h与对话框资源 ID 的冲突风险MFC 工程的界面元素按钮、编辑框ID 定义在resource.h中格式如#define IDC_CAPTURE_BTN 1001。若多个控件 ID 重复或 ID 值超出0x0000–0xFFFF范围运行时可能触发ASSERT(FALSE)或界面控件无法响应。检查方法用 VS 的“资源视图”展开 Dialog 节点右键每个控件 → 属性 → ID确认其值与resource.h中定义一致若发现IDC_STATIC被误用于按钮需手动改为IDC_CAPTURE_BTN并同步更新resource.h。文件类型典型位置必须验证项失败表现.sln根目录Format Version与 VS 版本匹配“此解决方案无法在当前版本中打开”.vcxprojxxx.vcxprojPlatformToolset和WindowsTargetPlatformVersion正确MSB8036SDK 错误stdafx.hstdafx.h是否被主.cpp文件第一行包含C1010预编译头错误resource.hresource.h所有控件 ID 在范围内且不重复界面控件点击无响应、ASSERT 断言3. 用 Visual C 实现远程监控核心功能的最小可行代码路径即使源码工程结构完整若不了解其核心监控逻辑的编码范式仍难以二次开发。以下以“本地屏幕截图并发送至服务端”为例还原一个典型 VC 远程监控模块的实现链条——它不依赖任何第三方图像库仅用 Windows GDI 和 Winsock确保在无 .NET Framework、无 VC Redistributable 的精简系统上也能运行。3.1 GDI 截图从桌面 DC 获取位图并转为内存流#include gdiplus.h #pragma comment(lib, gdiplus.lib) // 初始化 GDI Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(gdiplusToken, gdiplusStartupInput, NULL); // 获取桌面设备上下文 HDC hScreenDC GetDC(NULL); HDC hMemoryDC CreateCompatibleDC(hScreenDC); int screenWidth GetSystemMetrics(SM_CXSCREEN); int screenHeight GetSystemMetrics(SM_CYSCREEN); HBITMAP hBitmap CreateCompatibleBitmap(hScreenDC, screenWidth, screenHeight); SelectObject(hMemoryDC, hBitmap); BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 0, 0, SRCCOPY); // 将 HBITMAP 转为 Gdiplus::Bitmap 对象 Gdiplus::Bitmap* pBitmap Gdiplus::Bitmap::FromHBITMAP(hBitmap, NULL); // 写入内存流PNG 格式 IStream* pStream NULL; CreateStreamOnHGlobal(NULL, TRUE, pStream); CLSID pngClsid; GetEncoderClsid(Limage/png, pngClsid); // 此函数需自行实现见下方 pBitmap-Save(pStream, pngClsid, NULL); // 获取内存流大小并读取数据 STATSTG stg; pStream-Stat(stg, STATFLAG_NONAME); BYTE* pData new BYTE[stg.cbSize.LowPart]; ULARGE_INTEGER ulZero {0}; pStream-Seek(ulZero, STREAM_SEEK_SET, NULL); pStream-Read(pData, stg.cbSize.LowPart, NULL); // 清理资源 delete[] pData; pStream-Release(); pBitmap-Dispose(); DeleteObject(hBitmap); DeleteDC(hMemoryDC); ReleaseDC(NULL, hScreenDC); Gdiplus::GdiplusShutdown(gdiplusToken);逻辑说明这段代码绕过了CImage或CxImage等封装类直接调用 GDI 底层 API。关键点在于CreateStreamOnHGlobal创建内存流避免文件 I/OGetEncoderClsid函数需自行编写其作用是根据 MIME 类型如image/png查询 Windows 图像编码器 CLSID这是 GDI 保存图片的必要步骤。若省略此步Save()会失败并返回GenericError。3.1.1GetEncoderClsid函数实现必须补全int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT num 0; // number of image encoders UINT size 0; // size of the image encoder array in bytes Gdiplus::ImageCodecInfo* pImageCodecInfo NULL; Gdiplus::GetImageEncodersSize(num, size); if (size 0) return -1; pImageCodecInfo (Gdiplus::ImageCodecInfo*)(malloc(size)); if (pImageCodecInfo NULL) return -1; Gdiplus::GetImageEncoders(num, size, pImageCodecInfo); for (UINT j 0; j num; j) { if (wcscmp(pImageCodecInfo[j].MimeType, format) 0) { *pClsid pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return j; } } free(pImageCodecInfo); return -1; }3.2 Winsock 发送构造 TCP 数据包并添加长度头截图数据不能直接 send()必须按协议约定封装。常见做法是前 4 字节为数据总长度小端序后续为 PNG 二进制流。服务端据此判断接收完成。#include winsock2.h #pragma comment(lib, ws2_32.lib) // 初始化 Winsock WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), wsaData); // 连接服务端假设 IP 192.168.1.100端口 8080 SOCKET sock socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addr; addr.sin_family AF_INET; addr.sin_port htons(8080); addr.sin_addr.s_addr inet_addr(192.168.1.100); connect(sock, (sockaddr*)addr, sizeof(addr)); // 构造带长度头的数据包 DWORD dataLen stg.cbSize.LowPart; BYTE* packet new BYTE[dataLen 4]; memcpy(packet, dataLen, 4); // 前4字节存长度 memcpy(packet 4, pData, dataLen); // 后续存图片数据 // 发送 int sent send(sock, (char*)packet, dataLen 4, 0); if (sent SOCKET_ERROR) { printf(Send failed: %d\n, WSAGetLastError()); } closesocket(sock); WSACleanup(); delete[] packet;参数说明htons(8080)将主机字节序转为网络字节序send()第四个参数0表示阻塞模式适合调试若需异步发送应改用WSASend()并设置事件通知。SOCKET_ERROR判断不可省略否则网络断开时程序会卡死。3.3 服务端接收验证用 Python 快速搭建测试端为验证 VC 客户端是否正常工作无需部署完整服务端可用 Python 快速监听import socket import struct server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((0.0.0.0, 8080)) server_socket.listen(1) print(Waiting for connection...) conn, addr server_socket.accept() print(fConnected from {addr}) # 先读4字节长度 length_bytes conn.recv(4) if len(length_bytes) 4: print(Invalid length header) exit() data_len struct.unpack(I, length_bytes)[0] # 小端序解析 # 再读取指定长度数据 image_data b while len(image_data) data_len: chunk conn.recv(min(4096, data_len - len(image_data))) if not chunk: break image_data chunk # 保存为 PNG 文件 with open(received.png, wb) as f: f.write(image_data) print(fReceived {len(image_data)} bytes, saved to received.png) conn.close() server_socket.close()注意Python 使用struct.unpack(I, ...)显式指定小端序与 VC 中memcpy(packet, dataLen, 4)的内存布局严格一致。若 VC 端用htonl()转为大端序则 Python 端需改为I。4. 避免Microsoft Visual C Redistributable依赖的 3 种静态链接方案很多 VC 远程监控程序运行时报错MSVCP140.dll not found或VCRUNTIME140.dll is missing本质是目标机器未安装对应版本的 Microsoft Visual C Redistributable。虽然用户可手动下载安装但在企业批量部署或嵌入式场景中更可靠的做法是让 EXE 自包含运行时。以下是三种经生产验证的静态链接方案按推荐度排序4.1 方案一项目属性中启用/MT静态链接最常用在 Visual Studio 中右键项目 → 属性 → 配置属性 → C/C → 代码生成 → 运行时库将值从MD动态链接 DLL改为MT静态链接 LIB。对 Debug 版本选MTd。效果生成的 EXE 体积增加 1~2MB但不再依赖msvcp140.dll等文件。限制仅适用于 C 运行时CRT和 C 标准库如std::string,std::vector不包含 MFC 库。若工程使用 MFC需额外设置。提示切换/MT后若编译报错LNK2005: _DllMain12 already defined说明工程中混用了/MD编译的第三方.lib。此时需重新编译所有依赖库或改用/MD并随 EXE 分发对应 Redistributable。4.2 方案二MFC 静态链接针对 MFC 对话框工程若工程类型为 MFC还需单独处理 MFC 库。在项目属性 → 配置属性 → 常规 → 使用 MFC 的方式从在共享 DLL 中使用 MFC改为在静态库中使用 MFC。效果EXE 体积再增 3~5MB彻底摆脱mfc140u.dll依赖。注意此设置与/MT必须同时启用否则链接器会报LNK2005: _AfxGetModuleState冲突。4.3 方案三剥离调试符号与合并资源减小体积静态链接后 EXE 变大可通过以下操作优化项目属性 → 配置属性 → 链接器 → 调试 → 生成调试信息 → 设为否链接器 → 高级 → 生成映射文件 →否资源编译时将图标、字符串表等资源统一打包进.rc文件避免分散加载。最终生成的 EXE 可用Dependency Walkerdepends.exe验证若msvcp140.dll、vcruntime140.dll、mfc140u.dll等条目消失即表示静态链接成功。方案修改位置体积增量适用工程类型验证方法/MT静态 CRTC/C → 代码生成 → 运行时库1~2MB所有 VC 工程Dependency Walker 查无vcruntime*.dllMFC 静态链接常规 → 使用 MFC 的方式3~5MBMFC 工程Dependency Walker 查无mfc*.dll调试符号剥离链接器 → 调试 → 生成调试信息-0.5~1MB所有工程dumpbin /headers xxx.exe | findstr debug返回空5. 用 Process Explorer 定位远程监控程序的内存与句柄泄漏点即使编译通过、功能正常VC 远程监控程序在长时间运行后可能出现 CPU 占用飙升、内存持续增长、截图失败等问题。此时不能只看源码逻辑而要借助 Windows 原生工具直接观测进程行为。Process Explorer微软官方免费工具是比任务管理器更深入的诊断入口。5.1 监控 GDI 对象泄漏截图功能的隐形杀手MFC 或 Win32 程序频繁调用CreateCompatibleBitmap、CreateDC后未调用DeleteObject、DeleteDC会导致 GDI 对象数突破默认上限10,000。现象是截图功能逐渐变慢最终CreateCompatibleBitmap返回NULL。定位步骤1启动 Process Explorer找到目标进程2右键 → Properties → Performance 页面观察 “GDI Objects” 曲线是否持续上升3切换到 “Handles” 页面按Type列排序查找大量Bitmap、DC类型句柄4双击某个Bitmap句柄在下方窗口查看其创建堆栈需提前在 Options → Configure Symbols 设置 PDB 路径。注意若堆栈显示xxxDlg.cpp中某次CreateCompatibleBitmap调用后未配对DeleteObject即为泄漏点。修复方式是在BitBlt后立即DeleteObject(hBitmap)而非等到函数末尾统一清理。5.2 检测网络句柄堆积TCP 连接未关闭导致端口耗尽远程监控程序若每次截图都新建 socket 连接却不调用closesocket()会导致TCP Close_Wait状态连接堆积最终socket()调用失败。定位步骤1在 Process Explorer 的 “TCP” 页面筛选目标进程2观察 “State” 列中是否有大量CLOSE_WAIT或TIME_WAIT3右键某条连接 → Properties → Stack Trace确认connect()调用位置4检查源码中send()后是否遗漏closesocket()或异常分支如if (sent SOCKET_ERROR)未执行清理。5.3 分析内存碎片new/delete不匹配引发的堆损坏若程序使用new BYTE[1024*1024]分配大块内存却用free()释放或多次delete[]同一指针会导致堆损坏表现为随机崩溃或Heap Corruption异常。定位步骤1在 Visual Studio 中启用页堆Page Heap以管理员身份运行gflags -i yourapp.exe hpa2重启程序复现崩溃3VS 会自动捕获堆损坏异常调用堆栈指向非法delete行4修复原则new[]必须配delete[]malloc配free且每个指针仅释放一次。提示对于pData这类动态分配的内存建议统一用std::vectorBYTE管理利用 RAII 自动释放从根本上规避手动new/delete错误。本文还有配套的精品资源点击获取
返回列表