You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32 C程序跨机运行时设置剪贴板数据触发无报错崩溃

剪贴板跨机运行崩溃问题修复

问题现象

循环向客户端计算机剪贴板写入指定字符串时,服务端、客户端程序在同一台机器运行时功能可正常执行,客户端程序迁移至其他独立机器运行时,程序无任何错误码直接崩溃,需要实现稳定的客户端剪贴板循环写入功能,避免运行崩溃。

原始代码

服务端代码

while (1)
{
    HANDLE buf = malloc(1048);
    r = recv(s, (char*)buf, 1048, 0);
    if (r == SOCKET_ERROR)
    {
        printf("error receiving clipboard data\n");
        goto jumpThree;
    }
    printf("%s\n", (char*)buf);
    if (strncmp((char*)buf, "test", 4) == 0)// if we got the respond we expected
    {
        break;
    }
    free(buf);
    Sleep(1000);
}

客户端原始代码

while (1)
{
               const wchar_t* strData = L"I am spamming you";

                if (OpenClipboard(0)) {
                        
                HANDLE h = GetClipboardData(CF_TEXT);
                        
                if (strncmp((char*)h, "test", 4) == 0)
                {
                    printf("TEST: %s\n", (char*)h);
                    int r = send(ClientSocket, (char*)h, 75, 0);
                    if (r == SOCKET_ERROR)
                    {
                        printf("error sending clipboard data\n");
                        goto jumpThree;
                    }
                    CloseClipboard();
                }
                        
        HGLOBAL hClipboardData;
        hClipboardData = GlobalAlloc(GMEM_DDESHARE, sizeof(WCHAR) * (wcslen(strData) + 1));
        WCHAR* pchData;
        pchData = (WCHAR*)GlobalLock(hClipboardData);
        wcscpy(pchData, strData);
        GlobalUnlock(hClipboardData);
        SetClipboardData(CF_UNICODETEXT, hClipboardData);
        CloseClipboard();
    }
    Sleep(1000);
}

排查备注:根据官方文档说明,若调用OpenClipboard()时传入NULL参数同时调用EmptyClipboard()会导致SetClipboardData()触发崩溃,原始代码未调用EmptyClipboard(),已排除该诱因;最终确认问题根源为if条件分支语句逻辑错误。

根因明细

代码中存在多处未定义行为,同机运行时内存环境巧合没有触发异常,跨机运行时环境差异直接触发崩溃:

  • 空指针访问:GetClipboardData(CF_TEXT)调用后未校验返回值,当目标机器剪贴板中不存在ANSI格式文本数据时,接口返回NULL,直接传入strncmp会触发内存访问违例导致进程直接崩溃。
  • 资源重复释放:检测到"test"字符串的if分支内提前调用了CloseClipboard(),分支执行完成后外层逻辑会再次调用CloseClipboard(),重复关闭剪贴板句柄触发未定义行为。
  • 剪贴板操作流程违规:调用SetClipboardData写入数据前未调用EmptyClipboard()取得剪贴板所有权,不符合Windows API调用规范,会导致写入失败或进程崩溃。
  • 内存越界访问:send调用硬编码发送75字节数据,未按照剪贴板返回的实际字符串长度计算发送长度,跨机器运行时内存布局差异会触发越界访问。
  • 服务端字符串越界:recv调用拿到网络数据后未手动添加字符串终止符\0,直接传入printf会读取缓冲区外的野内存。

修复后代码

服务端修复点

  • 接收数据后手动添加字符串终止符,避免打印越界
  • 修复后服务端循环代码:
while (1)
{
    char* buf = (char*)malloc(1048);
    r = recv(s, buf, 1047, 0); // 预留1字节存终止符
    if (r == SOCKET_ERROR)
    {
        printf("error receiving clipboard data\n");
        free(buf);
        goto jumpThree;
    }
    buf[r] = '\0'; // 手动补字符串终止符
    printf("%s\n", buf);
    if (strncmp(buf, "test", 4) == 0)
    {
        free(buf);
        break;
    }
    free(buf);
    Sleep(1000);
}

客户端修复点

  • 所有Win32 API调用后增加返回值校验,空指针直接跳过对应逻辑不继续执行
  • 调整CloseClipboard()调用位置,保证每次成功打开剪贴板后仅配对调用一次关闭
  • 按API规范写入剪贴板前调用EmptyClipboard()取得所有权
  • 发送网络数据时按实际字符串长度计算发送字节,删除硬编码长度
  • 访问剪贴板内存块时配对调用GlobalLock/GlobalUnlock,避免内存访问违规
  • 修复后客户端循环代码:
while (1)
{
    const wchar_t* strData = L"I am spamming you";
    int exitFlag = 0;

    if (OpenClipboard(NULL)) {
        // 读取剪贴板ANSI文本
        HANDLE hRaw = GetClipboardData(CF_TEXT);
        if (hRaw != NULL) {
            char* pText = (char*)GlobalLock(hRaw);
            if (pText != NULL) {
                if (strncmp(pText, "test", 4) == 0)
                {
                    printf("TEST: %s\n", pText);
                    size_t sendLen = strlen(pText) + 1;
                    int r = send(ClientSocket, pText, (int)sendLen, 0);
                    if (r == SOCKET_ERROR)
                    {
                        printf("error sending clipboard data\n");
                        exitFlag = 1;
                    }
                }
                GlobalUnlock(hRaw);
            }
        }

        // 写入剪贴板逻辑
        if (!exitFlag) {
            if (EmptyClipboard()) {
                size_t dataSize = (wcslen(strData) + 1) * sizeof(WCHAR);
                HGLOBAL hClipMem = GlobalAlloc(GMEM_DDESHARE, dataSize);
                if (hClipMem != NULL) {
                    WCHAR* pWrite = (WCHAR*)GlobalLock(hClipMem);
                    if (pWrite != NULL) {
                        wcscpy(pWrite, strData);
                        GlobalUnlock(hClipMem);
                        SetClipboardData(CF_UNICODETEXT, hClipMem);
                    }
                }
            }
        }

        // 统一关闭剪贴板,避免重复调用
        CloseClipboard();
    }

    if (exitFlag) {
        goto jumpThree;
    }
    Sleep(1000);
}

内容的提问来源于stack exchange,提问作者Y K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 08:36:17