如何在VC ++中将HTML Unicode文本设置为剪贴板?

时间:2013-04-12 04:17:55

标签: c++ visual-c++ clipboard

我是C ++的新手。我想获取剪贴板的内容,其中可能包含Unicode字符,附加一个div标签,其中包含一些格式为HTML的内容并将其设置回剪贴板。

我已成功获取内容并附加内容。但无法将其作为HTML文本重新设置回剪贴板。我已经设置了简单的文本。这是我的代码:

#include <shlwapi.h>
#include <iostream>
#include <conio.h>
#include <stdio.h>

using namespace std;

wstring getClipboard(){
    if (OpenClipboard(NULL)){
        HANDLE clip = GetClipboardData(CF_UNICODETEXT);
        WCHAR * c;
        c = (WCHAR *)clip;
        CloseClipboard();
        return (WCHAR *)clip;
    }
    return L"";
}

bool setClipboard(wstring textToclipboard)
{
    if (OpenClipboard(NULL)){
        EmptyClipboard();
        HGLOBAL hClipboardData;
        size_t size = (textToclipboard.length()+1) * sizeof(WCHAR);
        hClipboardData = GlobalAlloc(NULL, size);
        WCHAR* pchData = (WCHAR*)GlobalLock(hClipboardData);
        memcpy(pchData, textToclipboard.c_str(), size);
        SetClipboardData(CF_UNICODETEXT, hClipboardData);
        GlobalUnlock(hClipboardData);
        CloseClipboard();
        return true;
    }
    return false;
}

int main (int argc, char * argv[])
{
   wstring  s =  getClipboard();
   s += std::wstring(L"some extra text <b>hello</b>");
   setClipboard(s);
   getch();
   return 0;
}

我确实尝试使用here描述的代码并阅读文档here。但我无法使其发挥作用。我尝试的可能是偏离轨道或完全错误。

更新:以下代码是我在Cody Gray对原始代码here建议修改后尝试的代码:

bool CopyHTML2(WCHAR *html ){

    wchar_t *buf = new wchar_t [400 + wcslen(html)];
    if(!buf) return false;

    static int cfid = 0;
    if(!cfid) cfid = RegisterClipboardFormat("HTML Format");


        // Create a template string for the HTML header...
    wcscpy(buf,
        L"Version:0.9\r\n"
        L"StartHTML:00000000\r\n"
        L"EndHTML:00000000\r\n"
        L"StartFragment:00000000\r\n"
        L"EndFragment:00000000\r\n"
        L"<html><body>\r\n"
        L"<!--StartFragment -->\r\n");

    // Append the HTML...
    wcscat(buf, html);
    wcscat(buf, L"\r\n");
    // Finish up the HTML format...
    wcscat(buf,
        L"<!--EndFragment-->\r\n"
        L"</body>\r\n"
        L"</html>");

    wchar_t *ptr = wcsstr(buf, L"StartHTML");
    wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
    *(ptr+10+8) = L'\r';

    ptr = wcsstr(buf, L"EndHTML");
    wsprintfW(ptr+8, L"%08u", wcslen(buf));
    *(ptr+8+8) = '\r';

    ptr = wcsstr(buf, L"StartFragment");
    wsprintfW(ptr+14, L"%08u", wcsstr(buf, L"<!--StartFrag") - buf);
    *(ptr+14+8) = '\r';

    ptr = wcsstr(buf, L"EndFragment");
    wsprintfW(ptr+12, L"%08u", wcsstr(buf, L"<!--EndFrag") - buf);
    *(ptr+12+8) = '\r';

    // Open the clipboard...
    if(OpenClipboard(0)) {
        EmptyClipboard();
        HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, wcslen(buf)+4);
        wchar_t *ptr = (wchar_t *)GlobalLock(hText);
        wcscpy(ptr, buf);
        GlobalUnlock(hText);
        SetClipboardData(cfid, hText);
        CloseClipboard();
        GlobalFree(hText);
    }

    // Clean up...
    delete [] buf;
    return true;
}

此代码编译成功,但我在SetClipboardData上收到以下错误:HEAP[Project1.exe]: Heap block at 007A8530 modified at 007A860A past requested size of d2 Project1.exe has triggered a breakpoint.

请指导我如何继续。我在Windows 8上使用Visual Studio Express 2012.谢谢。

3 个答案:

答案 0 :(得分:1)

您的ANSI(窄)和Unicode(宽)字符串不匹配。

wcscpy函数不同,w函数中的wsprintf不代表“宽”,它代表“Windows”。它是Win32 API的一部分,而不是C运行时库。所有使用字符串的Win32 API函数都有两个版本,一个后缀为A,后者处理ANSI字符串,另一个后缀为W,处理宽字符串。标头用宏隐藏了所有这些内容。我将更详细地解释所有这些here - 推荐阅读。

无论如何,这里的简单修复是显式调用该函数的宽变量,因为你在其他地方正确使用宽字符串。拨打wsprintf的所有电话都是这样的:

wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';

或者,您可以使用C运行时库提供的swprintf函数而不是Win32版本。这个功能就像您在其他地方使用的wcsstrwcscpy功能一样。名称中的w表示“宽”。这一系列功能的文档是here

另请注意,使用字符或字符串文字时,它们也需要是宽字符。您可以通过在L前添加它们来实现这一目标。你在某些地方这样做,但却错过了其他地方。确保你做到一致。

