2

我是 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;
}

我确实尝试使用此处描述的代码并阅读此处的文档。但我无法让它工作。我尝试的可能是偏离轨道或完全错误的。

更新:下面的代码是我在 Cody Gray 建议的对此处提供的原始代码进行修改后尝试的代码:

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。谢谢。

4

3 回答 3

1

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

wcscpy函数不同的是wwsprintf函数中的 不代表“wide”,它代表“Windows”。它是 Win32 API 的一部分,而不是 C 运行时库。所有处理字符串的 Win32 API 函数都有两个版本,一个以A处理 ANSI 字符串为后缀,另一个以W处理宽字符串为后缀。标题用宏对你隐藏了所有这些。我在这里更详细地解释了所有这些——推荐阅读。

无论如何,这里的简单解决方法是显式调用该函数的宽变体,因为您在其他任何地方都正确使用了宽字符串。使所有调用wsprintf看起来像这样:

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

或者,您可以使用swprintfC 运行时库提供的函数而不是 Win32 版本。这与您在其他地方使用的wcsstrand函数一样工作。wcscpy名字w中的意思是“宽”。这一系列功能的文档在这里

另请注意,当您使用字符或字符串文字时,它们也需要是宽字符。您可以通过在它们前面加上L. 您在某些地方这样做,但在其他地方却错过了。确保你始终如一地这样做。

不过,编译器应该警告您所有这些。您只需要确保提高警告级别并且不要忽略任何警告。还要确保为您的项目全局定义了UNICODE_UNICODE预处理器符号。这将确保您始终调用 Unicode/宽版本的函数。尽管这应该是所有新项目的默认设置。

于 2013-04-12T05:06:29.823 回答
0

这是我在 codeproject.com 的 Jochen Arndt的帮助下提出的功能。希望这可以帮助某人。这是一个完整的工作代码,如果您有兴趣检查一下。

它仍然存在一个问题。也就是说,当单独粘贴到 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;
}
于 2013-05-05T10:21:03.837 回答
0

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

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

正如您的电子邮件中提到的 Microsoft 文档中所述,剪贴板的内容应为 UNICODE,恰好与剪贴板内存标题中包含的字符的 ASCII 相同。

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

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

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);

}

请注意,此代码是在定义宏_UNICODEUNICODE时编译的。

于 2020-03-10T15:26:43.917 回答