Windows API GetOpenFileName with template and hook - windows

I'm trying to use a template with GetOpenFileName without success. I've found very little on this topic in the MSDN or on the web. I've based my attempt on what I saw here
http://visual-c.itags.org/visual-c-c++/77687/
My code follows. The TEMPLATE comments show where I made changes to code b4 the template attempt; mainly to remove certain lines. The normal Windows explorer type open windows is displayed but without the additions I wish to make with the template. I'm not at all sure what should be in the hook function but I know it does not get called since I set a break point there.
// Global variable
OPENFILENAME IFN;
// In WndProc
case WM_CREATE:
IFN.hInstance = ((LPCREATESTRUCT)lParam)->hInstance; // TEMPLATE
IFN.hwndOwner = hWnd; // TEMPLATE
break;
// In WndProc menu processing
case IDM_INPUT_FILE:
{
// OPENFILENAME IFN; // TEMPLATE
strcpy (szFile,"NEWEXPORT.GED");
IFN.lStructSize = sizeof(IFN);
// IFN.hwndOwner = hWnd; // TEMPLATE
// IFN.hInstance = NULL; // TEMPLATE
IFN.lpstrFilter = "All\0*.*\0GEDCOM\0*.GED\0";
IFN.nFilterIndex = 2;
IFN.lpstrCustomFilter = NULL;
IFN.lpstrFile = szFile;
IFN.nMaxFile = 510;
IFN.lpstrFileTitle = NULL;
IFN.lpstrInitialDir = NULL;
IFN.lpstrTitle = NULL;
IFN.Flags = OFN_FILEMUSTEXIST || OFN_PATHMUSTEXIST || OFN_EXPLORER || OFN_ENABLETEMPLATE || OFN_ENABLEHOOK ; // TEMPLATE
IFN.lpstrDefExt = NULL;
IFN.lpfnHook = FileAddOn; // TEMPLATE NULL;
IFN.lpTemplateName = MAKEINTRESOURCE(IDD_FILEADDON); // TEMPLATE
if (!GetOpenFileName(&IFN))
{
Beep (1000,500);
break;
}
// **************** Hook function
UINT_PTR CALLBACK FileAddOn (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return TRUE;
}
// IDD_FILEADDON was made with the visual C dialog editor and has the following properties
// Style=Child, Border=none,clip siblings,3D look

You must OR the OPENFILENAME flags with |, not ||

Related

How to handle results from a PropertySheet?

I find different examples in the Internet of how to program a PropertySheet in WinAPI, but they are not complete.
The code I am using is shown below. I have a PropertySheet with 3 Tabs, each one with a Dialog.
The different Dialogs are called, when I click the Tabs, so far it is working.
However, when I leave the PropertySheet pressing OK button, how do I get the contents in the Textboxes etc. of each Dialog?
Normally I used to do that in the DialogProc when WM_COMMAND/IDOK was received using:
GetDlgItemText( hDlg,IDC_TEXTBOX1, buf, 100);
But in the PropertySheet there is only one OK Button for all dialogs, no WM_COMMAND/IDOK is received in the DialogProc.
What shall I do?
Resource_file:
IDD_DIALOG_1 DIALOGEX 0, 0, 385, 186
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
BEGIN
LTEXT "param",IDC_STATIC,6,23,39,10
EDITTEXT IDC_TEXTBOX1,48,20,237,15
END
C Source:
LRESULT CALLBACK
Dialog1(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
char buf[500];
char* ptr;
int p; // =lParam, rin of edited person
int f;
switch (message)
{
case WM_INITDIALOG:
{
SetDlgItemText(hDlg, IDC_TEXTBOX1, "something");
return 0;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDOK: // never reached (OK Button belongs to the PropertySheet!)
}
}
}
return FALSE;
} /* Dialog1 */
INT_PTR DoPropertySheet(HWND hwndOwner, LPARAM p)
{
PROPSHEETPAGE psp[3];
PROPSHEETHEADER psh;
memset(psp,0,sizeof(psp));
for(int i=0;i<3; i++)
{
psp[i].dwSize = sizeof(PROPSHEETPAGE);
psp[i].dwFlags = PSP_USETITLE;
psp[i].hInstance = hInstance;
psp[i].lParam = p;
}
psp[0].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_1);
psp[0].pfnDlgProc = (DLGPROC)Dialog1;
psp[1].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_2);
psp[1].pfnDlgProc = (DLGPROC)Dialog2;
psp[2].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_3);
psp[2].pfnDlgProc = (DLGPROC)Dialog3;
psh.dwSize = sizeof(PROPSHEETHEADER);
psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW;
psh.hwndParent = hwndOwner;
psh.hInstance = hInstance;
psh.pszIcon = 0;
psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
psh.nStartPage = 0;
psh.ppsp = (LPCPROPSHEETPAGE) &psp;
psh.pfnCallback = NULL;
if (PropertySheet(&psh)) // 0:cancel, otherwise:1
{
//get contens of propertySheet here?? how??
}
return 0;
}
when user press OK or Apply all your pages got PSN_APPLY notification code. so you need look for WM_NOTIFY with PSN_APPLY code
when user press cancel you got PSN_RESET notification
INT_PTR CALLBACK PPDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
union {
LPARAM lp;
NMHDR* hdr;
PSHNOTIFY* psn;
};
switch (umsg)
{
case WM_NOTIFY:
lp = lParam;
switch (hdr->code)
{
case PSN_APPLY:
DbgPrint("apply");
break;
case PSN_RESET:
DbgPrint("cancel\n");
break;
}
break;
}
return 0;
}

