I am writting dll with global hooks. One of the task is viewing clipboard and deleting all data from it when someone perform copy operation. Here is my callback function for window:
string test("my data");
LRESULT CALLBACK WndHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_CREATE:
nextClipboardViewer = SetClipboardViewer(windowHandler);
MessageBeep(MB_ICONINFORMATION);
break;
case WM_CHANGECBCHAIN:
if((HWND) wParam == nextClipboardViewer)
nextClipboardViewer == (HWND) lParam;
else if(nextClipboardViewer != NULL)
SendMessage(nextClipboardViewer, msg, wParam, lParam);
break;
case WM_DRAWCLIPBOARD:
if(OpenClipboard(windowHandler)) {
EmptyClipboard();
HGLOBAL hClipboardData;
hClipboardData = GlobalAlloc(GMEM_MOVEABLE, test.size() + 1);
char * pchData;
pchData = (char*)GlobalLock(hClipboardData);
memcpy(pchData, test.c_str(), test.size() + 1);
GlobalUnlock(hClipboardData);
SetClipboardData(CF_TEXT, hClipboardData);
CloseClipboard();
}
SendMessage(nextClipboardViewer, msg, wParam, lParam);
break;
case WM_DESTROY:
ChangeClipboardChain(windowHandler, nextClipboardViewer);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}
I am just trying replace information in clipboard, but this code doesn't work.
Updated: Now i am using invisible window and SetClipboardViewer for monitoring changes. But data in clipboard doesn't change.
I doubt it's really safe to change the contents of the clipboard while processing a WM_DRAWCLIPBOARD message - at the very least I'm surprised you don't trigger an infinite loop (since your calls to EmptyClipboard() and SetClipboardData() could be triggering another WM_DRAWCLIPBOARD message). Possibly the system has protection against that - I've never tried to find out - but it still feels wrong :)
Try this version, which:
Moves the clipboard update to a separate message that the window posts to itself (moving it outside of the clipboard change the notification code)
Uses a global flag to ignore changes it makes itself.
Note: I think the actual bug with your code was that when you're processing WM_CREATE, windowHandler is not yet assigned. You are presumably setting that to the value CreateWindowEx returns, but when WM_CREATE is being processed CreateWindowEx hasn't actually returned yet. This means the clipboard viewer is never actually established correctly. I've changed the references to use hwnd to fix this.
string test("my data");
#define MSG_UPDATECLIPBOARD (WM_APP + 1)
static bool g_fIgnoreClipboardChange = false;
LRESULT CALLBACK WndHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_CREATE:
nextClipboardViewer = SetClipboardViewer(hwnd);
MessageBeep(MB_ICONINFORMATION);
break;
case WM_CHANGECBCHAIN:
if((HWND) wParam == nextClipboardViewer)
nextClipboardViewer = (HWND) lParam;
else if(nextClipboardViewer != NULL)
SendMessage(nextClipboardViewer, msg, wParam, lParam);
break;
case WM_DRAWCLIPBOARD:
if (!g_fIgnoreClipboardChange)
PostMessage(hwnd, MSG_UPDATECLIPBOARD, 0, 0);
if(nextClipboardViewer != NULL)
SendMessage(nextClipboardViewer, msg, wParam, lParam);
break;
case MSG_UPDATECLIPBOARD:
g_fIgnoreClipboardChange = true;
if(OpenClipboard(hwnd)) {
HGLOBAL hClipboardData;
hClipboardData = GlobalAlloc(GMEM_MOVEABLE, test.size() + 1);
char * pchData;
pchData = (char*)GlobalLock(hClipboardData);
memcpy(pchData, test.c_str(), test.size() + 1);
GlobalUnlock(hClipboardData);
SetClipboardData(CF_TEXT, hClipboardData);
CloseClipboard();
}
g_fIgnoreClipboardChange = false;
break;
case WM_DESTROY:
ChangeClipboardChain(hwnd, nextClipboardViewer);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}
Related
I'm new to #in32 and making simple chatting app.
Now I implement edit control to write message, submit by Enter key but not Ctrl + Enter. (I want Ctrl + Enter to make new line.)
I made them using sub procedure.
The problem is that when I submit message by Enter the edit control makes a second line, and cursor point that.
I want make edit control clearly when press Enter.
Here's my code
LRESULT CALLBACK SubEditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
HDC hdc;
PAINTSTRUCT ps;
RECT rt = { 20,20,200,500 };
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam) {
case VK_RETURN:
if (!GetAsyncKeyState(VK_CONTROL)) {
hdc = GetDC(GetParent(hEdit));
TCHAR buff[1024];
GetWindowText(hEdit, buff, 1024);
SetDlgItemText(GetParent(hEdit), ID_EDIT, (LPCWSTR)"");
DrawText(hdc, buff, -1, &rt, DT_LEFT);
ReleaseDC(GetParent(hEdit), hdc);
}
break;
}
break;
default:
return CallWindowProc(DefEditProc, hWnd, uMsg, wParam, lParam);
}
return FALSE;
}
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ HDC hdc;
int count=1; int xs,ys,xe,ye;
switch (message)
{
case WM_LBUTTONDOWN
hdc=GetDC(hwnd);
if(count%2!=1){
xs=GET_X_LPARAM(lParam);
ys=GET_Y_LPARAM(lParam);}
else{
xe=GET_X_LPARAM(lParam);
ye=GET_Y_LPARAM(lParam);
drawline(hdc,xs,ys,xe,ye);
}
ReleaseDC(hwnd,hdc);
count++;
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
I've tried , but there's something wrong with this code.
Use
if(count%2 == 1)
insted of
if(count%2 != 1)
Might work then.
I have wrote a window hook to retrieve the mouse events
_handle = SetWindowsHookEx(WH_MOUSE,
(HOOKPROC)KeyEvent,
nullptr,
GetCurrentThreadId());
static LRESULT WINAPI KeyEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode >= 0)
{
MOUSEHOOKSTRUCT* mStruct = ( MOUSEHOOKSTRUCT*)lParam;
MSLLHOOKSTRUCT* mWheelDStruct = (MSLLHOOKSTRUCT*)lParam;
CMouseHookCom::_this->ReportEventW(mStruct->hwnd, wParam, mWheelDStruct);
}
return(CallNextHookEx(NULL, nCode, wParam, lParam));
}
case WM_MOUSEWHEEL:
OutputDebugString(L"CMouseHookCom-WM_MOUSEWHEEL");
strm = (MSLLHOOKSTRUCT*)extraData;
zDelta = (short)(HIWORD(((MSLLHOOKSTRUCT*)extraData)->mouseData));
_stprintf(buffer, L"CMouseHookCom - WM_MOUSEWHEEL delta %d %i", zDelta, short((strm->mouseData >> 16) & 0xffff));
OutputDebugString(buffer);
if (zDelta > 0)
{
OutputDebugString(L"CMouseHookCom-WM_MOUSEWHEEL UP");
Invoke_onScrollOut(componentId);
}
else
{
OutputDebugString(L"CMouseHookCom-WM_MOUSEWHEEL DOWN");
Invoke_onScrollIn(componentId);
}
break;
default:
_stprintf(buffer, L"CMouseHookCom - UnHandleled event %d", event);
OutputDebugString(buffer);
break;
}
no matter how I try I always get positive results of the wheel. Also I always get different value. What is the correct way to retrieve the data?
You should use Raw Input instead of a mouse hook. That is less overhead on the OS to monitor the mouse, and it gives you information that a hook would not.
I want to make WndProc a class member function and I found this article, so I tried to apply it to the simplest Win32 program, which does nothing but creating a blank window, the very first step of Win32.
int Myclass::Start(HINSTANCE hInstance, int nCmdShow)
{
if (FAILED(InitWindow(hInstance, nCmdShow)))
return 0;
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;;
}
LRESULT Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
Myclass* pThis = nullptr;
if (message == WM_NCCREATE) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<Myclass*>(lpcs->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
}
else {
pThis = reinterpret_cast<Myclass*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if(pThis)
return pThis->RealWndProc(hWnd, message, wParam, lParam);
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT Myclass::RealWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
It runs well, but when I close the window the program stays in the message loop and doesn't quit.
I found out that WM_DESTROY is not delivered to RealWndProc(), so PostQuitMessage() is not called.
If I insert if(WM_DESTROY == message) { PostQuitMessage(0); return 0; } right before the last line of StaticWndProc, then the program quits. But I'm not sure if this is a good way to do it.
How can I make WM_DESTROY consumed by RealWndProc()?
Like this is how I did it.. Works just fine.. The only problem I can think of in your code is one of two:
You missed the calling convention for the WindowProcedure.
You forgot to pass "this" to CreateWindowEx.
And the code:
#include <windows.h>
class Window
{
public:
Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW,
POINT Location = {CW_USEDEFAULT, CW_USEDEFAULT}, int Width = CW_USEDEFAULT,
int Height = CW_USEDEFAULT, HWND Parent = HWND_DESKTOP, HMENU Menu = nullptr);
int Loop();
private:
HWND WindowHandle;
static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
LRESULT RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
};
Window::Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU Menu)
{
WNDCLASSEX WndClass =
{
sizeof(WNDCLASSEX), CS_DBLCLKS, Window::WindowProcedure,
0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_BACKGROUND),
nullptr, Class, LoadIcon(nullptr, IDI_APPLICATION)
};
if(RegisterClassEx(&WndClass))
{
WindowHandle = CreateWindowEx(dwStyleEx, Class, Title, dwStyle, Location.x, Location.y, Width, Height, Parent, Menu, GetModuleHandle(nullptr), this);
}
}
LRESULT __stdcall Window::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Window* Data = nullptr;
switch(Msg)
{
case WM_NCCREATE:
{
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
Data = static_cast<Window*>(pCreate->lpCreateParams);
SetWindowLongPtr(Hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(Data));
break;
}
default:
{
Data = reinterpret_cast<Window*>(GetWindowLongPtr(Hwnd, GWLP_USERDATA));
break;
}
}
return Data ? Data->RealWindowProcedure(Hwnd, Msg, wParam, lParam) : DefWindowProc(Hwnd, Msg, wParam, lParam);
}
LRESULT Window::RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(Hwnd, Msg, wParam, lParam);
}
return 0;
}
int Window::Loop()
{
MSG Messages = {nullptr};
ShowWindow(WindowHandle, SW_SHOW);
while(GetMessage(&Messages, nullptr, 0, 0))
{
TranslateMessage(&Messages);
DispatchMessage(&Messages);
}
return Messages.wParam;
}
int main()
{
Window w("TItle", "Class");
return w.Loop();
}
You should catch WM_CLOSE and have it call DestroyWindow() to trigger WM_DESTROY:
case WM_CLOSE:
DestroyWindow(hWnd);
break;
Also, your StaticWndProc() method is using the wrong calling convention. It must use the __stdcall calling convention, which is wrapped by WINAPI and CALLBACK macros, eg:
LRESULT CALLBACK Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
I am trying to Subclass the Listbox and the Edit Control of a Combobox for some customasing reasons. Below is the code work . Subclassing for Edit Control is working perfect but Listbox is not getting the messeage of MouseDown.
void Subclass(HWND hComboBox)
{
HWND hEdit=FindWindowEx(hComboBox, NULL, WC_EDIT, NULL);
HWND hCombo=FindWindowEx(hComboBox, NULL, WC_LISTBOX, NULL);
SetProp(hEdit, TEXT("Wprc"), (HANDLE)GetWindowLongPtr(hEdit, GWL_WNDPROC));
SubclassWindow(hEdit, ComboBox_Proc);
SetProp(hCombo, TEXT("Wprc1"), (HANDLE)GetWindowLongPtr(hCombo, GWL_WNDPROC));
SubclassWindow(hCombo, ComboBox_Proc1);
}
static LRESULT CALLBACK ComboBox_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CHAR:
break;
case WM_KEYDOWN:
break;
case WM_DESTROY:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD)GetProp(hwnd, TEXT("Wprc")));
RemoveProp(hwnd, TEXT("Wprc"));
break;
default:
return CallWindowProc((WNDPROC)GetProp(hwnd, TEXT("Wprc")), hwnd, msg, wParam, lParam);
}
return FALSE;
}
static LRESULT CALLBACK ComboBox_Proc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_LBUTTONDOWN:
//PROBLEM IS HERE
break;
case WM_DESTROY:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD)GetProp(hwnd, TEXT("Wprc1")));
RemoveProp(hwnd, TEXT("Wprc1"));
break;
default:
return CallWindowProc((WNDPROC)GetProp(hwnd, TEXT("Wprc1")), hwnd, msg, wParam, lParam);
}
return FALSE;
}
The ListBox part of a ComboBox is of type COMBOLBOX (with L).
The ComboLBox window is not a child of the ComboBox window.
The only way I found to subclass the COMBOLBOX control is as follows.
Windows sends the WM_CTLCOLORLISTBOX message to the COMBOBOX (no L) before the listbox is drawn. The lParam of this message contains the handle of the listbox.
case WM_CTLCOLORLISTBOX:
{
if ( !hSubclassedListBox )
{
hSubclassedListBox = (HWND)lParam;
SubclassWindow(hSubclassedListBox , MyLBProc);
}
}
Alsoo see this link for more information
For those who are using Visual Studio with WINVER set to 0500 or higher (Windows XP or later), you can use the GetComboBoxInfo function (passing the handle to the ComboBox), which will return (in a COMBOBOXINFO structure) the handles to both the Edit box and the ComboLBox (ListBox). The handles can then be used to get the CWnd-derived objects they represent.