When I run this C code:
// Get rid of the CRT and stuff; we don't need it
#pragma comment(linker, "/Entry:mainCRTStartup")
#pragma comment(linker, "/NoDefaultLib:msvcrt.lib")
#pragma comment(linker, "/NoDefaultLib:kernel32.lib")
#pragma comment(linker, "/NoDefaultLib:ntdll.lib")
#pragma comment(linker, "/Subsystem:Console")
#pragma comment(lib, "user32.lib")
#include <windows.h>
int mainCRTStartup()
{
MSG msg;
HWND hWndParent;
WNDCLASS wndClass =
{
0, &DefWindowProc, 0, 0, NULL, NULL, LoadCursor(NULL, IDC_ARROW),
GetSysColorBrush(COLOR_3DFACE), NULL, TEXT("MyClass")
};
RegisterClass(&wndClass);
hWndParent = CreateWindow(
wndClass.lpszClassName, NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW,
0, 0, 33, 100, NULL, NULL, NULL, NULL);
CreateWindow(TEXT("Button"), wndClass.lpszClassName,
WS_VISIBLE | WS_CHILD | BS_GROUPBOX,
5, 5, 100, 50, hWndParent, NULL, NULL, NULL);
while (GetMessage(&msg, hWndParent, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
I get:
But when I run the equivalent D code:
// Again, get rid of the runtime
pragma(startaddress, mainCRTStartup);
pragma(lib, "dmd_win32.lib");
import win32.windows;
int mainCRTStartup()
{
MSG msg;
HWND hWndParent;
WNDCLASS wndClass =
{
0, &DefWindowProc, 0, 0, NULL, NULL, LoadCursor(NULL, IDC_ARROW),
GetSysColorBrush(COLOR_3DFACE), NULL, "MyClass"
};
RegisterClass(&wndClass);
hWndParent = CreateWindow(
wndClass.lpszClassName, NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW,
0, 0, 33, 100, NULL, NULL, NULL, NULL);
CreateWindow("Button", wndClass.lpszClassName,
WS_VISIBLE | WS_CHILD | BS_GROUPBOX,
5, 5, 100, 50, hWndParent, NULL, NULL, NULL);
while (GetMessage(&msg, hWndParent, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
I get:
I'm so confused... what can possibly be causing this white background?!
Edit:
I'm not sure it's actually a library bug... I just removed the dependency (and I don't see anything wrong with the code), but the problem is still there:
version = Unicode;
extern(Windows):
alias void* HWND, HMENU, HINSTANCE, HCURSOR, HBRUSH, HICON;
alias ushort ATOM, WORD;
alias uint UINT, DWORD;
alias int[2] POINT;
alias int BOOL;
alias int LONG;
alias size_t WPARAM, LPARAM, LRESULT;
alias char* LPSTR;
alias const(char)* LPCSTR;
alias wchar* LPWSTR;
alias const(wchar)* LPCWSTR;
version(Unicode)
{
alias LPCWSTR LPCTSTR;
alias LPWSTR LPTSTR;
alias GetMessageW GetMessage;
alias CreateWindowExW CreateWindowEx;
alias DispatchMessageW DispatchMessage;
alias DefWindowProcW DefWindowProc;
alias LoadCursorW LoadCursor;
alias RegisterClassW RegisterClass;
alias WNDCLASSW WNDCLASS;
}
else
{
alias LPCSTR LPCTSTR;
alias LPSTR LPTSTR;
alias GetMessageA GetMessage;
alias CreateWindowExA CreateWindowEx;
alias DispatchMessageA DispatchMessage;
alias DefWindowProcA DefWindowProc;
alias LoadCursorA LoadCursor;
alias RegisterClassA RegisterClass;
alias WNDCLASSA WNDCLASS;
}
LPCTSTR MAKEINTATOM(ATOM atom) { return cast(LPCTSTR)atom; }
ATOM RegisterClassA(WNDCLASSA*);
ATOM RegisterClassW(WNDCLASSW*);
HCURSOR LoadCursorA(HINSTANCE, LPCSTR);
HCURSOR LoadCursorW(HINSTANCE, LPCWSTR);
LRESULT DefWindowProcA(HWND, UINT, WPARAM, LPARAM);
LRESULT DefWindowProcW(HWND, UINT, WPARAM, LPARAM);
BOOL GetMessageA(const(MSG)*, HWND, UINT, UINT);
BOOL GetMessageW(const(MSG)*, HWND, UINT, UINT);
LONG DispatchMessageA(const(MSG)*);
LONG DispatchMessageW(const(MSG)*);
BOOL TranslateMessage(const(MSG)*);
HWND CreateWindowExA(int, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, void*);
HWND CreateWindowExW(int, LPCWSTR, LPCWSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, void*);
HBRUSH GetSysColorBrush(int);
alias LRESULT function(HWND, UINT, WPARAM, LPARAM) WNDPROC;
enum
{
NULL = null,
COLOR_3DFACE = 15,
BS_GROUPBOX = 7,
}
const LPCTSTR IDC_ARROW = cast(LPCTSTR)32512;
struct MSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}
struct WNDCLASSA
{
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
}
struct WNDCLASSW
{
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
}
enum
{
WS_OVERLAPPED = 0,
WS_TILED = WS_OVERLAPPED,
WS_MAXIMIZEBOX = 0x00010000,
WS_MINIMIZEBOX = 0x00020000,
WS_TABSTOP = 0x00010000,
WS_GROUP = 0x00020000,
WS_THICKFRAME = 0x00040000,
WS_SIZEBOX = WS_THICKFRAME,
WS_SYSMENU = 0x00080000,
WS_HSCROLL = 0x00100000,
WS_VSCROLL = 0x00200000,
WS_DLGFRAME = 0x00400000,
WS_BORDER = 0x00800000,
WS_CAPTION = 0x00c00000,
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,
WS_MAXIMIZE = 0x01000000,
WS_CLIPCHILDREN = 0x02000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_DISABLED = 0x08000000,
WS_VISIBLE = 0x10000000,
WS_MINIMIZE = 0x20000000,
WS_ICONIC = WS_MINIMIZE,
WS_CHILD = 0x40000000,
WS_CHILDWINDOW = 0x40000000,
WS_POPUP = 0x80000000,
WS_POPUPWINDOW = WS_POPUP|WS_BORDER|WS_SYSMENU,
}
pragma(startaddress, mainCRTStartup);
int mainCRTStartup()
{
MSG msg;
HWND hWndParent;
WNDCLASS wndClass =
{
0, &DefWindowProc, 0, 0, NULL, NULL, LoadCursor(NULL, IDC_ARROW),
GetSysColorBrush(COLOR_3DFACE), NULL, "MyClass"
};
ATOM atom = RegisterClass(&wndClass);
hWndParent = CreateWindowEx(
0, wndClass.lpszClassName, NULL, WS_VISIBLE | WS_OVERLAPPEDWINDOW,
0, 0, 33, 100, NULL, NULL, NULL, NULL);
CreateWindowEx(0, "Button", wndClass.lpszClassName,
WS_VISIBLE | WS_CHILD | BS_GROUPBOX,
5, 5, 100, 50, hWndParent, NULL, NULL, NULL);
while (GetMessage(&msg, hWndParent, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Either I'm doing something wrong, or it's a compiler bug... but even if it's a compiler bug, I still have no idea how something like this can happen, because it would need to call very specific APIs! Ideas?
This is subsystem version issue (linker responsibility). Add -L/SUBSYSTEM:CONSOLE:4.0 to dmd command line to fix MyClass background color. Walter likes old systems so default OPTLINK subsystem version is 3.10. Looks like Microsoft's link 9.0 use 5.0 as default subsystem version.
Linux guy here so I will do my best:
It looks like a problem with the library itself. My guess is you got it from here?
https://github.com/AndrejMitrovic/DWinProgramming
In this case the solution is to contact the fellow who wrote the dmd win32 library you are using. If you compiled them from source then certainly there is a way to contact that person. It appears you have found a bug!
PS: One other possibility if you compiled the libraries from source. You may have a compile argument or flag that is different and causes the difference. Also, perhaps it is a compatibility issue with a library dependency. In this case the answer is still to contact the developer of the library. He will probably ask you for some compile output.
Related
It's been years since I last worked with Windows API and I'm trying my hand at it again. I have a simple Window (with title "Test"). I put in the Window message handler under WM_CREATE, 2 CreateWindow calls to create a static and an edit control. I'm pretty sure the coordinates are decent (not overlapping or off the window rectangle). Am I putting the calls under the wrong event? It's just a simple window - not MDI or SDI or anything like that. Here is my code for the entire program. I hope it's sufficient to figure out what I'm doing wrong. I'm using Eclipse CDT with Cygwin's G++ compiler to build it....:
#include <windows.h>
BOOL InitApplication(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "";
wc.lpszClassName = "MainWindow";
return(RegisterClass(&wc));
}
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
HINSTANCE hCurInstance = hinstance;
HWND hWnd = CreateWindow("MainWindow", "Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)(NULL), (HMENU)(NULL), hCurInstance, (LPVOID)(NULL));
if(!hWnd)
return(FALSE);
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return(TRUE);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
CreateWindow("WC_STATIC", "&Test: ", SS_LEFT | WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, NULL, (HINSTANCE)(GetWindowLongPtr(hWnd, GWLP_HINSTANCE)), (LPVOID)(NULL));
CreateWindow("WC_EDIT", "", WS_BORDER | WS_TABSTOP | WS_VISIBLE | WS_CHILD | ES_LEFT, 60, 10, 50, 20, hWnd, NULL, (HINSTANCE)(GetWindowLongPtr(hWnd, GWLP_HINSTANCE)), (LPVOID)(NULL));
return(0);
default:
return(DefWindowProcA(hWnd, uMsg, wParam, lParam));
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmd, int nCmdShow)
{
BOOL fGotMessage;
MSG msg;
if(!InitApplication(hInstance))
return(FALSE);
if(!InitInstance(hInstance, nCmdShow))
return(FALSE);
while(((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) && (fGotMessage != -1))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
WM_CREATE is the correct "event" message to create the child controls in.
You are simply using the wrong class names for the child controls. That is why you are not seeing them. Had you checked the result of those CreateWindow() calls for failures, you would have noticed that CreateWindow() was returning NULL, and GetLastError() was reporting ERROR_CANNOT_FIND_WND_CLASS (1407).
You need to replace "WC_STATIC" with "Static", and replace "WC_EDIT" with "Edit". Or, you can use the pre-defined WC_STATIC and WC_EDIT constants that are defined in <commctrl.h>.
You can also replace GetWindowLongPtr(hWnd, GWLP_HINSTANCE) with NULL when creating system-defined classes, as they are registered globally, not per-module. The HINSTANCE parameter of CreateWindow/Ex() is ignored for them.
On a side note: your GetMessage() loop can be simplified to just:
while (GetMessage(&msg, (HWND) NULL, 0, 0))
See: When will GetMessage return -1?
This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 6 years ago.
I have tried to compile this code:
#include <windows.h>
#include <commctrl.h>
#define ID_TABCTRL 1
#define ID_EDIT 2
#define BTN_ADD 3
#define BTN_DEL 4
#define BTN_CLR 5
#define MAX_TAB_LEN 15
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND hTab, hEdit;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR pCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = { 0 };
wc.lpszClassName = L"Tab control";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Tab control",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 380, 230, 0, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
WINCOMMCTRLAPI BOOL WINAPI InitCommonControlsEx(_In_ const INITCOMMONCONTROLSEX *picce);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
TCITEMW tie;
wchar_t text[4];
LRESULT count, id;
INITCOMMONCONTROLSEX icex;
switch (msg) {
case WM_CREATE:
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
hTab = CreateWindowW(WC_TABCONTROLW, NULL, WS_CHILD | WS_VISIBLE,
0, 0, 200, 150, hwnd, (HMENU)ID_TABCTRL, NULL, NULL);
hEdit = CreateWindowW(WC_EDITW, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
250, 20, 100, 25, hwnd, (HMENU)ID_EDIT, NULL, NULL);
SendMessage(hEdit, EM_SETLIMITTEXT, MAX_TAB_LEN, 0);
CreateWindowW(WC_BUTTONW, L"Add", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
250, 50, 100, 25, hwnd, (HMENU)BTN_ADD, NULL, NULL);
CreateWindowW(WC_BUTTONW, L"Delete", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
250, 80, 100, 25, hwnd, (HMENU)BTN_DEL, NULL, NULL);
CreateWindowW(WC_BUTTONW, L"Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
250, 110, 100, 25, hwnd, (HMENU)BTN_CLR, NULL, NULL);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case BTN_ADD:
GetWindowTextW(hEdit, text, 250);
if (lstrlenW(text) != 0) {
tie.mask = TCIF_TEXT;
tie.pszText = text;
count = SendMessageW(hTab, TCM_GETITEMCOUNT, 0, 0);
SendMessageW(hTab, TCM_INSERTITEMW, count,
(LPARAM)(LPTCITEM)&tie);
}
break;
case BTN_DEL:
id = SendMessageW(hTab, TCM_GETCURSEL, 0, 0);
if (id != -1) {
SendMessageW(hTab, TCM_DELETEITEM, 0, id);
}
break;
case BTN_CLR:
SendMessageW(hTab, TCM_DELETEALLITEMS, 0, 0);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return(DefWindowProcW(hwnd, msg, wParam, lParam));
}
and it seems like this libary:
#include <commctrl.h>
giving me the following error:
error LNK2019: unresolved external symbol __imp__InitCommonControlsEx#4 referenced in function _WndProc#16 C:\Users\User\Desktop\Magshimim\Magshimim EX1\Magshimim EX1\01.obj Magshimim EX1
now the code is an example code from here: http://zetcode.com/gui/winapi/advancedcontrols/
so i figured out that something wrong with my compiler...
can any one help me find the problem and correct it?
edit:
the question is not a duplicate because the refereed duplicate talks in a very general way about the error itself and as a beginner i dont have the skills to build a proper answer from such a question and because that code is been used by many peoples who check the example i think its impotent to make a specific question
Either add #pragma comment(lib, "comctl32.lib") or adjust the linker settings to link against comctl32.lib.
You can check the table at the bottom of a function's MSDN article to find out, which library you are required to link against. Every Windows application is linked against kernel32.dll and every GUI application against user32.dll. Anything else needs to be specified explicitly1.
1 There are exceptions, check IInspectable's comment below.
I want to catch message of WM_DEVICECHANGE.But, there is a problem which i can not understand.I want to see when usb or cd inserted.Maybe my notification filter is wrong.
I m using radstudio and the language of its c,also its commandline application.I think everything is obvious in code.What am i doing wrong,i created window for only getting messages.Also i did not understand how it message going to WndProc from message loop.
#pragma hdrstop
#pragma argsused
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <dbt.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_DEVICECHANGE:
{
MessageBox(0,"a","b",1);
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bRet;
HANDLE a;
HWND lua;
HANDLE hInstance;
MSG msg;
WNDCLASSEX wndClass;
HANDLE hVolNotify;
DEV_BROADCAST_DEVICEINTERFACE dbh;
DEV_BROADCAST_VOLUME NotificationFilter;
lua = CreateWindow("lua", NULL, WS_MINIMIZE, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
wndClass.lpfnWndProc = WndProc;
ZeroMemory(&NotificationFilter, sizeof (NotificationFilter));
NotificationFilter.dbcv_size = sizeof (NotificationFilter);
NotificationFilter.dbcv_devicetype = DBT_DEVTYP_VOLUME;
a = RegisterDeviceNotification(lua,&NotificationFilter,DEVICE_NOTIFY_WINDOW_HANDLE);
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
MessageBox(0,"o","b",1);
if (bRet == -1)
{
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
What am i doing wrong,i created window for only getting messages.
You are asking CreateWindow() to create a window of class "lua" but you have not actually registered the "lua" class via RegisterClass/Ex() before calling CreateWindow(), and you are not checking to see if CreateWindow() returns a NULL window handle on failure.
Also i did not understand how it message going to WndProc from message loop.
That is handled by DispatchMessage(). You need to assign wndClass.lpfnWndProc and register it with RegisterClass() before calling CreateWindow(). Afterwards, when DispatchMessage() sees a message that targets the window created by CreateWindow(), it knows that WndProc() has been associated with that window and will call it directly, passing it the message.
Try this instead:
#pragma hdrstop
#pragma argsused
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <dbt.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (uiMsg == WM_DEVICECHANGE)
{
MessageBox(NULL, TEXT("WM_DEVICECHANGE"), TEXT("WndProc"), MB_OK);
return 0;
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL));
WNDCLASS wndClass = {0};
wndClass.lpfnWndProc = &WndProc;
wndClass.lpszClassName = TEXT("lua");
wndClass.hInstance = hInstance;
if (RegisterClass(&wndClass))
{
HWND lua = CreateWindow(wndClass.lpszClassName, NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (lua != NULL)
{
DEV_BROADCAST_VOLUME NotificationFilter = {0};
NotificationFilter.dbcv_size = sizeof(NotificationFilter);
NotificationFilter.dbcv_devicetype = DBT_DEVTYP_VOLUME;
HDEVNOTIFY hVolNotify = RegisterDeviceNotification(lua, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if (hVolNotify != NULL)
{
MSG msg;
while( GetMessage(&msg, NULL, 0, 0) > 0 )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterDeviceNotification(hVolNotify);
}
DestroyWindow(lua);
}
UnregisterClass(wndClass.lpszClassName, hInstance);
}
return 0;
}
For added measure, you can use CreateWindowEx() instead of CreateWindow() to create a message-only window instead, if desired:
HWND lua = CreateWindowEx(0, wndClass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
You need to set the dbcv_unitmask field of the DEV_BROADCAST_VOLUME structure to indicate which drive letters you're interested in. If you want to see media changes you also need to set the DBTF_MEDIA flag in the dbcv_flags field.
For some reason, as soon as I try to separate certain blocks of code into different functions, adding buttons (bitmaps included) to a toolbar isn't working anymore. Having them put together in one place however works like a charm, however.
Yet I can't figure out the reason for this. Maybe a pointer isn't working as expected...
The expected output is this:
The relevant code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateUserInterface(hwnd);
break;
...
}
return 0;
}
void CreateUserInterface(HWND hwnd)
{
HFONT hfDefault;
HWND hEdit;
HWND hTool;
TBADDBITMAP tbab;
TBBUTTON tbb[TBBSIZE];
HWND hStatus;
int statWidths[] = {100, -1};
// create edit control
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
"EDIT",
"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
0, 0, 100, 100,
hwnd, (HMENU) IDC_MAIN_EDIT,
GetModuleHandle(NULL), NULL);
if(hEdit == NULL)
{
MessageBox(hwnd, "Could not create edit box!", "Error!",
MB_OK | MB_ICONERROR);
}
hfDefault = GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM) hfDefault, MAKELPARAM(FALSE, 0));
// create toolbar
hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
if(hTool == NULL)
{
MessageBox(hwnd, "Could not create tool bar!", "Error!",
MB_OK | MB_ICONERROR);
}
SendMessage(hTool, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
tbab.hInst = HINST_COMMCTRL;
tbab.nID = IDB_STD_SMALL_COLOR;
SendMessage(hTool, TB_ADDBITMAP, 0, (LPARAM) &tbab);
ZeroMemory(tbb, sizeof tbb);
tbb[0].iBitmap = STD_FILENEW;
tbb[0].fsState = TBSTATE_ENABLED;
tbb[0].fsStyle = TBSTYLE_BUTTON;
tbb[0].idCommand = ID_FILE_NEW;
tbb[1].iBitmap = STD_FILEOPEN;
tbb[1].fsState = TBSTATE_ENABLED;
tbb[1].fsStyle = TBSTYLE_BUTTON;
tbb[1].idCommand = ID_FILE_OPEN;
tbb[2].iBitmap = STD_FILESAVE;
tbb[2].fsState = TBSTATE_ENABLED;
tbb[2].fsStyle = TBSTYLE_BUTTON;
tbb[2].idCommand = ID_FILE_SAVE_AS;
SendMessage(hTool, TB_ADDBUTTONS, sizeof(tbb)/sizeof(TBBUTTON), (LPARAM) &tbb);
}
What I get is:
The relevant code:
#define TBBSIZE 1
void CreateUserInterface(HWND hwnd)
{
...
HWND hTool;
TBADDBITMAP tbab;
TBBUTTON tbb[TBBSIZE];
...
CreateToolbar(hwnd, hTool);
InitializeBitmap(hTool, &tbab);
InitializeButtons(htool, tbb, TBBSIZE);
...
}
void CreateToolbar(HWND hwnd, HWND hTool)
{
hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
if(hTool == NULL)
{
MessageBox(hwnd, "Could not create tool bar!", "Error!",
MB_OK | MB_ICONERROR);
}
SendMessage(hTool, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
}
void InitializeBitmap(HWND hTool, TBADDBITMAP *tbab)
{
(*tbab).hInst = HINST_COMMCTRL;
(*tbab).nID = IDB_STD_SMALL_COLOR;
SendMessage(hTool, TB_ADDBITMAP, 0, (LPARAM) tbab);
}
void InitializeButtons(HWND hTool, TBBUTTON *tbb, int size)
{
ZeroMemory(tbb, sizeof(*tbb) * size);
tbb[size-size].iBitmap = STD_FILENEW;
tbb[size-size].fsState = TBSTATE_ENABLED;
tbb[size-size].fsStyle = TBSTYLE_BUTTON;
tbb[size-size].idCommand = ID_FILE_NEW;
SendMessage(hTool, TB_ADDBUTTONS, size, (LPARAM) tbb);
...
}
(Don't mind the status bar to the bottom right, I forgot to include it in the code in the first example)
For obvious reasons the problem must lie somewhere in the bit that handles adding the bitmaps and buttons. But what it is I don't know...what am I missing?
Edit1: To make it read easier I have removed the two additional buttons (less code). It works the same way still, i.e. not working at all. ;-)
EDIT2: Thanks to HostileFork I found out that a windows handle doesn't work quite the same way as a normal raw pointer in C. The solution was to pass the address of hTool to the CreateToolbar function:
#define TBBSIZE 1
void CreateUserInterface(HWND hwnd)
{
...
HWND hTool;
...
CreateToolbar(hwnd, &hTool);
...
}
void CreateToolbar(HWND hwnd, HWND *hTool)
{
*hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
... // and so on
}
It looks like you're assigning to an argument, which will only affect it for the duration of the function:
void CreateToolbar(HWND hwnd, HWND hTool)
{
hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU) IDC_MAIN_TOOL,
GetModuleHandle(NULL), NULL);
...
}
You need to either make hTool a return value, or pass it by reference or pointer so that its value can be bubbled up to the caller and used in the other functions...
Does InitializeButtons have a default value for the third parameter, size? If so, check what it is, as you don't appear to be sending this value in the call to the method. My guess is that the parameter defaults to zero, so this will be passed in the SendMessage call.
If InitializeButtons doesn't have a default value for the third parameter, then either you have a typo in your example source, you have two InitializeButtons methods with different numbers of parameters (and hence are calling the wrong one), or your code shouldn't compile :)
This bug appears only with common control v6 (theme enabled) on XP (seems to work on 7 and 2008). I wonder if someone else might have seen this bug feature.
When you have a single-line TabControl with lots of tabs, a pairs of arrows should appear if there is not enough space to display all the tabs. This is all nice except that the client area is also clipped, which is not nice at all.
Have I miss something ? I played with tabcontrol's window style, but no luck so far.
To illustrate this, it's actually best to see it in action:
#define UNICODE
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
HWND htab, hbut;
int WINAPI WinMain (HINSTANCE instance,
HINSTANCE previnst,
LPSTR args,
int wndState)
{
int i;
MSG messages;
WNDCLASSEX wincl = {
.hInstance = instance, .lpszClassName = L"WindowsApp",
.lpfnWndProc = WindowProcedure, .style = CS_DBLCLKS, .cbSize = sizeof wincl,
.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1)
};
InitCommonControls();
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClassEx (&wincl))
return 0;
HWND hwnd = CreateWindow(L"WindowsApp", L"Windows App", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, instance, NULL);
htab = CreateWindowEx(WS_EX_CONTROLPARENT, WC_TABCONTROL,
L"MyTab", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 10, 10, 514, 325, hwnd,
(HMENU) 10, instance, NULL);
hbut = CreateWindow(
WC_BUTTON, L"My nice button that is clipped", WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 30, 494, 285, htab, (HMENU) IDOK, instance, NULL
);
for (i = 0; i < 10; i ++)
{
WCHAR myBuf[100];
TCITEM tc = {.mask = TCIF_TEXT, .pszText = myBuf};
wsprintf(myBuf, L"My super tab %d", i + 1);
TabCtrl_InsertItem(htab, i, &tc);
}
SendMessage(hbut, WM_SETFONT, (LPARAM) GetStockObject(DEFAULT_GUI_FONT), FALSE);
SendMessage(htab, WM_SETFONT, (LPARAM) GetStockObject(DEFAULT_GUI_FONT), FALSE);
ShowWindow(hwnd, wndState);
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT r;
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
GetClientRect(hwnd, &r);
MoveWindow(htab, 10, 10, r.right-20, r.bottom - 20, TRUE);
MoveWindow(hbut, 10, 30, r.right-40, r.bottom - 60, TRUE);
break;
case WM_COMMAND:
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
Unfortunately I can't test it (I have Win7) but according to http://msdn.microsoft.com/en-us/library/hh298367%28v=VS.85%29.aspx you need to have clip siblings on the tab control and also the parent window. You could also try using the tab control's adjust rect message to get the positions for your button.