I am having trouble using the function SHAutoComplete. It simply does not work correctly when I use it on a edit box whose parent window is not a dialog box.
The auto-complete functionality seems to be working fine, but the rendering of the drop-down list with the possible candidates based on what was typed on the edit box is very messed up. Basically only the borders of the drop-down are shown. The borders are rendered wide enough to fit the possible suggestions, but the suggestions themselves are never drawn. Even the drop-down list background color is wrong. It is as if it was never painted and remains with the original parent window color.
And if the number of suggestions is big enough so the drop-down needs a scroll-bar, the scrollbar also does not get rendered correctly - the arrows do not get drawn.
On both cases, with or without scrollbars, the drop-down list does not accept mouse input, i.e., I cannot click on the items. If I press the "down" key on the keyboard while the drop-down is being shown, it kind of works as expected. After the second or third press the items finally start to appear. But the scrollbar still is does not get rendered correctly.
If instead of registering my own windows class I simply use a dialog with ::DialogBoxParam(), then it all goes as expected. The auto-complete works without any problems at all.
Here is what I am doing. This code will register a window class, create the main window, create an edit box and then call SHAutoComplete on it. It must be linked with Shlwapi.lib
// this code must be linked with Shlwapi.lib
#include <Windows.h>
#include <Shlwapi.h>
// name of the class that will be created for the main window
static const char WindowClassName[] = "SHAutoCompleteDoesNotWorkWithoutADialogWindowClassName";
// the main window procedure
static LRESULT CALLBACK WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
{
HWND hwndEdit = ::CreateWindowEx(
0,
"EDIT",
0,
WS_CHILD | WS_VISIBLE,
10,
10,
300,
25,
hwnd,
NULL,
NULL,
0);
::SHAutoComplete(hwndEdit, SHACF_DEFAULT);
return 0;
}
case WM_DESTROY:
::PostQuitMessage(1);
return 0;
default:
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
// the app entry point
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
::CoInitialize(NULL);
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW ;
wcex.lpfnWndProc = WindowProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = WindowClassName;
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
ATOM atom = ::RegisterClassEx(&wcex);
HWND hwnd = ::CreateWindowEx(
0,
MAKEINTATOM(atom),
"SHAutoComplete Test",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
MSG msg;
while(::GetMessage(&msg, hwnd, 0, 0) > 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::UnregisterClass((LPCTSTR)atom, NULL);
::CoUninitialize();
return 0;
}
That code produces the following:
the drop-down when a scroll bar is needed
http://www.abload.de/img/shautocomplete_2i1sk4.jpg
the drop-down after a couple of presses to the "down" key. Notice how the scroll bar still is not rendered correctly.
http://www.abload.de/img/shautocomplete_3efsgw.jpg
Now, when I switch to Dialog Boxes, works like a charm. In the code below, IDD_DIALOG1 is simply an empty dialog box resource, created automatically by the IDE.
Here is the relevant part of the rc file
IDD_DIALOG1 DIALOGEX 0, 0, 316, 185
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
END
And here is the code that uses it
// this code must be linked with Shlwapi.lib
#include <windows.h>
#include <Shlwapi.h>
#include "Resource.h"
BOOL CALLBACK DialogProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
HWND hwndEdit = ::CreateWindowEx(
0,
"EDIT",
0,
WS_VISIBLE | WS_CHILD,
0,
0,
300,
20,
hwnd,
NULL,
NULL,
0);
::SHAutoComplete(hwndEdit, SHACF_DEFAULT);
return 1;
}
case WM_CLOSE:
::EndDialog(hwnd, 0);
return 1;
default:
return 0;
}
}
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
::CoInitialize(NULL);
::DialogBoxParam(
NULL,
MAKEINTRESOURCE(IDD_DIALOG1),
NULL,
DialogProc,
0);
::CoUninitialize();
return 0;
}
Could you please point out where I am going wrong? As far as I can see, other than the creation and destruction of the main window, there seems to be no difference at all between the two of them. Am I missing something on the SHAutoComplete docs that states it can only be used on edit boxes inside dialogs?
You are using a filtered message loop so any messages for the drop down are not being processed. Pass NULL as the second parameter to GetMessage
Related
I have some problems with making static controls rounded. I can't understand why SetWindowRgn doesn't work for a static control here. Also I've tried SelectClipRgn and it works. But nevertheless why SetWindowRgn doesn't? The Microsoft documentation states that
The system does not display any portion of a window that lies outside
of the window region.
The control must be rounded and clipped according to the documentation. But it is not. Here's my example of the problem:
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, // 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;
}
HRGN hrgnMain = CreateRoundRectRgn(0, 0, 200, 200, 5, 5);
int res = SetWindowRgn(hwnd, hrgnMain, TRUE);
ShowWindow(hwnd, nCmdShow);
HWND hControl = CreateWindow(L"Static", L"hello, world", WS_VISIBLE | WS_CHILD | SS_NOTIFY | SS_LEFTNOWORDWRAP,
20, 20, 40, 40, hwnd, NULL, hInstance, NULL);
HRGN hrgnControl = CreateRoundRectRgn(0, 0, 10, 10, 5, 5);
res = SetWindowRgn(hControl, hrgnControl, TRUE);
ShowWindow(hwnd, nCmdShow);
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);
}
Some builtin controls use CS_PARENTDC class style.
CS_PARENTDC sets the clipping region of the child window to that of the parent window. This conflicts with SetWindowRgn which wants to install custom region. Depending how your window is refreshed you could get different combinations of custom region working or not. For example if you resize parent window you can get your control refreshed partially with region set and partially without region.
Parent Display Device Contexts states that:
The system ignores the CS_PARENTDC style if the parent window uses a
private or class device context, if the parent window clips its child
windows, or if the child window clips its child windows or sibling
windows.
But it looks that setting only WS_CLIPCHILDREN for parent windows is not enough. Adding WS_CLIPSIBLINGS or WS_CLIPCHILDREN flags in control styles (even if you have only one child) triggers desired behavior.
HWND hControl = CreateWindow(
L"Static",
L"hello, world",
WS_CLIPSIBLINGS | WS_VISIBLE | WS_CHILD | SS_NOTIFY | SS_LEFTNOWORDWRAP,
20, 20, 40, 40,
hwnd,
NULL,
hInstance,
NULL);
Your code without WS_CLIPSIBLINGS
And with WS_CLIPSIBLINGS
Alternatively the CS_PARENT style could be removed by using GetClassLongPtr and SetClassLongPtr. Because CS_PARENT is used only to reuse clipping region it shouldn't have any other unexpected effects.
SetClassLongPtr(
hControl,
GCL_STYLE,
GetClassLongPtr( hControl, GCL_STYLE ) & ~CS_PARENTDC );
I'm starting programming in C++ and want to start making applications with graphics and user interface.
I've watched many tutorials on the subject,
and have finished the first part of the tutorial
but the window refused to pop up, and I know that it's working because there are no errors,
and I can see it running in task manager.
Please help.
Hear is the code:
#include <Windows.h>
bool running = true;
LRESULT CALLBACK windows_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch (uMsg) {
case WM_CLOSE:
case WM_DESTROY: {
running = false;
}break;
default: {
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return result;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
//compile window
WNDCLASS window_class = {};
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpszClassName = 0;
window_class.lpfnWndProc = windows_callback;
//register clases
RegisterClass(&window_class);
// create window
HWND window = CreateWindowA(0, "game stuff", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 720, 360, 0, 0, hInstance, 0); {
while (running) {
// input
// simulate
MSG mesage;
while (PeekMessage(&mesage, window, 0, 0, PM_REMOVE)) {
TranslateMessage(&mesage);
DispatchMessage(&mesage);
}
// render
}
};
}
window refused to pop up, and I know that it's working because there
are no errors, and I can see it running in task manager.
You don't check the error for RegisterClass and CreateWindowA functions calls.
Check the return value of RegisterClass() you will find that the return value is zero, that indicates failure. To get extended error information, call GetLastError.
CreateWindowA() also fails, the return value is NULL. To get extended error information, call GetLastError.
If you use ANSI version of these functions, change WNDCLASS to WNDCLASSA, RegisterClass to RegisterClassA.
You missed class name in both two functions. The following is an example based on your presented code to make the window show up. You can have a try.
CHAR clsName[] = "test";
WNDCLASSA window_class = {};
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpszClassName = clsName;
window_class.lpfnWndProc = windows_callback;
//register clases
ATOM atom = RegisterClassA(&window_class);
if (0 == atom)
{
DWORD err = GetLastError();
return 0;
}
// create window
HWND window = CreateWindowA(clsName, "game stuff", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 720, 360, 0, 0, hInstance, 0);
if (NULL == window)
{
DWORD err = GetLastError();
return 0;
}
More references: "Your First Windows Program", "Unicode in the Windows API".
Update:
why does it have a Chinese name (window's name displays unexecpted)
For using above sample code to display window's name expected you can change project setting to "Use Multi-Byte Character Set" like this:
Or you can use UNICODE APIs (RegisterClassW and CreateWindowW) with project setting "Use Unicode Character Set".
Or you can use the macros like RegisterClassEx and CreateWindowEx, it will choose UNICODE or ASCII version API along with your project setting automatically.
I have some problems with making static controls rounded. I can't understand why SetWindowRgn doesn't work for a static control here. Also I've tried SelectClipRgn and it works. But nevertheless why SetWindowRgn doesn't? The Microsoft documentation states that
The system does not display any portion of a window that lies outside
of the window region.
The control must be rounded and clipped according to the documentation. But it is not. Here's my example of the problem:
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, // 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;
}
HRGN hrgnMain = CreateRoundRectRgn(0, 0, 200, 200, 5, 5);
int res = SetWindowRgn(hwnd, hrgnMain, TRUE);
ShowWindow(hwnd, nCmdShow);
HWND hControl = CreateWindow(L"Static", L"hello, world", WS_VISIBLE | WS_CHILD | SS_NOTIFY | SS_LEFTNOWORDWRAP,
20, 20, 40, 40, hwnd, NULL, hInstance, NULL);
HRGN hrgnControl = CreateRoundRectRgn(0, 0, 10, 10, 5, 5);
res = SetWindowRgn(hControl, hrgnControl, TRUE);
ShowWindow(hwnd, nCmdShow);
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);
}
Some builtin controls use CS_PARENTDC class style.
CS_PARENTDC sets the clipping region of the child window to that of the parent window. This conflicts with SetWindowRgn which wants to install custom region. Depending how your window is refreshed you could get different combinations of custom region working or not. For example if you resize parent window you can get your control refreshed partially with region set and partially without region.
Parent Display Device Contexts states that:
The system ignores the CS_PARENTDC style if the parent window uses a
private or class device context, if the parent window clips its child
windows, or if the child window clips its child windows or sibling
windows.
But it looks that setting only WS_CLIPCHILDREN for parent windows is not enough. Adding WS_CLIPSIBLINGS or WS_CLIPCHILDREN flags in control styles (even if you have only one child) triggers desired behavior.
HWND hControl = CreateWindow(
L"Static",
L"hello, world",
WS_CLIPSIBLINGS | WS_VISIBLE | WS_CHILD | SS_NOTIFY | SS_LEFTNOWORDWRAP,
20, 20, 40, 40,
hwnd,
NULL,
hInstance,
NULL);
Your code without WS_CLIPSIBLINGS
And with WS_CLIPSIBLINGS
Alternatively the CS_PARENT style could be removed by using GetClassLongPtr and SetClassLongPtr. Because CS_PARENT is used only to reuse clipping region it shouldn't have any other unexpected effects.
SetClassLongPtr(
hControl,
GCL_STYLE,
GetClassLongPtr( hControl, GCL_STYLE ) & ~CS_PARENTDC );
My App is a DLL and i'm injecting it into a (Game) process .
When i use LoadBitmap() and use MAKEINTRESOURCE(IMAGE_RESOURCE_NAME)
Like this :
MyImage = LoadBitmap(hInstance, MAKEINTRESOURCE(IMAGE_RESOURCE_NAME))
SendMessage(MyButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)MyImage);
The Create Button code :
MyButton = CreateWindow("BUTTON", "My Button", WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_FLAT, 17, 18, 110, 30, hwnd, (HMENU)ButtonId, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
LoadBitmap() works when i Inject the DLL into any application but the game.
i think because when i inject the DLL to the game, it won't load from Resources and the image doesn't appear. so i'm not able to use LoadBitmap from Resources. somehow the Resources dosen't go with the DLL data to the Game and the game doesn't find the resources so it can't find the Image.
So alternatively i tried to use LoadImage() from disk file. and that way it worked and the Image appears on the button.
When i Inject it to any application like notepad it appears like this :
(That's what i want it to be like)
But when i inject the DLL to the game, the button appears in a border & 3D Effect :
With a lot of searching i assumed that The Game i'm injecting to doesn't apply Visual Styles to my DLL GUI Window and the Buttons appear in the Classy look, Border & 3D Effect. even BS_FLAT doesn't apply to the button.
Here's the full code I'm using :
#include "stdafx.h"
#include "Process.h"
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <tchar.h>
#include "resource.h"
HINSTANCE hInstance;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc = { 0 };
HWND MainHwnd;
MSG Msg;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(30, 30, 30)));
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpszMenuName = NULL;
wc.lpszClassName = "My Application";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, std::to_string(GetLastError()).c_str(), "RegisterClassEx!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
MainHwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"Application",
"My Application",
WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 280,
NULL, NULL, hInstance, NULL);
if (MainHwnd == NULL)
{
MessageBox(NULL, std::to_string(GetLastError()).c_str(), "CreateWindow!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(MainHwnd, nCmdShow);
UpdateWindow(MainHwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
int MyButtonId = 1000;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE: {
HWND MyButton;
HBITMAP MyImage;
MyButton = CreateWindow("BUTTON", "A Button Text", WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_FLAT, 17, 18, 110, 30, hwnd, (HMENU)MyButtonId, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
///////// --->
// Here I'm using one of these :
// Using LoadImage()
MyImage = (HBITMAP)LoadImage(hInstance, "UI\\myimage.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_LOADFROMFILE);
// Using LoadBitmap() | My_Bitmap is an image resource name
MyImage = LoadBitmap(hInstance, MAKEINTRESOURCE(My_Bitmap));
///////// <---
SendMessage(MyButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)MyImage);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
unsigned long __stdcall Win_Thread(LPVOID Param)
{
WinMain(NULL, NULL, NULL, 1);
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
// Set hInstance to hModule
hInstance = hModule;
CreateThread(0, 0, LPTHREAD_START_ROUTINE(Win_Thread), hModule, 0, 0);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
I think i have two options.
1. try to make the game find my Images from Resources and use LoadBitmap() from Resources. so the button won't be with border & 3d effect.
2. continue using LoadImage() from disk file, and try to hide the border & 3d Effect, e.g Enable Visual Styles for my DLL GUI.
Unfortunately i couldn't do any of those and have no idea how to do that, i'm searching the whole internet but didn't find anything about that.
How could i achieve that, any ideas?
Unfortunately, your pursuit of LoadBitmap is a snipe hunt. Visual Styles are the only thing causing the different appearance. Even if you get the code for using a resource working, you'll still have the wrong appearance unless you enable visual styles.
MSDN has a reference specifically for using Visual Styles in a plugin DLL, when the main application doesn't use them:
Adding Visual Style Support to an Extension, Plug-in, MMC Snap-in or a DLL That Is Brought into a Process
The gist is that you need to use the ISOLATION_AWARE_ENABLED macro and manifest your DLL for visual styles.
You will also need to call InitCommonControlsEx. That's mentioned in several other sections of the above document. For flat buttons, pass the ICC_STANDARD_CLASSES flag (inside the structure).
You do have a couple mistakes in your code, and these might prevent visual styles from properly activating even when you do the manifesting and isolation.
Your DLL should not have a WinMain function. Let there be one function doing all the work that gets called from both WinMain and the DLL thread, instead of having the DLL thread call WinMain. This isn't wrong by itself, just bad style, but it caused the next error which is a bigger problem:
Your hInstance parameter hides the global hInstance variable, resulting in the wrong value for wc.hInstance. If WinMain and DllMain both set the global variable, and then all the rest of the code used the global, you wouldn't be having this problem. But fixing it needs code running in the EXE and not the DLL, which means removing the call from the DLL thread to WinMain.
INTRODUCTION:
I am creating tab control with child dialog boxes as pages.
I have Visual Styles enabled via #pragma comment. I have also called InitCommonControlsEx and #pragma comment( lib, "comctl32.lib" ) as well.
Initially, when window loads, dialog and its common controls have proper background, please see image below:
During resizing things are not so consistent -> background starts to mismatches visibly. I will provide screenshot below:
You can clearly see that checkbox and static control have improper background, while it seems to me that dialog box ( created to act as a child control ) has proper background.
Edited on November 24th, 2014:
After enclosing controls into group boxes there seems to be no painting problems. My monitor is old CRT ( Samsung SyncMaster 753s ), and I have bad eyesight, but again, it seems that everything paints properly. The window still flickers horribly on resize, but I have tried everything in my power to fix it.
QUESTION:
How can I fix this?
MY EFFORTS TO SOLVE THIS:
I haven't found anything yet but I am still Goggling while typing this question...
RELEVANT INFORMATION:
Here are the instructions for creating demo that illustrates the problem:
1.) Create empty C++ project in Visual Studio;
2.) add header file, name it pomocne_funkcije.h and copy/paste following:
#include <windows.h>
#include <windowsx.h>
#include <comutil.h>
#include <commctrl.h>
#include <stdio.h>
#include <vector>
#include <ole2.h>
#include <string>
#include <stdlib.h>
#include <locale.h>
#include <Uxtheme.h>
#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
#pragma comment( lib, "comctl32.lib")
#pragma comment( lib,"Msimg32.lib")
#pragma comment( lib, "comsuppw.lib")
#pragma comment( lib, "UxTheme.lib")
3.) Create dialog box in resource editor, add checkbox static control.
Set the following for dialog box:
Border : none
Control : true
Control parent : true
Style : child
System menu : false
4.) Here is the code for main.cpp :
#include "pomocne_funkcije.h"
static HINSTANCE hInst;
// dialog procedure for firts tab
INT_PTR CALLBACK Ugovori(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
{
EnableThemeDialogTexture( hDlg, ETDT_ENABLETAB );
}
return (INT_PTR)TRUE;
}
return (INT_PTR)FALSE;
}
// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hDlgFirstTab; // handle to the first page dialog box
switch(msg)
{
case WM_CREATE:
{
RECT rcClient = {0};
::GetClientRect( hwnd, &rcClient );
HWND hwndTab = CreateWindowEx( 0, WC_TABCONTROL,
L"Ugovori", WS_CHILD | WS_VISIBLE,
10, 10, rcClient.right - rcClient.left - 20,
rcClient.bottom - rcClient.top - 63,
hwnd, (HMENU)3000,
((LPCREATESTRUCT)lParam)->hInstance, 0 );
TCITEM tci = {0};
tci.mask = TCIF_TEXT;
tci.pszText = L"Основни подаци";
TabCtrl_InsertItem( hwndTab, 0, &tci );
// set font so cyrilic symbols can be properly displayed instead of ???
SendMessage( hwnd, WM_SETFONT,
(WPARAM)(HFONT)GetStockObject(DEFAULT_GUI_FONT),
(LPARAM)TRUE );
SendMessage( hwndTab, WM_SETFONT,
(WPARAM)(HFONT)GetStockObject(DEFAULT_GUI_FONT),
(LPARAM)TRUE );
// create page ( dialog box )
hDlgFirstTab = CreateDialog( ((LPCREATESTRUCT)lParam)->hInstance,
MAKEINTRESOURCE(IDD_DIALOG1),
hwnd,
(DLGPROC)Ugovori ); // dialog procedure
ShowWindow( hDlgFirstTab, SW_SHOW );
}
return 0L;
case WM_MOVE:
case WM_MOVING:
case WM_SIZING:
case WM_SIZE:
{
RECT rcClient = {0};
GetClientRect( hwnd, &rcClient );
SetWindowPos( GetDlgItem( hwnd, 3000 ), NULL,
rcClient.left + 10, // move it away from window edge by 10 pixels
rcClient.top + 10, // move it away from window edge by 10 pixels
rcClient.right - rcClient.left - 20,
// - 63 was the size of the button,
// but I have deleted that button here to preserve space
rcClient.bottom - rcClient.top - 63,
SWP_NOZORDER | SWP_NOCOPYBITS );
// get tab control's client rectangle
GetClientRect( GetDlgItem( hwnd, 3000 ), &rcTab );
//============= place dialog box into tab's client area
MapWindowPoints( GetDlgItem( hwnd, 3000 ), hwnd,
(LPPOINT)(&rcTab), 2 );
// get tab's display area
TabCtrl_AdjustRect( GetDlgItem( hwnd, 3000 ),
FALSE, &rcTab );
// move dialog box
SetWindowPos(hDlgFirstTab, NULL,
rcTab.left, rcTab.top,
rcTab.right - rcTab.left,
rcTab.bottom - rcTab.top,
SWP_NOZORDER);
//========================= done
// repaint window
InvalidateRect( hwnd, NULL, FALSE );
}
return 0L;
case WM_ERASEBKGND:
return 1L;
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint( hwnd, &ps );
SendMessage( hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0 );
EndPaint( hwnd, &ps );
}
return 0L;
case WM_PRINTCLIENT:
{
RECT rcClient = {0};
GetClientRect( hwnd, &rcClient );
FillRect( (HDC)wParam, &rcClient,
(HBRUSH)GetStockObject(WHITE_BRUSH) );
}
return 0L;
case WM_CLOSE:
::DestroyWindow(hwnd);
return 0L;
case WM_DESTROY:
::PostQuitMessage(0);
return 0L;
default:
return ::DefWindowProc( hwnd, msg, wParam, lParam );
}
return 0;
}
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
// store hInstance in global variable for later use
hInst = hInstance;
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// initialize common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS |
ICC_STANDARD_CLASSES | ICC_TAB_CLASSES;
InitCommonControlsEx(&iccex);
// register main window class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Main_Window";
wc.hIconSm = LoadIcon( hInstance, IDI_APPLICATION );
if(!RegisterClassEx(&wc))
{
MessageBox(NULL,
L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// create main window
hwnd = CreateWindowEx( 0, L"Main_Window",
L"Contract manager",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL, NULL, hInstance, 0 );
if(hwnd == NULL)
{
MessageBox(NULL, L"Nemogu da napravim prozor!", L"Greska!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
I am working in Visual Studio 2008 on Windows XP using C++ and WinAPI.
Your tab dialogs are below the tab control in the Z order. This causes crazy overdraw issues on first resizing. After adding the tab dialogs as childs to your main window, call SetWindowPos(tab, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) to move them to the top of the Z order.
Had this exact issue; took me days to figure it out. I created a simple application in Visual Studio:
manifest reference to ComCtrl v 6
InitCommonControlsEx()
a dialog with a tab control
a sub-dialog for the tab content (sibling to the tab)
in the sub-dialog, a radio button and a static
no fancy handling of WM_PRINTCLIENT or anything alike
just EnableThemeDialogTexture() in WM_INITDIALOG of the sub-dialog
I went to check it on XP and … it worked perfect and looked beautiful, apart from flickering.
I added WS_CLIPCHILDREN and suddenly was able to reproduce your screenshots perfectly. I was first thinking this was the cause.
I kept pushing and added WS_COMPOSITED to prevent flickering, and the tab window suddenly was gone – entirely covered by the tab’s background. Now I realized the Z order was at fault.
Child windows are always created at the bottom of the Z order, i.e. below your tab control. Your code never moves them upwards. The tabs only ever displayed by pure luck/overdraw, producing the artifacts you observe. Fix the Z order and there will be no more artifacts.