Win 32 API c++ Notepad Menu isn't work - winapi

I'm coding a application like Notepad in Win32 c++. But when i change szClassName in createWindow(), the menu can't work although it still show it when I run
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDC_NOTEPAD));
hWnd = CreateWindow(L"EDIT", szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);
//SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}

In CreateWindow(), the hMenu parameter is the control ID but not the menu handle.
I suggest creating the main window before adding a textbox:
WNDCLASSEX wc;
// ...
wc.lpszClassName="window class";
wc.lpszMenuName=hMenu;
// ...
RegisterWindowEx(&wc);
hWnd=CreateWindow("window class", ...);
// When processing WM_CREATE message in WndProc()
hEdit=CreateWindow("EDIT","your textbox", ... /* set hWndParent as hWnd */);
I think theForger's Win32 API Programming Tutorial is also a good place for you to start.

Related

Creating ComboBox in Win32 not working properly

I am creating a Win32 ComboBox for the first time. And I have a problem here.
When calling CreateWindow for the ComboBox, it calls the WndProc callback function again with the WM_CREATE message, so what happens is the ComboBox makes a child ComboBox, again and again like recursion.
Here is the code:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <random>
#include <time.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = L"ComboBox";
const WCHAR *items[] = { L"Apple", L"Orange", L"Melon", L"Grape", L"Strawberry" };
HWND hwnd;
enum COMMAND_ID {
COMMAND_ID_CONTROL_COMBO_0
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
srand(time(NULL));
g_hInst = hInstance;
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndClass);
hwnd = CreateWindow(
lpszClass,
lpszClass,
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (true)
{
GetMessage(&msg, NULL, 0, 0);
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static HWND hCombo;
static WCHAR str[128];
switch (msg)
{
case WM_CREATE:
{
hCombo = CreateWindow(
L"combobox",
NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWN,
10, 10, 200, 200,
hWnd,
(HMENU)COMMAND_ID_CONTROL_COMBO_0,
g_hInst,
NULL);
for (int i = 0; i < 5; ++i)
{
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)items[i]);
}
SendMessage(hCombo, CB_SETCURSEL, 0, NULL);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wparam))
{
case COMMAND_ID_CONTROL_COMBO_0:
switch (HIWORD(wparam))
{
case CBN_SELCHANGE:
{
int iCurSel = SendMessage(hCombo, CB_GETCURSEL, NULL, NULL);
SendMessage(hCombo, CB_GETLBTEXT, iCurSel, (LPARAM)str);
SetWindowText(hWnd, str);
}
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd, str);
break;
}
break;
default:
break;
}
}
return 0;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
And here is the result:
I tried to put some boolean flag to execute WM_CREATE only once, and it works, I mean only creating one ComboBox without any child in it.
But, it just looked like only a white window with a border mark, there's no arrow button or anything to dropdown page that the ComboBox is supposed to have.
This recursive case never happened when I was creating different controls like Buttons, CheckBoxes, ListBoxes, etc.
And the ComboBox created doesn't look like it has the proper shape, too.
Hope I am just missing something simple.
When calling CreateWindow for the ComboBox, it calls the WndProc
callback function again with the WM_CREATE message, so what happens
is the ComboBox makes a child ComboBox, again and again like
recursion.
WM_CREATE message is sent to the window procedure of the new window after the window is created. Your first WM_CREATE message is generated by this line hwnd = CreateWindow(). Then you create another window in first WM_CREATE message, so it will generate second WM_CREATE message. Because you use the same registered class ("ComboBox" / "combobox", it is not case sensitive) to create all these windows, all of them use the same one window procedure. So you receive WM_CREATE message again and again until CreateWindow fail to create a window and return NULL.
But, it just looked like only a white window with a border mark,
there's no arrow button or anything to dropdown page that the ComboBox
is supposed to have.
The root cause is you register a class with the same name as the existing system class: "ComboBox" / "combobox". This new registered class override the existing one. It is just a common window instead of a predefined Combobox control as #RemyLebeau pointed out.
An application can register an application local class having the same
name as a system class. This replaces the system class in the context
of the application but does not prevent other applications from using
the system class.
Refer to "How the System Locates a Window Class".
To make the Combobox display in expected shape, what you need to do is changing the lpszClass to a non-predefined one, for example, like "SimpleComboBoxExample".
It is suggested to use predefined macro of Combobox Class Name: WC_COMBOBOX instead of L"combobox".
More reference: "How to Create a Simple Combo Box".
You are not actually creating a Win32 ComboBox at all. You are registering your own class named "ComboBox", and then creating a window of that class, which creates a window of that class, which creates a window of that class, and so on recursively.
You need to change this line:
LPCTSTR lpszClass = L"ComboBox";
To a different unique name, such as "MyWindowClass".
On a side note, your message loop is structured wrong. It should look like this instead:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
And in your WndProc(), the return 0; statement underneath the WM_COMMAND handler is in the wrong place. It needs to be moved inside of the WM_COMMAND handler instead:
case WM_COMMAND:
{
switch (...)
{
...
}
return 0; // <-- moved here
}
//return 0; // <-- from here

