Search code examples
c++visual-c++clipboard

How to set HTML Unicode text to clipboard in VC++?


I am a newbie to C++. I want to get the content of the clipboard, which might contain Unicode chars, append a div tag with some content formatted in HTML and set that back to clipboard.

I have achieved successfully in getting the content and appending it. But could not set it back to the clipboard as an HTML text. I have achieved setting as simple text. Here is my code:

#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;
}

I did try using the code described here and read the doc here. But I couldn't make it work. What I tried could be way off track or completely wrong.

Update: The code below is what I tried after the modifications suggested by Cody Gray to the original code presented 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;
}

This code compiles successfully, But I get the following error at SetClipboardData : HEAP[Project1.exe]: Heap block at 007A8530 modified at 007A860A past requested size of d2 Project1.exe has triggered a breakpoint.

Please guide me on how to proceed. I am using Visual Studio Express 2012 on Windows 8. Thanks.


Solution

  • This is the function I came up with the help of Jochen Arndt at codeproject.com. Hope this helps somebody. Here is a complete working code, if you are interested in checking this out.

    It still has one problem. That is when pasted to onenote alone, it pastes gibberish after a anchor tag. It does not happen with Word, PowerPoint or Excel. And it does not have this problem for normal English language texts. If you have a solution for this, please do let me know. The problem seems to be with OneNote. Not with the code.

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