【问题标题】:Copy unicode string to clipboard isn't working将 unicode 字符串复制到剪贴板不起作用
【发布时间】:2016-11-17 20:58:44
【问题描述】:

我不知道为什么这段代码不能正常工作:

#define UNICODE

#include <iostream>
#include <sstream>
#include <windows.h>

void main(void)
{
    wchar_t* strData = L"CreateWindowExA";

    MessageBox(NULL, strData, L"Warning", MB_OK);

    if (OpenClipboard(0)) {
        EmptyClipboard();
        HGLOBAL hClipboardData;
        hClipboardData = GlobalAlloc(GMEM_DDESHARE,
                                     wcslen(strData) + 1);
        char* pchData;
        pchData = (char*)GlobalLock(hClipboardData);
        strcpy(pchData, LPCSTR(strData));
        GlobalUnlock(hClipboardData);
        SetClipboardData(CF_TEXT, hClipboardData);
        CloseClipboard();
    }

    MessageBox(NULL, L"Copied to Clipboard", L"Title", MB_OK);
}

【问题讨论】:

  • GlobalAlloc 中指定分配数量的参数分配字节 的数量,而不是字符的数量(在您的情况下为2 个字节宽)。您需要弄清楚要分配多少字节。其次,您不会仅通过强制转换将宽字符串转换为 ANSI 字符串,反之亦然。 (LPCSTR) 演员不会工作。如果您在代码的其他部分没有向我们展示类似的操作,请停止这样做,因为您的程序注定会失败。
  • SetClipboardData() 的返回值是多少?您是否在下面看到我的评论并链接到 API 文档?如果您使用 NULL 窗口句柄,请尝试跳过 EmptyClipboard()。
  • 您似乎不太喜欢阅读documentation“以下值已过时,但提供与 16 位 Windows 兼容。它们是忽略。 GMEM_DDESHARE [...]"。此外,您的代码中的错误检查实际上是零。为什么我们要猜测,哪个 API 调用失败了? GlobalLock 应该只在可移动内存上调用(当您请求固定内存时)。它用于将句柄转换为指针,但您已经将有效的内存指针传递给它。不好。

标签: c++ winapi unicode clipboard ollydbg


【解决方案1】:

更改此部分:

hClipboardData = GlobalAlloc(GMEM_DDESHARE, sizeof(WCHAR) * (wcslen(strData) + 1));

WCHAR* pchData;
pchData = (WCHAR*)GlobalLock(hClipboardData);
wcscpy(pchData, strData);
GlobalUnlock(hClipboardData);
SetClipboardData(CF_UNICODETEXT, hClipboardData);

WCHAR 分配2* 的字节数。代替char,使用WCHAR。代替strcpy,使用wcscpy。代替CF_TEXT,使用CF_UNICODETEXT

【讨论】:

  • 来自文档:如果应用程序在 hwnd 设置为 NULL 的情况下调用 OpenClipboard,EmptyClipboard 会将剪贴板所有者设置为 NULL;这会导致 SetClipboardData 失败。 msdn.microsoft.com/en-us/library/windows/desktop/…
  • IOW,不要打电话给EmptyClipboard()。或者,如果您这样做,请使用有效的 HWND 致电 OpenClipboard()
  • 我会使用sizeof(wchar_t)(或与您的答案一致sizeof(WCHAR))而不是魔术常数2,但这只是一种风格。
  • 在这里工作正常。只有我制作的模组是我笔记中的模组,而不是使用 EmptyClipboard()。如果我使用 CF_TEXT,它会将剪贴板上的垃圾留在剪贴板上。如果我使用 CF_UNICODETEXT,它会将正确的文本放在剪贴板上,当我粘贴到文本编辑器中时,我会得到正确的文本。
  • 不,一点也不。不知道它是什么。我说的是使用剪贴板并从中获取与我放在上面相同的信息。如果那个插件弄错了,那就是插件的问题。粘贴到记事本时是否得到正确的数据?
【解决方案2】:

您需要应用以下更改来修复您的代码:

if (OpenClipboard(0)) {

您需要提供一个有效的窗口句柄,以获得剪贴板的所有权。需要所有权,以便您可以更改剪贴板的内容。

    HGLOBAL hClipboardData;
    hClipboardData = GlobalAlloc(GMEM_DDESHARE,
                                 wcslen(strData) + 1);

有 2 个错误,需要修复。如Memory and the Clipboard 中所述,将对象放入剪贴板时,应使用带有 GMEM_MOVEABLE 标志的GlobalAlloc 函数分配内存。另一方面,GMEM_DDESHARE 被忽略,并且在不传递任何标志的情况下,调用默认使用 GMEM_FIXED。这将返回一个内存指针,并将其传递给GlobalLock 随后将失败。

其次,此 API 调用需要 字节 的大小。 Windows 中的 Unicode 代码单元是 2 个字节。你需要(wcslen(strData) + 1) * sizeof(wchar_t)

    char* pchData;
    pchData = (char*)GlobalLock(hClipboardData);
    strcpy(pchData, LPCSTR(strData));

strcpy 复制单字节单元,直到第一个 NUL 字符。使用 UTF-16LE 编码(在 Windows 中使用),您正在复制单个字符。您应该改用wcscpy,并将目的地转换为wchar_t*

    wchar_t* pchData;
    pchData = (wchar_t*)GlobalLock(hClipboardData);
    wcscpy(pchData, strData);

    SetClipboardData(CF_TEXT, hClipboardData);

由于您复制的是 UTF-16LE 编码文本,因此剪贴板格式应为 CF_UNICODETEXT


参考:

【讨论】:

    【解决方案3】:
    strcpy(pchData, LPCSTR(strData));  
    

    不是 UTF16 数据的好选择。

    使用wcscpy 并移除演员表。

    【讨论】:

    • @BPL 因为缺少内存,正如其他回答者告诉你的那样(没有先看到它)。但是,你也需要这个。
    【解决方案4】:

    我在C++ MFC 中尝试@Joseph Willcoxson 的答案,感谢您的代码,我发现第一次调用剪贴板功能有效。但是当第二次调用它抛出奇怪的异常没有特定的错误信息。经过一番搜索和测试,我发现wcscpy会出现编译错误C4996 : function may be unsafe. Consider using wcscpy_s instead.

    我发现wcscpy_s用法HERE,修改为使用wcscpy_s,除了注释掉GlobalFree(),使得剪贴板函数可以多次调用成功而不会出错:

    void toClipboardWStr(const wchar_t* strData) {
        if (OpenClipboard(0)) {
            EmptyClipboard();
            int size_m = sizeof(WCHAR) * (wcslen(strData) + 1);
            HGLOBAL hClipboardData = GlobalAlloc(GMEM_DDESHARE, size_m);
            WCHAR* pchData;
            pchData = (WCHAR*)GlobalLock(hClipboardData);
            //wcscpy(pchData, strData);
            wcscpy_s(pchData, size_m / sizeof(wchar_t), strData);
            GlobalUnlock(hClipboardData);
            SetClipboardData(CF_UNICODETEXT, hClipboardData);
            CloseClipboard();
            // if you need to call this function multiple times, I test no need to GlobalFree, or will occur error
            //GlobalFree(hClipboardData);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-21
      • 2010-10-09
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 2016-03-15
      • 1970-01-01
      相关资源
      最近更新 更多