Is there a way to get the handle of the entry field in a date time picker (DTP)?

The DATETIMEPICKERINFOstructure obtained by sending the DTM_GETDATETIMEPICKERINFOmessage has a field hwndEdit which might be what I'm looking for. However, I'm getting always NULL for it so I'm wondering what's its actual meaning. If not, is there a way to get the handle of the entry field?
hwndEdit only seems to be valid when the control has the DTS_APPCANPARSE style and you click the date text with the mouse (I tested this with OutputDebugString and a timer). The edit control is created and destroyed dynamically. The hwndUD handle is only valid if DTS_UPDOWN is set and the hwndDropDown is only valid while the dropdown is visible.
It is not called out in the documentation but DTM_GETDATETIMEPICKERINFO is marked Vista+ and this often means the feature is only implemented in ComCtl32 v6 so you also have to make sure you have a manifest that requests this version.
To change the color you can try DTM_SETMCCOLOR but only MCSC_BACKGROUND is documented to work when Visual Styles are active.
I'm afraid there is no way to get what you wanted. I just created a simple Win32 application just to test the possibility. If I use the DTM_GETDATETIMEPICKERINFO, hwndDropDown, hwndEdit and hwndUD give me NULL. If I try to enum child window, well before I do so I check it with Spy++, no luck, there is no child window associated with it.
Finally, I tried GetFocus() and WindowFromPoint(), both give me the HWND of the DateTimePicker itself only.
Here is my testing code:
#pragma comment(lib, "comctl32.lib")
#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
enum MYID {
MYID_FIRST = WM_APP,
MYID_DTP
};
LPCTSTR const g_MyWndClass = _T("DTPTest");
LPCTSTR const g_MyWndTitle = _T("DTPTest");
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void OnWindowCreate(HWND);
void OnTimer(HWND);
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int nCmdShow)
{
INITCOMMONCONTROLSEX icex{};
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&icex);
WNDCLASSEX wcex{};
wcex.cbSize = sizeof(wcex);
wcex.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.lpfnWndProc = WndProc;
wcex.lpszClassName = g_MyWndClass;
wcex.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&wcex);
HWND hwnd = CreateWindowEx(0,
g_MyWndClass, g_MyWndTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 600, 400,
nullptr, nullptr, nullptr, nullptr);
if (!hwnd) { return 99; }
SetTimer(hwnd, 0, 100, nullptr);
ShowWindow(hwnd, nCmdShow);
MSG msg{};
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l)
{
switch (msg) {
case WM_CREATE:
OnWindowCreate(hwnd);
break;
case WM_TIMER:
OnTimer(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, w, l);
}
return 0;
}
void OnWindowCreate(HWND hwnd)
{
HWND hwndDTP = CreateWindowEx(0, DATETIMEPICK_CLASS, nullptr,
WS_CHILD | WS_VISIBLE | DTS_SHOWNONE,
20, 50, 220, 20,
hwnd, reinterpret_cast<HMENU>(MYID_DTP), nullptr, nullptr);
DATETIMEPICKERINFO info{};
info.cbSize = sizeof(DATETIMEPICKERINFO);
SendMessage(hwndDTP, DTM_GETDATETIMEPICKERINFO, 0,
reinterpret_cast<LPARAM>(&info));
if (!info.hwndDropDown && !info.hwndEdit && !info.hwndUD)
{
MessageBox(hwnd, _T("No luck with DTM_GETDATETIMEPICKERINFO"),
nullptr, MB_ICONERROR);
}
}
void OnTimer(HWND hwnd)
{
POINT pt{};
GetCursorPos(&pt);
HWND hwndPoint = WindowFromPoint(pt);
HWND hwndFocus = GetFocus();
TCHAR buf[99]{};
wsprintf(buf, _T("Pointing at %p, focusing %p"),
hwndPoint, hwndFocus);
SetWindowText(hwnd, buf);
}