infinite loop inside the getmessage (DispatchMessage(& msg ); is not working)

I am creating a button application using resource editor. After creating button I try to do like this-
m_hwndPreview = CreateDialogParam( g_hInst,MAKEINTRESOURCE(IDD_MAINDIALOG), m_hwndParent,(DLGPROC)DialogProc, (LPARAM)this);
if (m_hwndPreview == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
MSG msg;
BOOL bRet;
while ( (bRet=GetMessage (& msg,0, 0,0)) != 0)
{// this msg always contains the data like -msg = {msg=0x0000c03e wp=0x0000000000000012//always 12 I don't know why ?? lp=0x0000000000000000}
if (bRet == -1)
{
bRet = HRESULT_FROM_WIN32(GetLastError());
MessageBox(NULL, L"Hurr i am the error",L"Error",MB_ICONERROR | MB_OK);
}
else if (!IsDialogMessage (m_hwndPreview, & msg))
{
TranslateMessage (&msg); //on debugging TranslateMessage = 0x000007feee615480 TranslateMessage
DispatchMessage(& msg ); //but show nothing when I put cursor on this method to know the value that means it's not called
MessageBox(NULL, L"there is no error in receiving before dispatch the message",L"Error",
MB_ICONERROR | MB_OK);//this messagebox repeats again and again after the call to DialogProc function and I am not able to come out of the loop and here I need to restart my PC
at some other place I define createdialog function like this-
//this function is called just on createDialogParam() function.after it the controls go to getmessage where everything loops.
BOOL CALLBACK AMEPreviewHandler::DialogProc(HWND m_hwndPreview, UINT Umsg, WPARAM wParam, LPARAM lParam)
{ //this dialogProc function is declares Static some where in the code otherwise the createdialogparam will give error DLGPROC is invalid conversion
//this Umsg alays creates strange value like 48 and 32 etc.. and Wparam contains a very big value like 12335423526 (I mean big and strange) and lparam contains 0.
switch(Umsg)
{
case WM_INITDIALOG:
{
MessageBox(NULL, L"Inside the WM_INITDIALOG function",L"Error",
MB_ICONERROR | MB_OK);
return TRUE;
}
break;
case WM_CREATE:
{
/////////////////
MessageBox(NULL, L"Inside the WM_CREATE",L"Error",
MB_ICONERROR | MB_OK);
/////////////////////////////////
}
break;
case WM_COMMAND:
{ //here are my two buttons created by me which should show messagebox on click
int ctl = LOWORD(wParam);
int event = HIWORD(wParam);//I don't know why this event is in blue colour .. but its not the pqrt of problem right now.
if (ctl == IDC_PREVIOUS && event == BN_CLICKED )
{
MessageBox(m_hwndPreview,L"Button Clicked is next inside WM_COMMAND ",L"BTN WND",MB_ICONINFORMATION);
return 0;
}
if (ctl == IDC_NEXT && event == BN_CLICKED )
{
MessageBox(m_hwndPreview,L"Button Clicked is previous inside WM_COMMAND",L"BTN WND",MB_ICONINFORMATION);
return 0;
}
return FALSE;
}break;
case WM_DESTROY:
{
////////////////::
MessageBox(NULL, L"Inside the WM_DESTROY",L"Error",
MB_ICONERROR | MB_OK);
//////////////////
PostQuitMessage(0);
return 0;
}
break;
case WM_CLOSE:
{
MessageBox(NULL, L"Inside the WM_CLOSE",L"Error",
MB_ICONERROR | MB_OK);
DestroyWindow (m_hwndPreview);
return TRUE;
}
break;
}
MessageBox(NULL, L"outside the DefWindowProc function",L"Error",
MB_ICONERROR | MB_OK);
return 0;
}
The problem occurring is that when I debut it the control first go to CreateDialogParam and then it go to getmessage where the control don't come out of the loop causing restart problem. And I have no display of button and image at preview pane. What I expect if everything go fine is after debugging it should show picture on preview pane and I have 2 buttons "Next" and "Previous" but it show just a blank window (the buttons and photo I have already created using Resource editor... That's correct I am sure about that) .. but I don't know why I am not coming out getmessage function and dispatchmessage is not called (because I saw on debugging).
so now you can try to comment the getmessage part code that will probably out if the problem because you are creating button using IDD_MAINDIALOG and your createdialogparam directly calls your dailogproc function where you receive WM_COMMAND and that you handle by your code behind.
you should write
return true;
just after the DispatchMessage(msg);
and tehn debug it and inform me about t he result on debugging.

Win32 TreeView AfterSelection

quick question...
I am working with treeview in win32 (VC++).
I want to remove selection facility provided for treeview. Can anyone tell what window message is posted onAfterSelect Event of tree view.
TV also has checkboxes. So disabling mouse click isn't an option...
Thanks in advance...
-
Varun
More Info
I am stuck at another point. My win32 application is essentially a modeless dialog - using CreateDialog & ShowWindow. After getting TVN_SELCHANGING, when I am returning 1, it isn't working. I think the default wndproc is getting called before I bypass the windows message. What should I do now?
I had this problem and just reversed the selection once it had already taken place. If you're not responding to it, anyway, then there shouldn't be any side effects.
case WM_NOTIFY:
{
if(wParam == IDC_TREE_MC)
{
LPNMHDR lpnmh = (LPNMHDR) lParam;
TVHITTESTINFO ht = {0};
if ((lpnmh->code == NM_CLICK) && (lpnmh->idFrom == IDC_TREE_MC)) // For Treeview Check Box Check Event
{
DWORD dwpos = GetMessagePos();
ht.pt.x = GET_X_LPARAM(dwpos);
ht.pt.y = GET_Y_LPARAM(dwpos);
MapWindowPoints(HWND_DESKTOP, lpnmh->hwndFrom, &ht.pt, 1);
TreeView_HitTest(lpnmh->hwndFrom, &ht);
if(TVHT_ONITEMSTATEICON & ht.flags)
PostMessage(hDlg, UM_CHECKSTATECHANGE, (WPARAM)lpnmh->hwndFrom, (LPARAM)ht.hItem);
else
TreeView_SelectItem(lpnmh->hwndFrom, NULL);
}
else if ((lpnmh->code == TVN_SELCHANGED ) && (lpnmh->idFrom == IDC_TREE_MC))
TreeView_SelectNode(lpnmh->hwndFrom, NULL);
}
break;
}
to remove selection facility provided for treeview
Could you please clarify this?
Do you want to prevent user from changing selection?
If you really want to do it, insert WM_NOTIFY case handler in the parent window, check for NMTREEVIEW code member (lParam is a pointer to NMTREEVIEW).
If code is TVN_SELCHANGING return 1 if you want to prevent selection change.
Returning 0 will alow selection change.
int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
hWndDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, WndProc);
if (hWndDialog != NULL)
{
ShowWindow(hWndDialog, SW_SHOW);
}
while(GetMessage(&Msg, NULL, 0, 0))
{
if(!IsDialogMessage(hWndDialog, &Msg))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
return 0;
}
INT_PTR CALLBACK WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_BTN_REFRESH:
RefreshButtonHandler();
break;
case IDC_BTN_ADD_INSTALL:
AddInstallBtnHandler();
break;
case IDOK:
case IDCANCEL:
DestroyWindow(hDlg);
PostQuitMessage(0);
return (INT_PTR)TRUE;
break;
}
case WM_NOTIFY:
{
if(wParam == IDC_TREE_MC)
{
LPNMHDR lpnmh = (LPNMHDR) lParam;
TVHITTESTINFO ht = {0};
if ((lpnmh->code == NM_CLICK) && (lpnmh->idFrom == IDC_TREE_MC)) // For Treeview Check Box Check Event
{
DWORD dwpos = GetMessagePos();
ht.pt.x = GET_X_LPARAM(dwpos);
ht.pt.y = GET_Y_LPARAM(dwpos);
MapWindowPoints(HWND_DESKTOP, lpnmh->hwndFrom, &ht.pt, 1);
TreeView_HitTest(lpnmh->hwndFrom, &ht);
if(TVHT_ONITEMSTATEICON & ht.flags)
PostMessage(hDlg, UM_CHECKSTATECHANGE, (WPARAM)lpnmh->hwndFrom, (LPARAM)ht.hItem);
else
TreeView_SelectItem(lpnmh->hwndFrom, NULL);
}
else if ((lpnmh->code == TVN_SELCHANGING ) && (lpnmh->idFrom == IDC_TREE_MC))
return (INT_PTR)TRUE;
}
break;
}
case UM_CHECKSTATECHANGE:
{
//Handle TreeView Check State Event
}
break;
}
return (INT_PTR)FALSE;
}
Sorry for the bad formatting... I am sleep deprived :-)

