I created sample to check tooltip control. Here is a little bit messy program, which creates window, button and registers for button tooltip with message qwerty. Minor logs were added to each window/button/tooltip windows.
#include <windows.h>
#include <Windowsx.h>
#include <CommCtrl.h>
#include <string>
#include <map>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static HWND hwndTip = 0;
static HWND tool = 0;
WNDPROC old_tooltip_proc = 0;
WNDPROC old_button_proc = 0;
std::string GetLastErrorAsString()
{
//Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); //No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
//Free the buffer.
LocalFree(messageBuffer);
return message;
}
void log_msg(std::string prefix, UINT msg)
{
std::map<UINT, std::string> table
{
{WM_NCHITTEST, "WM_NCHITTEST"},
{TTM_WINDOWFROMPOINT, "TTM_WINDOWFROMPOINT"},
{WM_SETCURSOR, "WM_SETCURSOR"},
{WM_PAINT, "WM_PAINT"},
{WM_NCPAINT, "WM_NCPAINT"},
{WM_ERASEBKGND, "WM_ERASEBKGND"},
{WM_SHOWWINDOW, "WM_SHOWWINDOW"},
{WM_ACTIVATEAPP, "WM_ACTIVATEAPP"},
{WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING"},
{WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED"},
{WM_GETTEXT, "WM_GETTEXT"},
{WM_MOUSELEAVE , "WM_MOUSELEAVE"},
{WM_GETTEXTLENGTH, "WM_GETTEXTLENGTH"},
{WM_NCCALCSIZE, "WM_NCCALCSIZE"},
{WM_TIMER, "WM_TIMER"},
{WM_MOVE, "WM_MOVE"},
{WM_MOUSEMOVE, "WM_MOUSEMOVE"},
{WM_LBUTTONDOWN, "WM_LBUTTONDOWN"},
{TTM_RELAYEVENT, "TTM_RELAYEVENT"},
{SB_SETTEXTA, "SB_SETTEXTA"}
};
if (table.find(msg) == table.end())
{
OutputDebugString((prefix + " " + std::to_string(msg) + "\n").c_str());
return;
}
OutputDebugString((prefix + " " + table.at(msg) + "\n").c_str());
}
LRESULT CALLBACK tooltip_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
log_msg("TOOLTIP", message);
return CallWindowProc(old_tooltip_proc, hwnd, message, wParam, lParam);
}
LRESULT CALLBACK button_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
log_msg("BUTTON", message);
return CallWindowProc(old_button_proc, hwnd, message, wParam, lParam);
}
void createToolTip(HINSTANCE hInstance, HWND parent_window)
{
hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
parent_window, NULL, hInstance,
NULL);
old_tooltip_proc = (WNDPROC)SetWindowLongPtr(hwndTip, GWLP_WNDPROC, (LONG_PTR)tooltip_proc);
if (!hwndTip)
{
MessageBox(parent_window, "CreateWindowEx TOOLTIPS_CLASS failed", "ERROR", MB_OK);
return;
}
}
TOOLINFO get_tool_info(HWND tool)
{
TOOLINFO g_toolItem = { 0 };
g_toolItem.cbSize = sizeof(g_toolItem);
g_toolItem.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
g_toolItem.hwnd = GetParent(tool);
g_toolItem.uId = (UINT_PTR)tool;
g_toolItem.hinst = NULL;
g_toolItem.lpszText = LPSTR_TEXTCALLBACK;
return g_toolItem;
}
void register_tool(HWND _tool)
{
std::string f("qwerty");
TOOLINFO toolinfo = get_tool_info(_tool);
toolinfo.lpszText = const_cast<char*>(f.c_str());
if (SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolinfo) == FALSE)
{
MessageBox(toolinfo.hwnd, "TTM_ADDTOOL failed", "ERROR", MB_OK);
return;
}
tool = _tool;
}
void unregister_tool(HWND tool)
{
TOOLINFO toolinfo = get_tool_info(tool);
SendMessage(hwndTip, TTM_DELTOOL, 0, (LPARAM)&toolinfo);
}
HWND createButton(HWND parent_window)
{
auto handle = CreateWindow(TEXT("button"), TEXT("Hellooooooooooooooooooooooooooooo"),
WS_VISIBLE | WS_CHILD,
10, 10, 800, 250,
parent_window, NULL, NULL, NULL);
old_button_proc = (WNDPROC)SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)button_proc);
return handle;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
INITCOMMONCONTROLSEX ic;
ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
ic.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&ic);
static TCHAR szAppName[] = TEXT("ToolTipApplication");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, // window class name
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
HWND button = createButton(hwnd);
createToolTip(hInstance, ::GetDesktopWindow());
register_tool(button);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
log_msg("window", message);
return DefWindowProc(hwnd, message, wParam, lParam);
}
Tooltip works fine, but I want to replace default behaviour. If you hover over button, tooltip will be shown. But when I quickly move cursor at tooltip then tooltip will be disappeared. I want to disable such vanishing. Tooltips from system tray resembly this behaviour. If you hover over tooltip, it will not be disappeared simultaneously. Something like that:
P.S. I tried to redefine handling of TTM_RELAYEVENT message, but it did not give any results.
Add TTF_TRANSPARENT
TTF_TRANSPARENT: Causes the tooltip control to forward mouse event
messages to the parent window. This is limited to mouse events that
occur within the bounds of the tooltip window.
code:
TOOLINFO get_tool_info(HWND tool)
{
TOOLINFO g_toolItem = { 0 };
g_toolItem.cbSize = sizeof(g_toolItem);
g_toolItem.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRANSPARENT;
g_toolItem.hwnd = GetParent(tool);
g_toolItem.uId = (UINT_PTR)tool;
g_toolItem.hinst = NULL;
g_toolItem.lpszText = LPSTR_TEXTCALLBACK;
return g_toolItem;
}
Related
I would like to prevent some action in my app using winapi for example I would like to block moving my window app. How can I do this?
I try this:
if( pWindowsMessage->message == WM_MOVING )
{
return 1;
}
else if(pWindowsMessage->message == WM_MOVE)
{
return 1;
}
But it doesn't work.
Other example: How to prevent close the window?
if( pWindowsMessage->message == WM_CLOSE )
{
return 1;
}
It works. But is it a good solution?
Of course the first and the second example are in the function which get messages.
You can block messages in different areas by handling WM_NCLBUTTONDOWN messages.
And According to WM_NCHITTEST,we can handle events in different regions.
Here is a sample:
#include <Windows.h>
#include <cassert>
#include <cstdlib>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("hello windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindow(szAppName,
TEXT("the hello program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NCLBUTTONDOWN:
if (wParam == HTCAPTION || (wParam >= HTLEFT && wParam < HTBORDER))
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I'm trying to add thumbnail buttons to a window, but there is no error and no thumbnail button is shown. I read the following pages for reference:
Your First Windows Program;
ITaskbarList3::ThumbBarAddButtons method;
some code on github.
Environment: win10 64bit, vs2015
// function WindowProc and wWinMain are copied from msdn directly.
#include "stdafx.h"
#include <windows.h>
#include "shobjidl.h"
#include <exception>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT addThumbnailButtons(HWND hwnd) {
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr)) {
ITaskbarList4* ptbl = nullptr;
HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&ptbl));
if (SUCCEEDED(hr)) {
// create 2 buttons
THUMBBUTTON thmb[2] = {};
thmb[0].dwMask = THB_TOOLTIP;
thmb[0].iId = 0;
wcscpy_s(thmb[0].szTip, L"Button 1");
thmb[1].dwMask = THB_TOOLTIP;
thmb[1].iId = 1;
wcscpy_s(thmb[1].szTip, L"Button 2");
//ptbl->HrInit();
hr = ptbl->ThumbBarAddButtons(hwnd, ARRAYSIZE(thmb), thmb);
ptbl->Release();
return hr;
}
else {
throw std::exception("createInstance failed");
}
}else{
throw std::exception("coinitialize failed");
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
HRESULT hr = addThumbnailButtons(hwnd);
if (FAILED(hr)) {
throw std::exception("addbuttons failed");
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Per the ITaskBarList3 documentation:
When an application displays a window, its taskbar button is created by the system. When the button is in place, the taskbar sends a TaskbarButtonCreated message to the window. Your application should call RegisterWindowMessage(L"TaskbarButtonCreated") and handle that message in its wndproc. That message must be received by your application before it calls any ITaskbarList3 method.
You must wait for that message before calling addThumbnailButtons(), eg:
UINT uMsgTaskbarCreated;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
uMsgTaskbarCreated = RegisterWindowMessage(L"TaskbarButtonCreated");
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
default:
if ((uMsg == uMsgTaskbarCreated) && (uMsgTaskbarCreated != 0))
{
HRESULT hr = addThumbnailButtons(hwnd);
if (FAILED(hr)) {
...;
}
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Hello all I have been trying to paint to my child window outside of WM_PAINT and Instead of painting on the child window it paints off of the window onto the screen I think it may be because of the location of x and y but shouldn't point (0,0) be the top left of the child window not of my actual screen?
Here is what the code I wrote to try to paint onto the child window:
#pragma warning(disable:4996)
#pragma comment(lib, "Ws2_32.lib")
#include <Windows.h>
#define WIDTH 800
#define HEIGHT 600
#define CLASS_NAME "Class"
#define IDC_MAIN_EDIT 101
#define IDC_WINDOW 102
int x = 0, y = 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void print_line(HWND hwnd, char *Msg);
void Println();
int Run();
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex = { 0 };
MSG msg;
HWND hwnd = NULL;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = CLASS_NAME;
wcex.hInstance = hInstance;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
MessageBoxA(NULL, "Failed to register class", "Error", MB_OK | MB_ICONERROR);
return -1;
}
hwnd = CreateWindow(CLASS_NAME //Name of the window class
, CLASS_NAME//Title of the window
, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN// the window style
, 0 //x Postition of the window
, 0// y position of the windpo
, WIDTH, HEIGHT, // Width and Height
NULL,//Parent window(we have no parent window)
NULL,//Menu(we are not using menu's)
hInstance,//application handle
NULL);//Creates the window
if (!hwnd)
{
MessageBoxA(NULL, "Failed to register class", "Error", MB_OK | MB_ICONERROR);
return -2;
}
ShowWindow(hwnd, nCmdShow);
return Run();
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND hEdit = NULL;
HWND hWindow = NULL;
#define Print(msg2) print_line(hWindow, msg2)
switch (msg)
{
case WM_CREATE:
hWindow = CreateWindowEx(WS_EX_CLIENTEDGE, "Window", "",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOHSCROLL | WS_CLIPSIBLINGS,
0, 0, WIDTH, HEIGHT - 25, hwnd, (HMENU)IDC_WINDOW, GetModuleHandle(NULL), NULL);
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOHSCROLL,
0, 535, WIDTH, 25, hwnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
Print("Hello");
break;
default:
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
}
int Run()
{
MSG msg = { 0 };
WSAData wsa;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsa) != 0) return -1;
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
void print_line(HWND hwnd, char *Msg)
{
HDC hdc;
hdc = GetDC(hwnd);
TextOut(hdc,
x,
y,
Msg,
strlen(Msg));
ReleaseDC(hwnd, hdc);
}
void Println()
{
x = 0;
y += 20;
}
Yes I know there are other questions concerning this topic but none of them seemed to answer my question or address any of the problems I have been experiencing while trying to paint on the child window.
It Seems to me you create your windows in WndProc, but they do not get returned, so both windows created in WndProc end up being NULL when WM_CREATE returns (AS the are only declared in WndProc).
Perhaps you need to set these both to globals, the use for example GetDC(hEdit) to draw to them. Overall it still looks like an odd bit of code.
It seems to me that you want to write text to the edit control you created with id=IDC_MAIN_EDIT by manually drawing the text into the control?
Try
case WM_COMMAND:
//Print("Hello");
hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT); // get handle to edit window
if (hEdit != NULL) // did we get a handle to the edit window?
{
int len = GetWindowTextLength(hEdit);
SendMessage(hEdit , EM_SETSEL, len, len); // select end of contents
SendMessage(hEdit , EM_REPLACESEL, 0, (LPARAM)"Hello"); // replace end with new textt
}
break;
and the edit control will draw the text for you.
If you want to manually write the text to the window created by you modify the Print macro as follows:
#define Print(msg2) print_line(hwnd, msg2)
I'm trying to do sort in a ListView. Didn't found any complete example how to do it, only a pieces of code.
I've already created ListView, added columns and items, specified my Comp function. However my problem is that lp1 and lp2 are always 0. I've checked documentation and it says : "These are the values that were specified in the lParam member of the items' LVITEM structure when they were inserted into the list." which I do set.
Here is the code :
#include <windows.h>
#include <stdio.h>
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")
HWND ListView;
HWND hwnd;
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
MSG messages;
WNDCLASSEX wincl = { 0 };
wincl.lpszClassName = "Testtttt";
wincl.lpfnWndProc = WindowProc;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hIcon = 0;
wincl.hIconSm = 0;
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0,0,0));// COLOR_BACKGROUND;
wincl.hInstance = GetModuleHandle(NULL);
RegisterClassEx(&wincl);
hwnd = CreateWindowEx(
0,
wincl.lpszClassName,
"Test",
WS_SYSMENU | WS_EX_STATICEDGE | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
1000,
500,
hwnd,
NULL,
GetModuleHandle(NULL),
NULL);
RECT rec;
GetClientRect(hwnd, &rec);
SetWindowPos(ListView, 0, 0, 0, rec.right, rec.bottom - 23, 0);
ShowWindow(hwnd, nCmdShow);
while (GetMessage(&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
}
void AddListViewItem(char item, char *text)
{
static int i;
LVITEM lis;
lis.mask = LVIF_TEXT /*| LVIF_PARAM*/;
lis.pszText = (LPSTR)(text);
lis.iItem = i;
lis.iSubItem = item;
lis.lParam = i;
if (item==1)
i++;
if (item == 0)
ListView_InsertItem(ListView, &lis);
else
SendMessage(ListView, LVM_SETITEM, 0, (LPARAM)&lis);
}
int CALLBACK myCompFunc(LPARAM lp1, LPARAM lp2, LPARAM sortParam)
{
bool isAsc = (sortParam > 0);
int column = abs(sortParam) - 1;
char tmp[128];
sprintf_s(tmp,sizeof tmp, "%d %d %d", lp1, lp2, column);
MessageBox(0, (LPCSTR)tmp, "myCompFunc", MB_OK);
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_NOTIFY)
{
LPNMHDR lpnmh = (LPNMHDR)lParam;
if (lpnmh->idFrom == 8553)
if (lpnmh->code == LVN_COLUMNCLICK)
{
NMLISTVIEW* pListView = (NMLISTVIEW*)lParam;
ListView_SortItems(ListView, myCompFunc, pListView->iSubItem);
}
}
else if (message == WM_CREATE)
{
ListView = CreateWindow(WC_LISTVIEW, (LPCSTR)L"", (WS_CHILD | WS_VISIBLE | LVS_REPORT), 0, 0, 900, 500, hwnd, (HMENU)8553, GetModuleHandle(NULL), NULL);
SendMessage(ListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); // Set style
LVCOLUMN listColumn = { 0 };
listColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
listColumn.pszText = "Column1";
listColumn.cx = 150;
listColumn.fmt = LVCFMT_LEFT;
ListView_InsertColumn(ListView, 0, &listColumn);
listColumn.pszText = "Column2";
listColumn.cx = 150;
listColumn.iSubItem = 1;
ListView_InsertColumn(ListView, 1, &listColumn);
{
AddListViewItem(0, "a");
AddListViewItem(1, "a 1");
AddListViewItem(0, "b");
AddListViewItem(1, "b 1");
AddListViewItem(0, "c");
AddListViewItem(1, "c 1");
}
}
else if (message == WM_DESTROY)
{
PostQuitMessage(0);
}
else
return DefWindowProc(hwnd, message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
return 0;
}
This code produces simple FindText dialog window and when user clicks top-right X window close button the WM_CLOSE message is send to the hook procedure, but when clicking "cancel" button no message is produced to indicate that window ceased to be.
#include <windows.h>
#include <iostream>
#include <iomanip>
UINT_PTR CALLBACK FRHookProc(HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM /*lParam*/) {
switch (uiMsg) {
case WM_INITDIALOG: {
ShowWindow(hdlg, SW_SHOW);
break;
}
}
using namespace std;
if (uiMsg == WM_CLOSE) cout << "FindTextW window has been closed";
return 0;
}
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int /* nCmdShow*/) {
using namespace std;
WNDCLASSEXW wc;
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = &MyWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(PVOID);
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
wc.lpszMenuName = L"MainMenu";
wc.lpszClassName = L"window";
ATOM class_atom = RegisterClassExW(&wc);
HWND hWnd = CreateWindowExW(
0,
reinterpret_cast<LPCWSTR>(class_atom),
L"window title",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPCHILDREN | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
WCHAR szFindWhat[MAX_PATH] = {0};
FINDREPLACEW fr;
ZeroMemory(&fr, sizeof(fr));
fr.lStructSize = sizeof(fr);
fr.hwndOwner = hWnd;
fr.lpstrFindWhat = szFindWhat;
fr.lpfnHook = FRHookProc;
fr.wFindWhatLen = MAX_PATH;
fr.Flags = FR_ENABLEHOOK;
/*HWND hdlg =*/ FindTextW(&fr);
MSG msg;
for (;;) {
GetMessageW(&msg, 0, 0, 0);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
Read the documentation more carefully. You took the completely wrong approach to detecting the dialog window being closed.
FindText function:
Before calling FindText, you must call the RegisterWindowMessage function to get the identifier for the FINDMSGSTRING message. The dialog box procedure uses this identifier to send messages when the user clicks the Find Next button, or when the dialog box is closing.
FINDMSGSTRING message:
A Find or Replace dialog box sends the FINDMSGSTRING registered message to the window procedure of its owner window when the user clicks the Find Next, Replace, or Replace All button, or closes the dialog box.
...
You must specify the FINDMSGSTRING constant in a call to the RegisterWindowMessage function to get the identifier for the message sent by the dialog box.
When you create the dialog box, use the hwndOwner member of the FINDREPLACE structure to identify the window to receive FINDMSGSTRING messages.
...
The Flags member of the FINDREPLACE structure includes one of the following flags to indicate the event that caused the message.
FR_DIALOGTERM (0x00000040)
The dialog box is closing. After the owner window processes this message, a handle to the dialog box is no longer valid.
So, with that said, try this instead:
#include <windows.h>
#include <iostream>
#include <iomanip>
static const UINT uFindMsgString = RegisterWindowMessageW(L"commdlg_FindReplace");
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if ((Msg == uFindMsgString) && (Msg != 0))
{
FINDREPLACE *fr = reinterpret_cast<FINDREPLACE*>(lParam);
if (fr->flags & FR_DIALOGTERM)
{
std::cout << "FindTextW window has been closed";
PostQuitMessage(0);
}
// process other dialog notifications as needed...
return 0;
}
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int /* nCmdShow*/)
{
WNDCLASSEXW wc = {0};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = &MyWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(PVOID);
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
wc.lpszMenuName = L"MainMenu";
wc.lpszClassName = L"window";
ATOM class_atom = RegisterClassExW(&wc);
HWND hWnd = CreateWindowExW(
0,
wc.lpszClassName,
L"window title",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPCHILDREN | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
WCHAR szFindWhat[MAX_PATH] = {0};
FINDREPLACEW fr = {0};
fr.lStructSize = sizeof(fr);
fr.hwndOwner = hWnd;
fr.lpstrFindWhat = szFindWhat;
fr.wFindWhatLen = MAX_PATH;
HWND hdlg = FindTextW(&fr);
MSG msg;
while (GetMessageW(&msg, 0, 0, 0) > 0)
{
if (!IsDialogMessage(hdlg, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
DestroyWindow(hWnd);
UnregisterClass(wc.lpszClassName, hInstance);
return 0;
}
You should be getting a WM_COMMAND notification for the cancel button's BN_CLICKED message. When 'cancel' is pressed you should get that notification for the IDCANCEL control ID.