2016-11-17 15 views
-1

я не знаю, почему этот код не работает должным образом:Копировать юникод строку в буфер обмена не работает

#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); 
} 
+0

Аргумент, определяющий сумму, выделяемую в 'GlobalAlloc', выделяет количество ** байтов **, а не количество символов (которые в вашем случае имеют ширину 2 байта). Вам нужно выяснить, сколько байтов выделить. Во-вторых, вы не конвертируете широкие строки в строки ANSI и наоборот, просто литье. Это '(LPCSTR)' cast не будет работать.Если вы делаете что-то подобное в некоторых других частях вашего кода, вы не показываете нам, то прекратите делать это, так как ваша программа будет обречена на провал. – PaulMcKenzie

+1

Что такое возвращаемое значение из 'SetClipboardData()'? Вы видите мой комментарий ниже и ссылку на документацию API? Попробуйте пропустить EmptyClipboard(), если вы используете дескриптор окна NULL. –

+1

Вы не являетесь большим поклонником чтения [документации] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa366574.aspx): * «Следующие значения устарели, но предоставляются для совместимости с 16-разрядными Windows. ** Они игнорируются. ** 'GMEM_DDESHARE' [...]" *. Кроме того, в вашем коде буквально проверяется ошибка. Почему мы должны угадать, какой API-вызов терпит неудачу? 'GlobalLock' следует вызывать только в подвижной памяти (при запросе фиксированной памяти). Он используется для преобразования дескриптора в указатель, но вы уже передаете ему действительный указатель памяти. Нехорошо. – IInspectable

ответ

4

Изменить этот раздел:

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

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

Выделяют 2 * количество байт для WCHAR. Вместо char, WCHAR. Вместо strcpy, wcscpy. Вместо CF_TEXT, CF_UNICODETEXT.

+1

Из документации: Если приложение вызывает OpenClipboard с hwnd, установленным в NULL, ** EmptyClipboard устанавливает для владельца буфера обмена значение NULL; это приводит к сбою SetClipboardData. ** https://msdn.microsoft.com/en-us/library/windows/desktop/ms649048(v=vs.85).aspx –

+1

IOW, не вызывайте 'EmptyClipboard () '. Или, если вы это сделаете, вызовите 'OpenClipboard()' с допустимым HWND. –

+1

Я бы использовал 'sizeof (wchar_t)' (или был совместим с вашим ответом 'sizeof (WCHAR)') вместо магической константы '2', но это всего лишь стиль. –

1
strcpy(pchData, LPCSTR(strData)); 

не является хорошим выбором для данных UTF16.

Используйте wcscpy и снимите отливку.

+0

@BPL Из-за недостающей памяти, как сказал вам другой ответчик (сначала не видел). Но, тем не менее, вам это тоже нужно. – deviantfan

3

Вы должны применить следующие изменения, чтобы исправить код:

if (OpenClipboard(0)) { 

Вам необходимо предоставить действительный дескриптор окна, чтобы взять на себя ответственность в буфер обмена. Требуется владение, чтобы вы могли изменять содержимое буфера обмена.

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

Есть 2 ошибки, которые необходимо исправить. Как описано в разделе Memory and the Clipboard, при размещении объекта в буфер обмена память должна быть назначена с помощью функции GlobalAlloc с флагом GMEM_MOVEABLE. GMEM_DDESHARE, с другой стороны, игнорируется и без передачи каких-либо флагов по умолчанию используется значение GMEM_FIXED. Это вернет указатель на память, и последующее прохождение его до GlobalLock.

Во-вторых, для этого вызова API требуется размер в байтах. Единица кода Unicode в Windows - 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.


Ссылки:

  • Исчерпывающая документация доступна в MSDN под Clipboard (Windows).