Select all text in edit contol by clicking Ctrl+A

How to select all text in edit control by pressing Ctrl+A?
I can catch Ctrl+A for parent window in WndProc.
But I don't know how to catch ctrl+a which are applied for edit control.
Also I tried to use accelerators, but again it applies only for parent window.
Thanks.
EDIT: 1-st the simplest method
This method Based on #phord's answers in this question:
win32 select all on edit ctrl (textbox)
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
if (msg.message == WM_KEYDOWN && msg.wParam == 'A' && GetKeyState(VK_CONTROL) < 0)
{
HWND hFocused = GetFocus();
wchar_t className[6];
GetClassName(hFocused, className, 6);
if (hFocused && !wcsicmp(className, L"edit"))
SendMessage(hFocused, EM_SETSEL, 0, -1);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
EDIT: 2-nd method
Need to use CreateAcceleratorTable + TranslateAccelerator functions:
//global variables:
enum {ID_CTRL_A = 1};
HACCEL accel;
//main procedure
ACCEL ctrl_a;
ctrl_a.cmd = ID_CTRL_A; // Hotkey ID
ctrl_a.fVirt = FCONTROL | FVIRTKEY;
ctrl_a.key = 0x41; //'A' key
accel = CreateAcceleratorTable(&ctrl_a, 1); //we have only one hotkey
//How GetMessage loop looks
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
if (!TranslateAccelerator(hWnd, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
//in WndProc we must add next cases
case WM_COMMAND:
{
if (LOWORD(wParam) == ID_CTRL_A && HIWORD(wParam) == 1)
{
//on which control there was pressed Ctrl+A
//there is no way of getting HWND through wParam and lParam
//so we get HWND which currently has focus.
HWND hFocused = GetFocus();
wchar_t className[6];
GetClassName(hFocused, className, 6);
if (hFocudsed && !wcsicmp(className, L"edit"))
SendMessage(hFocused, EM_SETSEL, 0, -1);
}
}
break;
case WM_DESTROY:
{
DestroyAcceleratorTable(accel);
PostQuitMessage(0);
}
break;
As you can see this is pretty simple.
No need to handle WM_KEYDOWN! I know that most examples here (and CodeProject and many other places) all say there is, but it does not cure the beep that results whenever a WM_CHAR arises that is not handled.
Instead, try this:
LRESULT CALLBACK Edit_Prc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
if(msg==WM_CHAR&&wParam==1){SendMessage(hwnd,EM_SETSEL,0,-1); return 1;}
else return CallWindowProc((void*)WPA,hwnd,msg,wParam,lParam);
}
Remember to subclass the EDIT control to this Edit_Prc() using WPA=SetWindowLong(...) where WPA is the window procedure address for CallWindowProc(...)
First change the WindowProc for the edit control:
if (!(pEditProc = (WNDPROC)SetWindowLong(hEdit, GWL_WNDPROC, (LONG)&EditProc)))
{
assert(false);
return false;
}
Then in the new window proc, process the ctrl+a:
LRESULT CALLBACK EditProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
if (msg == WM_KEYDOWN) {
if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'A') {
SendMessage(hwnd, EM_SETSEL, 0, -1);
}
}
return CallWindowProc(pEditProc, hwnd, msg, wParam, lParam);
}
Good News!
It seems Edit Controls (not multiline) now support Ctrl + A natively on Win10.
My current Windows SDK version is 10.0.17763.0.
Only tested on simple GUI APPs created with pure Windows API.
MFC APPs should have the same result.
The test binary platform is x86, and OS is Win10 x64.
Noob proof version?
I have written my own version using an accelerator table aswell.
This cleans out the WinMain a bit, and I tried to make everything as n00b proof as possible (since I am one).
Also the enum is ommited, since it is not needed.
As stated I am only a beginner in using the winapi, so please by all means
correct me if I am wrong.
In "Resource.h" I define two ID's
One for the accelerator table we will be using,
and one for the selectall command we will be using.
Inside Resource.h:
#define IDR_ACCEL1 101
#define ID_SELECT_ALL 9003
Then inside of the resource file (in vs2017 this is PROJECTNAME.rc)
we define the accelerator table.
PROJECTNAME.rc:
IDR_ACCEL1 ACCELERATORS
{
0x41, ID_SELECT_ALL, VIRTKEY, CONTROL // ctrl-A
}
Description
0x41 is virtkey 'a'.
ID_SELECT_ALL (will be the ID of the command, this should be the ID we defined in the Resource.h file.
The VIRTKEY keyword indicated that the 0x41 should be interpreted as a virtual key.
CONTROL is the modifier needed to combine the a with (ctrl+a).
Then inside the WinMain function load the accelerator:
HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCEL1));
if (hAccel == NULL)
{
MessageBox(NULL, _T("Failed to load accelerator table!"),_T("Error"), MB_OK | MB_ICONEXCLAMATION);
return 0;
}
Note: after trying to define hAccel we do a check to see if a valid handle has be assigned.
While this is not needed, I believe it's better convention.
After this we add the TranslateAccelerator function to the message loop, so the command can be processed in the window procedure:
BOOL bRet;
while (bRet = GetMessage(&Msg, NULL, 0, 0) > 0)
{
if (bRet == -1)
{
// Error handling can be done here.
}
else if (!TranslateAccelerator(hwnd, hAccel, &Msg))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
Then finally inside the Window procedure
We add the code as follows:
switch(msg)
{
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case ID_SELECT_ALL:
{
HWND hEdit;
hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT) // Define IDC_MAIN_EDIT with an integer value in "Resource.h".
}
break;
}
break;
}
}
Note: The message passed to WM_COMMAND is the ID we defined for the ctrl+a accel.
I hope this will help fellow n00bies.

