I have the problem with my dialog, that when i invoke the function "DialogBoxA" the Dialog doesn't appear. I only get the "Busy Cursor" and the the cursor is also trapped inside of a small rectangle (the size of the dialog). After the press of (almost) any button it magically appears.
Dialog box call
DialogBoxA(hInstance,MAKEINTRESOURCEA(IDD_ORACLE_DIALOG),0,reinterpret_cast<DLGPROC>(DlgProc));
Dialog resource code
IDD_ORACLE_DIALOG DIALOGEX 0, 0, 155, 71
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,43,50,50,14
PUSHBUTTON "Cancel",IDCANCEL,98,50,50,14
COMBOBOX IDC_COMBOSERVER,53,6,94,30,CBS_DROPDOWN | CBS_SORT | CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
EDITTEXT IDC_EDITLOG,54,25,94,14,ES_AUTOHSCROLL
LTEXT "Server",IDC_STATIC,7,7,47,13
LTEXT "Log",IDC_STATIC,7,25,47,13
END
DialogProc
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam,LPARAM lParam){
switch(Msg){
case WM_INITDIALOG:
for(auto it = vServerList.begin();it!=vServerList.end();++it){
SendMessageA(GetDlgItem(hWndDlg,IDC_COMBOSERVER),CB_ADDSTRING,0,reinterpret_cast<LPARAM>(it->c_str()));
}
if(vServerList.size()>0){
SendMessage(GetDlgItem(hWndDlg,IDC_COMBOSERVER),CB_SETCURSEL,0,0);
SendMessage(GetDlgItem(hWndDlg,IDC_EDITLOG),WM_NCLBUTTONDOWN,HTCAPTION,0);
}else{
SendMessage(GetDlgItem(hWndDlg,IDC_COMBOSERVER),WM_NCLBUTTONDOWN,HTCAPTION,0);
}
#ifdef _DEBUG
SendMessageA(GetDlgItem(hWndDlg,IDC_EDITLOG),WM_SETTEXT,0,reinterpret_cast<LPARAM>("q40log"));
#endif
return TRUE;
case WM_COMMAND:
switch(wParam){
case IDOK:
size_t tServer,tLog;
tServer = GetWindowTextLength(GetDlgItem(hWndDlg,IDC_COMBOSERVER))+1;
tLog = GetWindowTextLength(GetDlgItem(hWndDlg,IDC_EDITLOG))+1;
char *pszServer,*pszLog;
pszServer = static_cast<char*>(calloc(tServer,sizeof(char)));
pszLog = static_cast<char*>(calloc(tLog,sizeof(char)));
GetDlgItemTextA(hWndDlg,IDC_COMBOSERVER,pszServer,tServer);
GetDlgItemTextA(hWndDlg,IDC_EDITLOG,pszLog,tLog);
szServer = pszServer;
szUser = pszLog;
free(pszServer);
free(pszLog);
EndDialog(hWndDlg,0);
return TRUE;
case IDCLOSE:
case IDCANCEL:
EndDialog(hWndDlg,0);
return FALSE;
}
break;
}
return FALSE;
}
The obvious failure modes are:
The dialog procedure is faulty.
The dialog resource is invalid.
The dialog resource is linked incorrectly and so cannot be loaded.
The instance handle is incorrect.
Of these options, the cast that you used in the dialog procedure parameter, it seems very likely that the dialog procedure is faulty.
I expect that you added the cast because the code would not compile. And it would not compile because the dialog procedure has an incorrect signature. The cast does not change that fact. It is just a way for you to suppress the compiler and tell it that you know better than it does. Generally speaking, the compiler tends to be right. If your dialog procedure has the wrong signature you need to correct it. Refer to the documentation to find out the correct signature.
The other big problem with your code is that you ignore the return value of DialogBox. If the function fails then the documentation tells you that a value of 0 or -1. In that scenario you can call GetLastError to get extended error information. You must get into the habit of checking for errors when using Win32 API functions.
Related
I want to prevent moving of a window which I created before using CreateWindowEx.
HWND hWnd = CreateWindowEx(0, wc.lpszClassName, "Title", WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPEDWINDOW, 0, 0, rect.right - rect.left, rect.bottom - rect.top, nullptr, nullptr, nullptr, nullptr);
The most common solution suggested for this is changing the windows style to have no titlebar i.e.
SetWindowLongPtr(hWnd, GWL_STYLE, GetWindowLongPtr(hWnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME));
While this solves the problem for drag- & drop at the titlebar, the window is still movable from the control menu (if the window is active press ALT, LEFT, DOWN and select "Move"). I tried using EnableMenuItem(GetSystemMenu(hWnd, FALSE), SC_MOVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); because this is how the disabling for the Close entry works (using SC_CLOSE instead of SC_MOVE), but it did not disable the Move option.
Is there any way using win32 api to disable only the "Move" option from the context menu without removing the WS_SYSMENU style completly?
The clue to disabling the SC_MOVE item is in the description of the GetSystemMenu function:
The system automatically grays items on the standard window menu, depending on the situation. The application can perform its own checking or graying by responding to the WM_INITMENU message that is sent before any menu is displayed.
So even though you're disabling the menu item initially, the system is re-enabling it when the menu is displayed. To fix it, you need to handle WM_INITMENU or WM_INITMENUPOPUP yourself and override the system's behaviour. For example,
in your window procedure:
case WM_INITMENUPOPUP:
if (wParam == reinterpret_cast<WPARAM>(GetSystemMenu(hWnd, FALSE)))
{
// override handling of the system menu
EnableMenuItem(reinterpret_cast<HMENU>(wParam), SC_MOVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
return 0;
}
// if WM_INITMENUPOPUP isn't for the system menu, fall through to
// default processing
return DefWindowProc(hWnd, uMsg, wParam, lParam);
I am making a win32 api window application project on visual studio 2012.I use a LPWSTR type variable to store my strings described as follow.
LPWSTR MyStringList[3]={L"apple",L"orange",L"watermelon"};
I expect to copy the text from an editbox to one of the strings in MyStringList. Therefore,I made a simple editbox and button.
Here is the definition of editbox and button.
case WM_CREATE:
hEdit = CreateWindow(L"EDIT",
L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
10, 10, 200, 25,
hWnd,
(HMENU)ID_EDIT,
GetModuleHandle(NULL),
NULL);
hBtn = CreateWindow(L"BUTTON",
L"",
WS_CHILD | WS_VISIBLE,
250, 10, 50, 30,
hWnd,
(HMENU)ID_BUTTON,
GetModuleHandle(NULL),
NULL);
Here is the action when the button is pushed.The two messagebox are used to see if the string has been changed after function GetWindowText is called.
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case ID_BUTTON:
MessageBox(hWnd,MystringList[1],L"Before_Pusing",MB_OK);
GetWindowText(hEdit,MystringList[1],sizeof(MystringList[1]));
MessageBox(hWnd,MystringList[1],L"After_Pushing",MB_OK);
InvalidateRect(hWnd,NULL,TRUE);
break;
Now I tried to push the button after typing "banana" into the editbox. The second string "orange" should be replaced with "banana". However,it turned out that nothing changed .The second messagebox displayed "orange" as the first messagebox did. What's wrong with my code? Please help! Thanks a lot!
You could try this, adding whatever programming quirks that Windows needs.
#define MYLEN 20
...
char MyStringList[3][MYLEN+1] = {"apple", "orange", "watermelon"};
Then you can fetch the string from the edit box with
GetWindowText(hEdit,MystringList[1],MYLEN);
Although the GetWindowText() documentation says
"Copies the text of the specified window's title bar (if it has one) into a buffer. If the specified window is a control, the text of the control is copied. However, GetWindowText cannot retrieve the text of a control in another application."
I have a textbox that is not set to "read-only", so theoretically I should be able to write to it. It is activated with Edit_Enable(hwnd, true), Edit_SetReadOnly(hwnd, false) and SetFocus(hwnd). I can give the textbox focus by clicking on it and I can even see the caret blinking, but however, when I press any key the textbox receives no input. I can set its text with Edit_SetText(), but I cannot write anything manually to it.
I create this textbox with following code:
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL /*| ES_OEMCONVERT*/ | ES_LEFT /*| ES_WANTRETURN */| WS_TABSTOP;
HWND h = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", (LPCWSTR)NULL, dwStyle, posX, posY, width, height, hParent, NULL, (HINSTANCE)GetModuleHandle(NULL), NULL);
Messages are handled in this function. It is called every frame.
bool PumpMessages()
{
MSG msg;
ZeroMemory( &msg, sizeof(MSG) );
bool bQuit = false;
// Use PeekMessage() so we can use idle time to update the system.
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Translate and dispatch the message
TranslateMessage(&msg);
// message bug workaround
if (msg.message == WM_QUIT)
{
bQuit = true;
}
DispatchMessage(&msg);
}
return bQuit;
}
And the message procedure looks like following:
LRESULT CALLBACK Window::WndProcThunk(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_CHAR)
Log("WM_CHAR");
...
// Finally, if the message isn't consumed by the window or any registered listeners, let Windows do its thing.
return msgConsumed ? 0 : DefWindowProc(hWnd, msg, wParam, lParam);
}
The second strange thing happens here in the WndProcThunk message procedure. No WM_CHAR messages are logged. I think this is the reason that my TextBox doesn't do anything, because it needs WM_CHAR messages to react on user key presses. I have to find the reason why no WM_CHAR messages are sent, even though I use TranslateMessage() in my PumpMessages function. Any ideas?
PeekMessage returns FALSE if no messages are in the message queue, so your message loop will exit almost immediately. You need to either switch to using GetMessage or refactor the loop so that you are using your bQuit flag as a test rather than the return value of PeekMessage.
I solved the problem by disabling the DirectInput API. DirectInput 8 was actually eating all WM_KEYDOWM messages, which resulted in no WM_CHAR messages being sent. But the textbox needs WM_CHAR messages to react to user input. This is a strange behaviour of the API. If anyone has ideas to fix this problem without disabling DirectInput, feel free to post it. But I will use GetAsyncKeyState() for keyboard input from now on.
I'm creating a program in which I want to place a button near to bottom-right corner of the Window. I'm using GetClientRect to get top, bottom, right and left of the window. Top and left are working fine but bottom and right are not working. Here's my code:
WNDCLASSEX Program;
/*Class declaration*/
hWndMain = CreateWindowEx (WS_EX_APPWINDOW,
"Program",
"Program",
WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX,
GetSystemMetrics(SM_CXSCREEN)/2-210,
GetSystemMetrics(SM_CYSCREEN)/2-135,
420,270,
HWND_DESKTOP,
NULL,hInstance,NULL);
//Window Procedure
WM_CREATE:
{
RECT MaxSize;
GetClientRect(hWndMain,&MaxSize);
/*Menu declaration using CreateMenu, AppendMenu etc*/
HWND hCalculate = CreateWindowEx(0,WC_BUTTON,
"Calculate",
WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_DEFPUSHBUTTON | 0x00000001,
MaxSize.right-156,MaxSize.bottom-51,140,30,
hWnd,(HMENU)IDC_BUTTON1,
GetModuleHandle(NULL), 0);
}
After compiling the code and running the program I'm not able to see the button. Please help.
This assignment to hWndMain looks like it's in your mainline code:
hWndMain = CreateWindowEx (WS_EX_APPWINDOW,
While this is in the WndProc:
//Window Procedure
WM_CREATE:
{
...
GetClientRect(hWndMain,&MaxSize);
However, the WM_CREATE message is received and processed within the call to CreateWindow, so it hasn't yet returned and so the assignment to hWndMain hasn't yet taken place. So you're likely calling GetClientRect() with an invalid or NULL hWndMain, and it's likely failing and returning an error which you're ignoring. Instead, use the hwnd parameter that's passed to the WndProc.
I want to create an array of 256 colored buttons with the owner draw extended style to a dialog box created with the visual studio dialog design tool. I added a loop to the WM_INITDIALOG message handler in the dialog procedure to do this:
for (i=0; i<=255; i++)
{
int xp, yp;
HWND status;
xp = rect_pos.left+16*(i%16);
yp = rect_pos.top+16*(i>>4);
status = CreateWindow (
TEXT("button"),
"\0",
WS_CHILD|WS_VISIBLE|BS_OWNERDRAW|BS_PUSHBUTTON,
xp,
yp,
15,
15,
hDlg,
(HMENU) 5000+i, // id used to report events
hInst,
NULL
);
if (status == NULL)
xp =7;
}
I added a message handler for the WM_CTLCOLORBTN message.
case WM_CTLCOLORBTN:
{
int zz;
zz = GetWindowLong ((HWND) lParam, GWL_ID); // window identifier
zz -= 5000;
if ((zz >= 0) && (zz <= 255))
{
HBRUSH BS;
SetTextColor ((HDC) wParam, Collector.Color);
SetBkColor ((HDC) wParam, Collector.Color);
return ((LRESULT) Collector.Brush);
}
break;
}
It more or less works but only the first 64 buttons are displayed. I intend to use a different brush to color each button but for debug puproses, I substituted a single well defined brush. I've debugged the code and satisfied myself the x/y coordinates are proper for each button and that the ID provided in the hMenu createwindow call is proper. I watched all 256 buttons get colored in the WM_CTLCOLORBTN handler. I included a check to make sure the createwindow call does not return failure (NULL). I can get either 4 rows of 16 buttons or 4 columns of 16 buttons by interchanging the x/y parameters on the createwindow call.
If I remove the BS_OWNERDRAW bit from the createwindow call, all 256 buttons are drawn.
It's as if there a limit of 64 buttons with BS_OWNERDRAW :-(
Any help would be greatly appreciated!
TIA, Mike
Are you handling the WM_DRAWITEM message in conjunction with the BS_OWNERDRAW style?
In your case, it seems surprising to me that any buttons are displayed while using the BS_OWNERDRAW style, while BS_PUSHBUTTON is set.
As mentioned in the following link to the documentation for BS_OWNERDRAW, you need to handle WM_DRAWITEM and avoid specifying any other BS_ button styles.
Button Styles from MSDN
Also curious is that the WM_CTLCOLORBUTTON message may be received and then ignored for buttons containing the BS_PUSHBUTTON style. Check out the following link for the documentation on that window message.
WM_CTLCOLORBUTTON from MSDN
From what I can see in your code snippet, most likely you will want to do the following:
Set BS_OWNERDRAW when creating the child buttons.
Handle WM_DRAWITEM on the dialog and draw the button in its correct state. Note that you don't have to handle WM_CTLCOLORBUTTON, just use the Brushes and Fonts and modify the DC as you wish inside your WM_DRAWITEM handler.
Also, depending on your application, you might benefit from making your own window class to represent a grid of buttons on your own, and just drawing the items to taste. This is preferable if you're just displaying internal state and not really looking for the user to manage or interact with a grid of buttons.
Thanks to all who gave advice and help. I now have this working to my satisfaction. I have successfully colored the buttons and surrounded them with a black outline if selected or if they have the input focus. I'll add some code snippets below to show the final state of things but here is synopsis. First, I believe there is some legacy code in my system which makes owner drawn buttons respond to the WM_CTLCOLORBTN for the first child 64 buttons created. Second, I believe the only thing one needs to do is create the buttons, respond properly to the WM_DRAWITEM and WM_COMMAND/BN_CLICKED messages.
Here are the code snippets from my dialog box handler.
In the WM_INITDIALOG code -- create the buttons
for (i=0; i<=255; i++)
{
int xp, yp;
HWND status;
xp = rect_pos.left+16*(i&0x0F);
yp = rect_pos.top+16*(i>>4);
status = CreateWindow
(
TEXT("button"),
"\0",
WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_OWNERDRAW,
xp,
yp,
15,
15,
hDlg,
(HMENU) (5000+i), // id used to report events
hInst,
NULL
);
if (status == NULL)
xp =7;
SetFocus (status);
}
Respond to the WM_DRAWITEM message
case WM_DRAWITEM: // Owner drawn botton
{
LPDRAWITEMSTRUCT lpDrawItem;
HBRUSH BS, BS_Old;
HPEN PN_Old;
int sz=15;
int cntl;
cntl = LOWORD (wParam) - 5000;
lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
if (lpDrawItem->CtlType != ODT_BUTTON)
return FALSE;
BS = CreateSolidBrush (ColorRef[cntl]);
if (lpDrawItem->itemState & (ODS_SELECTED | ODS_FOCUS))
{
sz = 14;
PN_Old = (HPEN) SelectObject(lpDrawItem->hDC, GetStockObject(BLACK_PEN));
}
else
PN_Old = (HPEN) SelectObject(lpDrawItem->hDC, GetStockObject(NULL_PEN));
BS_Old = (HBRUSH) SelectObject(lpDrawItem->hDC, BS);
Rectangle (lpDrawItem->hDC, 0, 0, sz, sz);
SelectObject(lpDrawItem->hDC, PN_Old);
SelectObject(lpDrawItem->hDC, BS_Old);
DeleteObject (BS);
return true;
}
and finally in the WM_COMMAND code
if (HIWORD(wParam) == BN_CLICKED)
{
if ((LOWORD(wParam) >= 5000) && (LOWORD(wParam) <=5255))
{
Color[0] = ColorRef[LOWORD(wParam)-5000] & 0xFF;
Color[1] = (ColorRef[LOWORD(wParam)-5000] >> 16) & 0xFF;
Color[2] = (ColorRef[LOWORD(wParam)-5000] >> 8 ) & 0xFF;
InvalidateRect (hDlg, NULL, TRUE);
goto Set_Color;
}
}