Is DirextX9 material no longer worthy material for learning win32 API programming?

I have been expanding my library (Physical library of these weird things called 'books'... I know... I know) and I'm reading Beginning DirectX 9.0 by Wendy Jones. As I have gone through some books in the past that are 'outdated' the logic behind them is actually the same, if not more important in the earlier versions (in my experience) of things like C++ books I have read. The issue I am having with this DirectX 9 book is, 10/10 practice codes, don't work, ever. Even the solutions found on here, and MSDN
didn't work for me. (Identical problem).
So I was hoping if you could tell me before I go and purchase a book on DX11, if it might be something to do with my compiler/vs or the fact that vs is updated 2015, and this DX9 is obselete/DX11 standards have been introduced.
//Include the Windows header file that's needed for all Windows applications
#include <Windows.h>
HINSTANCE hInst; // global handle to hold the application instance
HWND wndHandle; // global variable to hold the window handle
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
//forward declerations
bool initWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT WPARAM, LPARAM);
//This is winmain, the main etry point for Windows applications
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
//Initialize the window
if (!initWindow(hInstance))
return false;
//main message loop: (See page 13, "Adding the Windows Code" - Chapter 2
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT);
{
//Check the message queue
while (GetMessage(&msg, wndHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(int)msg.wParam;
}
/******************************************************************************
* bool initWindow( HINSTANCE hInstance )
* initWindow registers the window class for the application, creates the window
******************************************************************************/
bool initWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
//Fill in the WNDCLASSEX structure. THis describes how the window will look to the system
wcex.cbSize = sizeof(WNDCLASSEX); // the size of the structure
wcex.style = CS_HREDRAW | CS_VREDRAW; // the class style
wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback
wcex.cbClsExtra = 0; // extra bytes to allocate for this calss
wcex.cbWndExtra = 0; // extra bytes to allocate for this instance
wcex.hInstance = hInstance; // handle to the application
wcex.hIcon = 0; // icon to associate with the application
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // the default cursor
wcex.lpszMenuName = NULL; // the resource name for the menu
wcex.lpszClassName = NULL; // the class name being created
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
//Create the window
wndHandle = CreateWindow(
(LPCWSTR)"DirectXExample", // the window class to use
(LPCWSTR)"DirectXExample", // the title bar text
WS_OVERLAPPEDWINDOW, // the window style
CW_USEDEFAULT, // the starting x coordinate
CW_USEDEFAULT, // the starting y coordinate
640, //the pixel width of the window
480, //the pixel height of the window
NULL, // the parent window; NULL for desktop
NULL, // the menu for the application; NULL for none
hInstance, // the handle to the apllication instance
NULL); // no values passed to the window
//make sure that the window handle that is created is valid
if (!wndHandle)
return false;
//Display the window on the screen
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
It's perfectly fine to keep using DirectX 9. But your implementation for getting a minimal host window up on the screen has some simple bugs. It also is doing some bad casts between ANSI and wide strings. Let's get you fixed:
Remove the forward declaration of WinMain. This line, just remove it.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
In the actual function body for WinMain, change the type for lpCmdLine from LPTSTR parameter to be just LPSTR.
//This is winmain, the main etry point for Windows applications
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
//Initialize the window
if (!initWindow(hInstance))
Your declaration of WndProc is also incorrect. WndProc should be declared as follows:
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam);
Once you fix the above declaration of WndProc, you can take out that bad cast operation in the WNDCLASS initialization. Change this:
wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback
To this:
wcex.lpfnWndProc = WndProc; // the window procedure callback
You are missing a definition of WndProc. You need to implement that function yourself. Here's a minimal implementation:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
The above will get your code to compile, but it will still have some bugs and won't actually run like it should. Let's fix those.
First, your message pump has an extra ; that is preventing it from actually running and keeping your code in an infinite loop. This line:
while (msg.message != WM_QUIT);
Should be (without the semicolon):
while (msg.message != WM_QUIT)
And while I'm here, your message pump implementation is kind of weird. GetMessage only returns FALSE when msg.message==WM_QUIT So the outer loop is not needed. Change this:
while (msg.message != WM_QUIT)
{
//Check the message queue
while (GetMessage(&msg, wndHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
to be this:
//Check the message queue until WM_QUIT is received
while (GetMessage(&msg, wndHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
When you actually implement your graphics loop for your DX app, you can change the GetMessage call to PeekMessage and then explicitly check for WM_QUIT then.
Your initWindow is failing for many reasons.
You are leaving some garbage values in the WNDCLASSEX variable. Change this line:
WNDCLASSEX wcex;
To be this:
WNDCLASSEX wcex = {};
You are forgetting to set wcex.lpszClassName. Make it this:
wcex.lpszClassName = L"DirectXExample";
And then your casting of ANSI strings to (LPCWSTR) is incorrect. To make it easier, here's a fixed version of your initWindow function.
bool initWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex = {};
//Fill in the WNDCLASSEX structure. THis describes how the window will look to the system
wcex.cbSize = sizeof(WNDCLASSEX); // the size of the structure
wcex.style = CS_HREDRAW | CS_VREDRAW; // the class style
wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback
wcex.cbClsExtra = 0; // extra bytes to allocate for this calss
wcex.cbWndExtra = 0; // extra bytes to allocate for this instance
wcex.hInstance = hInstance; // handle to the application
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); // icon to associate with the application
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // the default cursor
wcex.lpszMenuName = NULL; // the resource name for the menu
wcex.lpszClassName = L"DirectXExample"; // the class name being created
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
//Create the window
wndHandle = CreateWindow(
L"DirectXExample", // the window class to use
L"DirectXExample", // the title bar text
WS_OVERLAPPEDWINDOW, // the window style
CW_USEDEFAULT, // the starting x coordinate
CW_USEDEFAULT, // the starting y coordinate
640, //the pixel width of the window
480, //the pixel height of the window
NULL, // the parent window; NULL for desktop
NULL, // the menu for the application; NULL for none
hInstance, // the handle to the apllication instance
NULL); // no values passed to the window
//make sure that the window handle that is created is valid
if (!wndHandle)
return false;
//Display the window on the screen
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
And that should do it.

SysAnimate32 on Windows 7 and 8

This code no longer works on Windows 7 or 8.
Aren't SysAnimate32 controls supported on them ?
Is there a way to make it work as it used to work on Windows XP ?
(I am required to write more details but I have no more details to talk about :)
thanks
#include <windows.h>
#include <CommCtrl.h>
HINSTANCE hInstance;
#define IDC_MYANIMATE 9
HWND CreateAnimationControl (HWND hParent)
{
HWND hAnimation = Animate_Create( hParent, IDC_MYANIMATE, ACS_AUTOPLAY | WS_BORDER | WS_CHILD, hInstance);
Animate_Open (hAnimation, "test.avi");
ShowWindow (hAnimation, SW_SHOW);
return hAnimation;
}
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hAnimation = NULL;
switch (uiMsg)
{
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_CREATE:
hAnimation = CreateAnimationControl (hWnd);
break;
case WM_SHOWWINDOW:
if (wParam)
{
MoveWindow (hAnimation, 0, 0, 300, 300, TRUE);
Animate_Play (hAnimation, 0, -1, -1);
}
break;
}
return DefWindowProc (hWnd, uiMsg, wParam, lParam);
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpsCmdLine, int iCmdShow)
{
WNDCLASSEX WindowClass;
HWND hWnd;
MSG uMsg;
hInstance = GetModuleHandle (NULL);
WindowClass.cbClsExtra = 0;
WindowClass.cbSize = sizeof (WNDCLASSEX);
WindowClass.cbWndExtra = 0;
WindowClass.hbrBackground = CreateSolidBrush (RGB (0, 0, 0));
WindowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
WindowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
WindowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
WindowClass.hInstance = hInstance;
WindowClass.lpfnWndProc = WindowProcedure;
WindowClass.lpszClassName = "1";
WindowClass.lpszMenuName = NULL;
WindowClass.style = 0;
if (!RegisterClassEx (&WindowClass))
{
MessageBox (NULL, "Window class registration has failed!", "Error:", MB_OK | MB_ICONERROR);
return 0;
}
hWnd = CreateWindow ("1", "Win32 Animation Testing", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hInstance, NULL);
if( ! hWnd )
{
MessageBox (NULL, "Window creation has failed!", "Error:", MB_OK | MB_ICONERROR);
return 0;
}
ShowWindow( hWnd, SW_SHOW );
UpdateWindow( hWnd );
while( GetMessage( &uMsg, NULL, 0, 0 ) > 0 )
{
TranslateMessage( &uMsg );
DispatchMessage ( &uMsg );
}
return ( int ) uMsg.wParam;
}
No, that still works just fine when I tried your code on Windows 8, compiled with VS2012. A screenshot to prove it:
Notable is that your error checking is lacking, you don't pay attention to the return value of Animate_Open(). A FALSE return indicates that it could not open the .avi file. Many possible reasons, there's no better diagnostic from that function than "could not do it".
It was difficult to find a .avi file that it could handle, the control is stone-cold old and cannot handle but the simplest ones. In particular an .avi file that also has a audio track will not open, as documented by the MSDN Library. The test one I used was a very simple one I dug out of the Visual Studio image library, Animations/filecopy_16.avi.
So basic checks, after adding the error handling, is to ensure that the file is actually present in the same directory as your EXE and that it is a very simple .avi file that at least plays back in WMP.

Application Bars and 'Show Desktop'

I have a Windows app that uses the AppBar API to install as an application bar at the top of the screen (similar to the Windows task bar itself). This works great and the desktop size is adjusted accordingly, so my application is always visible.
However, if the user choose 'Show Desktop' (Windows+D), my application is hidden. Does anyone know of a way to trap 'Show Desktop' so I can ensure my application stays visible (I assume that Windows enumerates all top-level windows and hides them with ShowWindow(SW_HIDE).
Use the following code and pass the window handle to the function while form load.
Hopefully this resolves your problem.
public void SetFormOnDesktop(IntPtr hwnd)
{
IntPtr hwndf = hwnd;
IntPtr hwndParent = FindWindow("ProgMan", null);
SetParent(hwndf, hwndParent);
}
I was under the impression that setting the window as a topmost window (via SetWindowPos and the HWND_TOPMOST flag) prevented the desktop from covering it. Windows+D goes through minimizing all windows, and then covering up those that can't be minimized by raising the desktop in the z-order (well, it did at one point anyway). I belive you can make a window unminimizable by not passing WS_MINIMIZEBOX to CreateWindowEx, or using WS_EX_TOOLWINDOW though I'm not 100% on that part.
A much heavier handed approach would be to hook the global keyboard using SetWindowsHookEx and a KeyboardProc. This will have a deleterious effect on the user experience.
I went and coded up a really simple example of what I'm talking about. The following code makes a window that is not minimized OR covered by a user hitting Windows+D. Note that on Windows 7, gadgets on the desktop can still be brought above it; which I can't really explain.
#include <windows.h>
#include <tchar.h>
#define WIN_TITLE _T("Resists Win+D Window")
#define WIN_CLASS _T("Resists Win+D Class")
LRESULT CALLBACK CustomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//Special behavior goes here
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HWND CreateMainWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = CustomWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = WIN_CLASS;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
exit(1);
}
HWND hWnd = CreateWindowEx(
WS_EX_TOOLWINDOW,
WIN_CLASS,
WIN_TITLE,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
100,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
exit(1);
}
return hWnd;
}
/*
Main entry point
*/
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hwnd = CreateMainWindow(hInstance);
ShowWindow(hwnd, nCmdShow);
SetWindowPos(hwnd, HWND_TOPMOST, -1, -1, -1, -1, SWP_NOMOVE | SWP_NOSIZE);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
In your ABN_FULLSCREENAPP notification, you need to determine whether the window occupying the working area is the Desktop and if so, ignore the ABN_FULLSCREENAPP message.
P.S. As an alternative implementation, consider the commercial ShellAppBar component.
In addition to the answer of JKS, here is working code for VB.NET, assuming you already converted your form to an appbar. You need to p/invoke the functions FindWindow and SetFormOnDesktop.
'In your form
Public Sub New()
'Stuff
SetFormOnDesktop(Me.Handle)
'More stuff
End Sub
'In your form or somewhere else.
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function SetParent(_
ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function
Public Sub SetFormOnDesktop(hwnd As IntPtr)
Dim hwndf As IntPtr = hwnd
Dim hwndParent As IntPtr = FindWindow("ProgMan", Nothing)
SetParent(hwndf, hwndParent)
End Sub

Resources