3

I don't know why this code isn't working properly:

#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);
}
BPL
  • 9,632
  • 9
  • 59
  • 117
  • The argument that specifies the amount to allocate in `GlobalAlloc` allocates the number of **bytes**, not the number of characters (which in your case are 2 bytes wide). You need to figure out how many bytes to allocate. Second, you don't convert wide strings to ANSI strings and vice-versa by merely casting. That `(LPCSTR)` cast is not going to work. If you're doing anything like that in some other parts of your code you're not showing us, then stop doing it as your program will be doomed for failure. – PaulMcKenzie Nov 17 '16 at 21:31
  • 1
    What is the return value from `SetClipboardData()`? DId you see my comment below and link to API documentation? Try skipping EmptyClipboard() if you use a NULL window handle. – Joseph Willcoxson Nov 17 '16 at 22:41
  • 1
    You don't seem to be a big fan of reading [documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/aa366574.aspx): *"The following values are obsolete, but are provided for compatibility with 16-bit Windows. **They are ignored.** `GMEM_DDESHARE` [...]"*. Besides, there's literally zero error checking in your code. Why do we have to guess, which API call fails? `GlobalLock` should be called on movable memory only (while you request fixed memory). It is used to convert a handle into a pointer, but you pass it a valid memory pointer already. Not good. – IInspectable Nov 18 '16 at 00:07

5 Answers5

8

Change this section:

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

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

Allocate 2* number of bytes for WCHAR. Instead of char, use WCHAR. Instead of strcpy, use wcscpy. Instead of CF_TEXT, use CF_UNICODETEXT.

Joseph Willcoxson
  • 5,853
  • 1
  • 15
  • 29
  • 1
    From documentation: If an application calls OpenClipboard with hwnd set to NULL, **EmptyClipboard sets the clipboard owner to NULL; this causes SetClipboardData to fail.** https://msdn.microsoft.com/en-us/library/windows/desktop/ms649048(v=vs.85).aspx – Joseph Willcoxson Nov 17 '16 at 22:05
  • 1
    IOW, don't call `EmptyClipboard()`. Or, if you do, call `OpenClipboard()` with a valid HWND. – Joseph Willcoxson Nov 17 '16 at 22:06
  • 2
    I'd use `sizeof(wchar_t)` (or to be consistent with your answer `sizeof(WCHAR)`) instead of the magic constant `2`, but that's just a style thing. – Mark Ransom Nov 17 '16 at 22:12
  • Works fine here. Only mods I made were the ones in my notes and to not use EmptyClipboard(). If I use CF_TEXT, it leaves whatever garbage is on the clipboard on the clipboard. If I use CF_UNICODETEXT, it puts the right text on the clipboard and when I paste into a text editor, I get the right text. – Joseph Willcoxson Nov 18 '16 at 00:31
  • No, not at all. Have no clue what it is. I'm talking about using the clipboard and getting the same information out of it that I put on it. If that plugin is getting it wrong, then it's a problem with the plugin. Do you get the correct data when you paste into notepad? – Joseph Willcoxson Nov 18 '16 at 00:45
  • @JoeWillcoxson Actually you're right, it wasn't fair from me to have added the broken plugin after receiving answers related to the little snippet. Thanks for your time – BPL Nov 18 '16 at 01:14
  • It works if I use it on the same computer. But when moving to another computer (server/client) it won't work. Any suggestions? – Y K Jun 18 '22 at 12:07
  • @YK What do you mean? What call is failing? Have you called GetLastError() after the last failing call to get the reason why? – Joseph Willcoxson Jun 20 '22 at 13:30
  • @JosephWillcoxson I fixed it. The problem was that when GetClipboardData() returned NULL and then tried to do some string operations like strlen() it crashed immediately. So added an check for NULL values – Y K Jun 20 '22 at 13:44
4

You need to apply the following changes, to fix your code:

if (OpenClipboard(0)) {

You need to provide a valid window handle, to take ownership of the clipboard. Ownership is required, so that you can change the contents of the clipboard.

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

There are 2 bugs, that need to be fixed. As explained under Memory and the Clipboard, when placing an object into the clipboard, memory should be allocated by using the GlobalAlloc function with the GMEM_MOVEABLE flag. GMEM_DDESHARE, on the other hand, is ignored, and without passing any flags the call defaults to using GMEM_FIXED. This will return a memory pointer, and passing it to GlobalLock will subsequently fail.

Second, this API call requires the size in bytes. A Unicode code unit in Windows is 2 bytes. You need (wcslen(strData) + 1) * sizeof(wchar_t).

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

strcpy copies single-byte units, up to the first NUL character. With UTF-16LE encoding (as used in Windows), you are copying a single character. You should be using wcscpy instead, and cast the destination to wchar_t*:

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

    SetClipboardData(CF_TEXT, hClipboardData);

Since you copied UTF-16LE encoded text, the clipboard format should be CF_UNICODETEXT.


References:
IInspectable
  • 46,945
  • 8
  • 85
  • 181
1
strcpy(pchData, LPCSTR(strData));  

isn't a good choice for UTF16 data.

Use wcscpy and remove the cast.

deviantfan
  • 11,268
  • 3
  • 32
  • 49
  • @BPL Because the missing memory, as the other answerer told you (didn't see it first). But nonetheless, you need this too. – deviantfan Nov 17 '16 at 21:39
0

I try @Joseph Willcoxson 's answer in C++ MFC, thanks for the code, and I find that first time call clipboard function works. but when call second time it throw strange exception without specific error message. After some search and test, I find that wcscpy will get compile error C4996 : function may be unsafe. Consider using wcscpy_s instead.

And I find wcscpy_s usage HERE, modify to use wcscpy_s, besides comment out GlobalFree() make clipboard function can be called multiple times successfully without error:

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);
    }
}
yu yang Jian
  • 6,680
  • 7
  • 55
  • 80
0

I tries all of this solutions, first kinda works, but append two unreadable symbol at the end, and crash when i second time copy to clipboard.

@yu yang Jian solution crash immediately.

So a look up to my UTF-8 solution and see that as flag to GlobalAlloc i use not GMEM_DDESHARE but GMEM_MOVEABLE, and now its works perfectly fine.

So UTF-8 (char):

void CopyToClipboard(const char* buffer, size_t size) {

        //set the default size
        if (size <= 0)
            size = strlen(buffer) + 1;

        if (OpenClipboard(hWnd) {
            EmptyClipboard();
            HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, size);
            memcpy(GlobalLock(hMem), buffer, size);
            GlobalUnlock(hMem);
            SetClipboardData(CF_TEXT, hMem);
            CloseClipboard();
        }
    }

Utf-16 (wide char/unicode):

void CopyToClipboard(const wchar_t* buffer, size_t size) {

        //set the default size
        if (size <= 0)
            size = sizeof(WCHAR) * (wcslen(buffer) + 1);

        if (OpenClipboard(hWnd) {
            EmptyClipboard();
            HGLOBAL hClipboardData = GlobalAlloc(GMEM_MOVEABLE, size);
            WCHAR* pchData;
            pchData = (WCHAR*)GlobalLock(hClipboardData);
            wcscpy_s(pchData, size / sizeof(wchar_t), buffer);
            GlobalUnlock(hClipboardData);
            SetClipboardData(CF_UNICODETEXT, hClipboardData);
            CloseClipboard();
        }
    }
Kenny
  • 113
  • 9