但编译器应该警告你所有这些。您只需要确保提高警告级别,不要忽略任何警告。还要确保为项目全局定义UNICODE_UNICODE预处理器符号。这将确保您始终调用Unicode / wide版本的函数。虽然这应该是所有新项目的默认值。

答案 1 :(得分:0)

这是我在Jochen Arndt at codeproject.com的帮助下提出的功能。希望这有助于某人。 Here is a complete working code,如果您有兴趣查看此内容。

它还有一个问题。这就是当单独粘贴到onenote时,它会在锚标记之后粘贴乱码。 Word,PowerPoint或Excel不会发生这种情况。它对于普通的英语语言文本没有这个问题。如果您有解决方案,请告诉我。问题似乎与OneNote有关。没有代码。

bool setClipboard(LPCWSTR lpszWide){
    int nUtf8Size = ::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, NULL, 0, NULL, NULL);
    if (nUtf8Size < 1) return false;

    const int nDescLen = 105;
    HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, nDescLen + nUtf8Size);
    if (NULL != hGlobal)
    {
        bool bErr = false;
        LPSTR lpszBuf = static_cast<LPSTR>(::GlobalLock(hGlobal));
        LPSTR lpszUtf8 = lpszBuf + nDescLen;
        if (::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, lpszUtf8, nUtf8Size, NULL, NULL) <= 0)
        {
            bErr = true;
        }
        else
        {
            LPCSTR lpszStartFrag = strstr(lpszUtf8, "<!--StartFragment-->");
            LPCSTR lpszEndFrag = strstr(lpszUtf8, "<!--EndFragment-->");
            lpszStartFrag += strlen("<!--StartFragment-->") + 2;

            int i = _snprintf(
            lpszBuf, nDescLen,
            "Version:1.0\r\nStartHTML:%010d\r\nEndHTML:%010d\r\nStartFragment:%010d\r\nEndFragment:%010d\r\n",
            nDescLen, 
            nDescLen + nUtf8Size - 1,       // offset to next char behind string
            nDescLen + static_cast<int>(lpszStartFrag - lpszUtf8), 
            nDescLen + static_cast<int>(lpszEndFrag - lpszUtf8));
        }
        ::GlobalUnlock(hGlobal);
        if (bErr)
        {
            ::GlobalFree(hGlobal);
            hGlobal = NULL;
        }

        // Get clipboard id for HTML format...
        static int cfid = 0;
        cfid = RegisterClipboardFormat("HTML Format");
        // Open the clipboard...
        if(OpenClipboard(0)) {
            EmptyClipboard();
            HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, strlen(lpszBuf)+4);
            char *ptr = (char *)GlobalLock(hText);
            strcpy(ptr, lpszBuf);
            GlobalUnlock(hText);
            ::SetClipboardData(cfid, hText);
            CloseClipboard();
            GlobalFree(hText);
        }
    }

    return NULL != hGlobal;
}

答案 2 :(得分:0)

您的问题出在所引用示例中的使用 wchar_t 而不是 char ,这使您在偏移量计算上出错。

但是,我建议您避免使用 wchar_t UNICODE 文本传输到剪贴板。实际上, UTF-8 char可以使用1到4个字节的字节序列进行编码,而Windows上的 wchar_t 是固定的2字节类型。

如您的电子邮件中引用的Microsoft文档中所述,剪贴板的内容应为UNICODE,它与剪贴板内存头中包含的字符的ASCII相同。

要在剪贴板中传输UNICODE,可以使用标准的 char C ++函数来准备发送到剪贴板的内容(例如, std :: string )。

虽然引用的示例有效,但是请在这里找到另一个使用C ++框架的代码示例,该示例实际上可以将HTML格式的UTF-8字符复制到剪贴板:

void copyHTMLtoClipboard(const std::string& html) {

std::string contextStart("Version:0.9\r\nStartHTML:0000000000\r\nEndHTML:0000000000\r\nStartFragment:0000000000\r\nEndFragment:0000000000\r\n<html><body>\r\n<!--StartFragment -->\r\n");
std::string contextEnd("\r\n<!--EndFragment -->\r\n</body></html>");

std::stringstream aux;
aux << contextStart << html << contextEnd;
std::string res = aux.str();

size_t htmlStart = 105 * sizeof(char);
size_t fragmentStart = 119 * sizeof(char);
size_t htmlEnd = res.size() * sizeof(char);
size_t fragmentEnd = htmlEnd - 35 * sizeof(char);

aux.fill('0');
aux.width(10);
aux.seekp(23);
aux << htmlStart;

aux.seekp(43);
aux.fill('0');
aux.width(10);
aux << htmlEnd;

aux.seekp(69);
aux.fill('0');
aux.width(10);
aux << fragmentStart;

aux.seekp(93);
aux.fill('0');
aux.width(10);
aux << fragmentEnd;

res = aux.str();

HGLOBAL hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, htmlEnd + sizeof(char));
LPSTR dst = (LPSTR)GlobalLock(hdst);
memcpy(dst, res.c_str(), htmlEnd);
dst[htmlEnd] = 0;
GlobalUnlock(hdst);

OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(RegisterClipboardFormat(L"HTML Format"), hdst);
CloseClipboard();

GlobalFree(hdst);

}

请注意,此代码是编译后定义的宏 _UNICODE UNICODE