If I'm reading the question right, your goal is to get notifications when a standard push button is initially pushed by the user, whereas standard notification behavior of buttons only posts WM_COMMANDs on "clicks" where a click is the whole mouse down plus mouse up sequence.
Historically in order to get the BN_PUSHED and BN_UNPUSHED notifications in your WM_COMMAND handler you had to use the BS_NOTIFY window style when creating the button. However, if you read the documentation for BN_PUSHED or BN_UNPUSHED you will see
This notification code is provided only for compatibility with 16-bit versions of Windows earlier than version 3.0. Applications should use the BS_OWNERDRAW button style and the DRAWITEMSTRUCT structure for this task.
These were very old notifications that from what I can tell are not just deprecated but no longer even supported. You can do, however, as the documentation suggests: use an owner drawn button i.e. a button created with the BS_OWNERDRAW style.
This turns out to be more difficult than just creating the button with BS_NOTIFY turned on, because the button will no longer perform default painting by itself. Given this added chore, I'd recommend not doing it this way unless you want to custom paint your buttons anyway -- unless you happen to want some nonstandard visual look-and-feel for these buttons as well as nonstandard notification behavior. Otherwise, I would probably just do Win32 subclassing as someone else suggested to trap WM_LBUTTONDOWN etc. and then call the standard button WNDPROC after doing some action on the events i cared about.
Anyway the minimal owner drawn button that reports button down and button up events is like the following. (I post the button events as custom messages but you could do whatever you wish there)
#include <windows.h> #define BTN_ID 101 #define WM_PUSHBUTTONDOWN WM_APP + 1 #define WM_PUSHBUTTONUP WM_APP + 2 HINSTANCE g_instance = 0; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_instance = hInstance; MSG msg = { 0 }; WNDCLASS wc = { 0 }; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND); wc.lpszClassName = L"owner_draw_btn"; if (!RegisterClass(&wc)) return -1; if (!CreateWindow(wc.lpszClassName, L"foobar", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL)) return -1; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT HandleDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) { auto* dis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); if (dis->CtlType != ODT_BUTTON) return 0; auto style = (dis->itemState & ODS_SELECTED) ? DFCS_BUTTONPUSH | DFCS_PUSHED : DFCS_BUTTONPUSH; auto rect = &dis->rcItem; DrawFrameControl(dis->hDC, rect, DFC_BUTTON, style); TCHAR text[512]; auto n = GetWindowText(dis->hwndItem, text, 512); DrawText(dis->hDC, text, n, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER); if (dis->itemAction == ODA_SELECT) { PostMessage( hWnd, (dis->itemState & ODS_SELECTED) ? WM_PUSHBUTTONDOWN : WM_PUSHBUTTONUP, dis->CtlID, 0 ); } return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: CreateWindow( L"button", L"foobar", BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, 10, 10, 150, 35, hWnd, (HMENU) BTN_ID, g_instance, 0 ); return 0; case WM_DRAWITEM: return HandleDrawItem(hWnd, wParam, lParam); case WM_PUSHBUTTONDOWN: OutputDebugString(L"Button down event\n"); break; case WM_PUSHBUTTONUP: OutputDebugString(L"Button up event\n"); break; case WM_CLOSE: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, message, wParam, lParam); }
BS_PUSHLIKEandBS_DEFPUSHBUTTONare unrelated. Exchanging one for the other doesn't make sense. The Button Control Overviews explain the fundamentals.