The first thing I want to say is my English is pretty bad so if my description become a chunk of nonsense text please comment so I can try to clarify my question.
The situation
I'm trying to access clipboard and store all of it's data from all formats possible. I listen to WM_CLIPBOARDUPDATE message then loop through all the format available, get the data for each of the format, get the size then allocate a new memory for them. My googling tell me to use the GlobalSize() function to do that. There the problem arise.
The environment
I am developing a Win32 desktop application using C++ on Visual C++ 2017 version 4.7.02556. I am using Windows 10 64 bits if that matter.
Reproducing the error
Here is my function used to process Windows messages, I remove other message like WM_CREATE or WM_DESTROY because they don't relate to the problem. The consoleOutput() function is used for debugging and they works just fine.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
switch (message){
case WM_CLIPBOARDUPDATE: {
try {
if (OpenClipboard(hWnd)){
UINT uFormat = EnumClipboardFormats(0); // Get the first clipboard format available
HANDLE hContent;
while (uFormat) { //Iterate through all formats
hContent = GetClipboardData(uFormat); // Get the clipboard data of that format
if (hContent) {
SIZE_T size = GlobalSize(hContent); // Get the size of the data I just got
//ERROR
}
uFormat = EnumClipboardFormats(uFormat); // Get the next format
}
}
CloseClipboard();
}
catch (int error) {
CloseClipboard(); // Attempt to close clipboard if thing went wrong
consoleOutput(GetLastError());
throw;
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
The code works fine with everything I tried to copy from text to some random files/directories. But my program gave me an error when I tried to copy a image, for example:
Open an image using MS Paint, select any portion of the image and copy it and the error appears.
Open an image using Picasa, right click and select copy and the error appears.
Press PrntScr button and the error appears.
The error is throwed at SIZE_T size = GlobalSize(hContent); line, and it is either:
Critical error detected c0000374 YClipboard.exe has triggered a breakpoint.
or
Exception thrown at 0x77E17ECC (ntdll.dll) in YClipboard.exe: 0xC0000005: Access violation reading location 0xDD46116F.
The error is not persistent, most of the time it appeared right in the first copy, sometime I can copy the image, but do it multiple times with that same image cause the error. Above all, only attempt to copy "image" data cause the problem.
What I have tried so far
I am a novice about developing desktop app, and I my searching don't find the problem anywhere else. When I peek at the clipboard data using a third-party app, I found out the error only appear when my program met the CF_BITMAP or CF_METAFILEPICT format, and (not so) coincidentally both of those formats have a memory of size 0. Still I am unable to fix my problem.
What I need
Someone please enlighten me about this problem: what is the problem, the cause of it, how to fix it or just a suggestion about another method that help me read the size of clipboard data to replace GlobalSize() function.
Thanks in advance.
Not all clipboard formats present an HGLOBAL that you can use with functions like GlobalSize().
For example, CF_BITMAP presents a handle to a bitmap; an HBITMAP that you can use with functions like GetObject().
You'll need to handle those formats specially. See the Standard Clipboard Formats list on MSDN for more information.
I use the following function to send message to application. It seems to work find for a 32bit application but doesn't work for a 64bit application (the 64bit application does not seem to get any message). What is wrong and how can I fix it? Thank you.
void MyTest::SendCmd(HWND hwnd, QString cmd)
{
COPYDATASTRUCT data;
data.dwData = FIXHEADER;
data.cbData = cmd.size()+1;
data.lpData = cmd.toLocal8Bit().data();
LPARAM lpdwResult;
LRESULT err = SendMessageTimeout(hwnd, WM_COPYDATA, 0, (LPARAM)&data, SMTO_ABORTIFHUNG, 2000, &lpdwResult);
}
//FIXHEADER is a fixed hex value that the application checks against to make sure the message is sent intentional for it.
You need to compile your application in 64-bit mode otherwise this cannot work and 64-bit handles are truncated making them invalid.
Some reading for you:
https://learn.microsoft.com/en-us/windows/desktop/winauto/32-bit-and-64-bit-interoperability
The code :
HWND hWnd; //this is a window handle of current process
/*
*here is executed in a dll in current process , and it will hang
*when hWnd is a window which is created by current process.
*/
WCHAR lpwWndName[1000];
GetWindowText(hWnd,lpwWndName,1000);
I had read the MSDN :
1,When hWnd is created by another process , GetWindowText will get the title from a special space;
2,When hWnd is created by current process ,GetWindowText will send a WM_GETTEXT to hWnd,this method may hang if the hWnd doesn't response WM_GETTEXT.
How to resove this issue ? Thank you .
The reason of this issue is excute GetWindowText() in the message process function.
message process function()
switch(code)
{
case WM_COMMAND:
GetWindowText(hWnd);
break;
case WM_GETTEXT:
break;
}
if hWnd is created by current process , GetWindowText() will send a WM_GETTEXT to the window , and wait for the result,but the WM_GETTEXT will be processed after process WM_COMMAND.And then it deadlocks.
Say that I want to send a message to my WndProc, but I want to also send an integer.
SendMessage (m_hWnd, WM_DISPLAYCHANGE, NULL, int?);
My WndProc will receive it right? Then I want to send that lParam(integer) to a function.
case WM_DISPLAYCHANGE:
{
pD2DResources->OnRender(lParam);
}
break;
How do I send an integer as a lParam or wParam and then resend that integer to a function as a parameter?
LPARAM and WPARAM are just a typedef for long. So an int can be sent in as it is.
SendMessage(m_hWnd, WM_DISPLAYCHANGE, NULL, (LPARAM)yourInt)
In your wnd proc you could do
pD2DResource->Render((int)lParam)
As you are sending these custom information as a part of standard windows messages (message number below WM_USER) you should be careful to not pass the LPARAM values you receive in your window proc directly to DefWindowProc (default window proc) - because yourInt might have a special meaning for that particular standard windows message. Either you could pass in a fixed value from your window proc to the DefWindowProc or look at other ways to pass more than 4 byte of information through LPARAM/WPARAM. As SendMessage is synchronous, you could possibly pass address of a struct - just like many standard windows messages do.
Visual C++ 2012 RC, Win7
Chinese simplified
Project Properties > use multi byte character set
When I run this program, the window's title shows a single letter "S", not a whole word "Sample".
#pragma comment(linker, "/SubSystem:Windows")
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int) {
WNDCLASSW wc = { 0 };
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
wc.lpszClassName = L"MyWindowClass";
wc.lpfnWndProc = [](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg - WM_DESTROY)
return DefWindowProc(hWnd, uMsg, wParam, lParam);
else {
PostQuitMessage(0);
return HRESULT();
}
};
RegisterClassW(&wc);
CreateWindowExW(0, L"MyWindowClass", L"Sample",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, SW_SHOW, CW_USEDEFAULT, 0,
nullptr, nullptr, hInstance, nullptr);
for (MSG msg; GetMessage(&msg, nullptr, 0, 0); DispatchMessage(&msg));
}
If I use Unicode (Project Properties), keep source code unchanged, window title shows "Sample", looks correct.
If I use multi byte, in source code I use WNDCLASS = { ..., "MyWindowClass" } and RegisterClassA, keep CreateWindowExW unchanged, window title shows word "Sample", looks correct.
If I use multi byte, in source code I use CreateWindowExA("MyWindowClass", "Sample"), keep WNDCLASSW and RegisterClassW unchanged, window title shows letter "S".
What makes it show a single "S", am I doing something wrong?
Append
If I keep all unchanged, that is, use multi byte, use code shown above, window title shows letter "S".
(If you run this program and see "Sample" on window title, rather than "S", then it's more likely a specific problem on chs version of vc++ 2012 (or OS)).
The problem in your code is that you are using DefWindowProc instead of DefWindowProcW. Changing that will fix the code.
Ideally you should change your project settings to use Unicode, not multi-byte character set. This will simplify everything and you can use the macros like CreateWindowEx and RegisterClassEx instead of explicitly using the Unicode / ANSI versions as you are.
As others have said, this is a mismatch between character sets.
You should ideally match character sets between all your API calls that interact with each other. So if you use CreateWindowExW you should also use RegisterClassExW, DefWindowProcW, DispatchMessageW...
This is a very nice one, learned something new!
You need to change
return DefWindowProc(hWnd, uMsg, wParam, lParam);
to
if(IsWindowUnicode(hWnd))
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
else
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
Or even better: stick to one character encoding. At best just use RegisterClass, CreateWindowEx and so on and let the compiler take the right Unicode or ANSI function.
CreateWindowExA interprets the string as 8 bit characters. The second 8 bits of L"Sample" is zero, because its first character is 0x0053 - the L means use wide characters. So the function interprets that as a 1 character null terminated string.
I think the msdn page for RegisterClass hints at the reason for the failure here, in the remarks section it mentions how that if you use either wide character or ansi support, then it will pass internal text paramaters/message in this format (wide char/ansi). Quite possibly that's what's happening with the window title, even though we're saying use CreateWindowExA, this doesn't work as internally the Windows SDK has encoded that string as a wide character string, and the CreateWinowExA tries to output as if it was an Ansi string.
In short don't go mixing the W and A methods, unless you've a good reason for doing so, and let windows headers take care of it for you, if you want wide char support define your UNICODE macro.
In your last case your L"Sample" still remains Unicode, isn't it? You can use _T() macro, which automatically adds or removes L prefix depending on Unuicode setting of the project.
And Unicode L"Sample", as #Pete have already said, is "S\0..." in ascii, that's why only one symbol is printed.
I'm glad I found this. I was looking all over for an answer, and it seems rather difficult to find correctly with Google etc. I did find similar problems reported for specific programs, always blaming "some plug in".
It is maddening because the problem with the WndProc is nowhere near the call to CreateWindowEx nor RegisterClassEx!
BTW, I use -W suffixes explicitly, because I want to make one DLL that works for programs built either way, or overcome the non-Unicode settings of the program I'm adding to.