How to subclass a win32 control and maintain compatibility with older versions of comctl32.dll?

Version 6.0 of the common controls (comctl32.dll) implements a new approach for subclassing controls that is not available on older versions of Windows. What is the best way to implement subclassing so that it works on systems that support either version of the common controls library?
First, there is an article on MSDN that discusses the changes that occured in subclassing controls between version 6.0 and prior that you should be familiar with.
The best way to maintain backwards compatibility is to create wrapper functions for subclassing controls. This will require you to dynamically load the functions that are required for subclassing controls on version 6 of comctl32.dll. Here is a rough example of how it can be done.
typedef BOOL (WINAPI *LPFN_SETWINDOWSUBCLASS)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
typedef LRESULT (WINAPI *LPFN_DEFSUBCLASSPROC)(HWND, UINT, WPARAM, LPARAM);
typedef BOOL (WINAPI *LPFN_REMOVEWINDOWSUBCLASS)(HWND, SUBCLASSPROC, UINT_PTR);
typedef BOOL (WINAPI *LPFN_INITCOMMONCONTROLSEX)(LPINITCOMMONCONTROLSEX);
typedef struct SubclassStruct {
WNDPROC Proc;
} SubclassStruct;
LPFN_SETWINDOWSUBCLASS SetWindowSubclassPtr = NULL;
LPFN_REMOVEWINDOWSUBCLASS RemoveWindowSubclassPtr = NULL;
LPFN_DEFSUBCLASSPROC DefSubclassProcPtr = NULL;
LPFN_INITCOMMONCONTROLSEX InitCommonControlsExPtr = NULL;
HMODULE ComCtlModule = NULL;
int Subclasser_Init(void)
{
INITCOMMONCONTROLSEX CommonCtrlEx = {0};
ComCtlModule = LoadLibrary("comctl32.dll");
if (ComCtlModule == NULL)
return FALSE;
SetWindowSubclassPtr = (LPFN_SETWINDOWSUBCLASS)GetProcAddress(ComCtlModule, "SetWindowSubclass");
RemoveWindowSubclassPtr = (LPFN_REMOVEWINDOWSUBCLASS)GetProcAddress(ComCtlModule, "RemoveWindowSubclass");
DefSubclassProcPtr = (LPFN_DEFSUBCLASSPROC)GetProcAddress(ComCtlModule, "DefSubclassProc");
InitCommonControlsExPtr = (LPFN_INITCOMMONCONTROLSEX)GetProcAddress(ComCtlModule, "InitCommonControlsEx");
if (InitCommonControlsExPtr != NULL)
{
CommonCtrlEx.dwSize = sizeof(CommonCtrlEx);
InitCommonControlsExPtr(&CommonCtrlEx);
}
return TRUE;
}
int Subclasser_Uninit(void)
{
if (ComCtlModule != NULL)
FreeLibrary(ComCtlModule);
return TRUE;
}
LRESULT CALLBACK Subclasser_SharedSubclassProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam, UINT_PTR SubclassId, DWORD_PTR RefData)
{
SubclassStruct *Subclass = (SubclassStruct *)SubclassId;
return CallWindowProc(Subclass->Proc, hWnd, Message, wParam, lParam);
}
int Subclasser_SetProc(HWND hWnd, WNDPROC Proc, WNDPROC *OriginalProc, void *Param)
{
SubclassStruct *Subclass = NULL;
int Result = TRUE;
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(UINT_PTR)Param) == 0)
{
if (GetLastError() > 0)
return FALSE;
}
if (SetWindowSubclassPtr!= NULL)
{
Subclass = (SubclassStruct*)malloc(sizeof(SubclassStruct));
Subclass->Proc = Proc;
*OriginalProc = (WNDPROC)Subclass;
Result = SetWindowSubclassPtr(hWnd, Subclasser_SharedSubclassProc, (UINT_PTR)Subclass, NULL);
}
else
{
*OriginalProc = (WNDPROC)(void *)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_WNDPROC, (__int3264)(intptr)Proc) == 0)
{
if (GetLastError() > 0)
Result = FALSE;
}
}
if (Result == FALSE)
return FALSE;
return TRUE;
}
int Subclasser_UnsetProc(HWND hWnd, WNDPROC Proc, WNDPROC *OriginalProc)
{
SubclassStruct *Subclass = NULL;
int Result = TRUE;
if (RemoveWindowSubclassPtr != NULL)
{
if (*OriginalProc != NULL)
{
Subclass = (SubclassStruct *)*OriginalProc;
Proc = Subclass->Proc;
}
Result = RemoveWindowSubclassPtr(hWnd, Subclasser_SharedSubclassProc, (UINT_PTR)Subclass);
free(Subclass);
}
else
{
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_WNDPROC, (__int3264)(UINT_PTR)*OriginalProc) == 0)
{
if (GetLastError() > 0)
Result = FALSE;
}
}
SetLastError(0);
if (SetWindowLongPtr(hWnd, GWLP_USERDATA, 0) == 0)
{
if (GetLastError() > 0)
Result = FALSE;
}
*OriginalProc = NULL;
if (Result == FALSE)
return FALSE;
return TRUE;
}
LRESULT Subclasser_DefProc(WNDPROC OriginalProc, HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
if (OriginalProc == NULL)
return DefWindowProc(hWnd, Message, wParam, lParam);
if (DefSubclassProcPtr != NULL)
return DefSubclassProcPtr(hWnd, Message, wParam, lParam);
return CallWindowProc(OriginalProc, hWnd, Message, wParam, lParam);
}
The only other example can be found in OpenJDK. The one disadvantage to it is that it uses the the WindowProc as the subclass ID which crashes if you are subclassing more than one control on a dialog with the same WindowProc function. In the example above, we allocate a new memory structure called SubclassStruct and pass it's address as the subclass ID which guarantees that each instance of the control you subclass will have a unique subclass ID.
If you are using the subclassing functions in multiple applications, some that use comctl32.dll < 6 and some that use comctl32.dll >= 6, you could detect which version of the common control library was loaded by getting comctl32.dll's file version information. This can be done through the use of GetModuleFileName and GetFileVersionInfo.
In addition, if you use SetWindowWord/GetWindowWord in the subclass callbacks with comctl32.dll 6.0, such as in the following Dr. Dobbs article on
Writing Windows Custom Controls, then you will need to use those code blocks conditionally when comctl32.dll < 6, because they will not work on version 6 or greater and will cause your application to crash.

Resources