/*++ Copyright (c) 2000-2001, Microsoft Corporation All rights reserved. Module Name: hook.c Abstract: This file contains functions that wrap various hook procedures, and the helper functions that support their efforts. Hooks found in this file: WindowProc DialogProc CBTProc EnumChildToTagProc comdlg32.dll hooks: FRHookProcFind FRHookProcReplace OFNHookProc OFNHookProcSave PagePaintHook CCHookProc CFHookProc PageSetupHook PrintHookProc SetupHookProc OFNHookProcOldStyle OFNHookProcOldStyleSave Helper functions: FRHelper UpdateTextAndFlags NotifyFindReplaceParent OFNHookHelper OFNotifyHelper GenericHookHelper RemoveFontPropIfPresent SetNewFileOpenProp SetFontProp Externally available helper functions: IsFontDialog IsNewFileOpenDialog IsCaptureWindow SetCaptureWindowProp Revision History: 27 Jan 2001 v-michka Created. --*/ #include "precomp.h" // Stolen from commdlg.h #define CCHCLOSE 9 #define iszClose 0x040d // "Close" text for find/replace static WCHAR m_szClose [CCHCLOSE]; // So we can keep track of font dialogs const char m_szComdlgClass[] = "GodotComdlgClass"; ATOM m_aComdlgClass; #define CHOOSEFONT_DIALOG (HANDLE)1 #define NEWFILEOPEN_DIALOG (HANDLE)2 #define CAPTURE_WINDOW (HANDLE)3 // Not really a common dialog, but close enough // forward declares we will need -- we must have Unicode text, so // why write new wrappers when we have some lying around already? UINT __stdcall GodotGetDlgItemTextW(HWND hDlg, int nIDDlgItem, LPWSTR lpString, int nMaxCount); int __stdcall GodotLoadStringW(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBufferMax); BOOL __stdcall GodotSetWindowTextW(HWND hWnd, LPCWSTR lpString); LRESULT __stdcall GodotSendMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); // our own forward declares BOOL CALLBACK EnumChildToTagProc(HWND hwnd, LPARAM lParam); UINT FRHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fFindText); VOID UpdateTextAndFlags(HWND hDlg, LPFINDREPLACEW lpfrw, DWORD dwActionFlag, BOOL fFindText); LRESULT NotifyFindReplaceParent(LPFINDREPLACEW lpfr, LPFRHOOKPROC lpfrhp, UINT uMsg, BOOL fFindText); UINT_PTR OFNHookHelperOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn); UINT_PTR OFNHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fOpenFile); UINT_PTR OFNotifyHelper(HWND hdlg, WNDPROC lpfn, WPARAM wParam, LPARAM lParam); UINT_PTR GenericHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn); void SetFontProp(HWND hdlg); void SetNewFileOpenProp(HWND hdlg); /*------------------------------- WindowProc This is our global wrapper around *all* window procedrures for windows that we create or subclass. -------------------------------*/ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT RetVal; WNDPROC lpfn = WndprocFromFauxWndproc(hwnd, 0, fptWndproc); // Chain to the user's wndproc RetVal = GodotDoCallback(hwnd, uMsg, wParam, lParam, lpfn, FALSE, fptWndproc); // If we get a final destroy message, lets unhook ourselves if(uMsg==WM_NCDESTROY) CleanupWindow(hwnd); return(RetVal); } /*------------------------------- DialogProc This is our global wrapper around *all* dialog procedrures for windows that we create or subclass. -------------------------------*/ INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { DLGPROC lpfn; INT_PTR RetVal; LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); // See if we have a DLGPROC waiting to be assigned somewhere. if(lpgti && lpgti->pfnDlgProc) { // Perhaps it is for *this* dialog. We must make sure it is, or else we will be // assigning the proc to the wrong window! See Windows Bugs # 350862 for details. if(OUTSIDE_GODOT_RANGE(GetWindowLongInternal(hwndDlg, DWL_DLGPROC, TRUE))) { // Set the dlg proc and clear out the dlg proc pointer lpfn = (DLGPROC)SetWindowLongInternal(hwndDlg, DWL_DLGPROC, (LONG)lpgti->pfnDlgProc, TRUE); lpgti->pfnDlgProc = NULL; } } // Preprocess: also set some RetVal values, which may // or may not be overridden by the user's dlgproc. switch(uMsg) { case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: case WM_VKEYTOITEM: RetVal = (INT_PTR)(BOOL)FALSE; break; case WM_INITDIALOG: // Mark all the children now: before the client gets a // chance to init controls, but afte we are sure the // controls exist. EnumChildWindows(hwndDlg, &EnumChildToTagProc, 0); RetVal = (INT_PTR)(BOOL)FALSE; break; case WM_CHARTOITEM: case WM_COMPAREITEM: // The user's proc MUST do something for these messages, // we have no idea what to return here! RetVal = 0; break; case WM_QUERYDRAGICON: RetVal = (INT_PTR)NULL; break; default: RetVal = (INT_PTR)(BOOL)FALSE; break; } // Chain to the user's dialog procedure. They ought to have one, right? if(lpfn = WndprocFromFauxWndproc(hwndDlg, 0, fptDlgproc)) RetVal = GodotDoCallback(hwndDlg, uMsg, wParam, lParam, lpfn, FALSE, fptDlgproc); return(RetVal); } /*------------------------------- CBTProc This is our CBT hook that we use to get information about a window about to be created. We also do our window subclassing here so we can be assured that we are handling the messages properly. -------------------------------*/ LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); HHOOK hh = (lpgti ? lpgti->hHook : NULL); switch(nCode) { // We only care about the window creation notification case HCBT_CREATEWND: { LRESULT RetVal; InitWindow((HWND)wParam, NULL); RetVal = CallNextHookEx(hh, nCode, wParam, lParam); // Make sure no one cancelled window creation; if they did, the // window will be destroyed without a WM_DESTROY call. Therefore // we must be sure to clean ourselves up or we will watch Windows // lose our global atom! if(RetVal != 0) CleanupWindow((HWND)wParam); // Lets unhook here, we have done our duty TERM_WINDOW_SNIFF(hh); return(RetVal); break; } default: // Should be impossible, but just in case, // we do a nice default sorta thing here. if(hh) return(CallNextHookEx(hh, nCode, wParam, lParam)); else return(0); break; } } /*------------------------------- EnumChildToTagProc Tag some child windows -------------------------------*/ BOOL CALLBACK EnumChildToTagProc(HWND hwnd, LPARAM lParam) { InitWindow(hwnd, NULL); // Keep on enumerating return(TRUE); } /*------------------------------- FRHookProcFind/FRHookProcReplace -------------------------------*/ UINT_PTR CALLBACK FRHookProcFind(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { return(FRHelper(hdlg, uiMsg, wParam, lParam, TRUE)); } UINT_PTR CALLBACK FRHookProcReplace(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { return(FRHelper(hdlg, uiMsg, wParam, lParam, FALSE)); } /*------------------------------- // OFNHookProc/OFNHookProcSave -------------------------------*/ UINT_PTR CALLBACK OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { return(OFNHookHelper(hdlg, uiMsg, wParam, lParam, TRUE)); } UINT_PTR CALLBACK OFNHookProcSave(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { return(OFNHookHelper(hdlg, uiMsg, wParam, lParam, FALSE)); } /*------------------------------- // PagePaintHook // // // We use this hook to handle the WM_PSD_PAGESETUPDLG when it comes through -------------------------------*/ UINT_PTR CALLBACK PagePaintHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); WNDPROC lpfnP = (lpgti ? lpgti->pfnPagePaint : NULL); if(uiMsg != WM_PSD_PAGESETUPDLG) return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, lpfnP)); else { // Ok, this is the WM_PSD_PAGESETUPDLG event. wParam is some random stuff // to pass on and lParam is a PAGESETUPDLGA structure that we need to turn // into a PAGESETUPDLGW. if(lpfnP) { LPPAGESETUPDLGA lppsda; PAGESETUPDLGW psdw; UINT_PTR RetVal; ALLOCRETURN ar = arNoAlloc; WNDPROC lpfnS = (lpgti ? lpgti->pfnPageSetup : NULL); lppsda = (LPPAGESETUPDLGA)lParam; psdw.lStructSize = sizeof(PAGESETUPDLGW); // Copy some stuff over now psdw.hwndOwner = lppsda->hwndOwner; psdw.ptPaperSize = lppsda->ptPaperSize; psdw.rtMinMargin = lppsda->rtMinMargin; psdw.rtMargin = lppsda->rtMargin; psdw.hInstance = lppsda->hInstance; psdw.lCustData = lppsda->lCustData; // Do NOT deallocate the HGLOBALS here, someone else might // need them! Passing FALSE for the fFree param accomplishes // this feat. psdw.hDevMode = HDevModeWfromA(&(lppsda->hDevMode), FALSE); psdw.hDevNames = HDevNamesWfromA(&(lppsda->hDevNames), FALSE); // Hide the details of our hook from them (by munging flags as // necessary) psdw.Flags = lppsda->Flags; if(lpfnP) psdw.Flags &= ~PSD_ENABLEPAGEPAINTHOOK; psdw.lpfnPagePaintHook = lpfnP; if(lpfnS) psdw.Flags &= ~PSD_ENABLEPAGESETUPHOOK; psdw.lpfnPageSetupHook = lpfnS; if(lppsda->Flags & PSD_ENABLEPAGESETUPTEMPLATE) { psdw.hPageSetupTemplate = lppsda->hPageSetupTemplate; ar = GodotToUnicodeOnHeap(lppsda->lpPageSetupTemplateName, &(LPWSTR)psdw.lpPageSetupTemplateName); } RetVal = ((* lpfnP)(hdlg, WM_PSD_PAGESETUPDLG, wParam, (LPARAM)&psdw)); if(ar==arAlloc) GodotHeapFree((LPWSTR)psdw.lpPageSetupTemplateName); lppsda->hDevMode = HDevModeAfromW(&(psdw.hDevMode), TRUE); lppsda->hDevNames = HDevNamesAfromW(&(psdw.hDevNames), TRUE); return(RetVal); } else return(FALSE); } } // // Ok, here are all our almost but not quite pointless hooks that we have. // These hooks allow us to make sure that the dialog and all the controls in // it are tagged appropriately. See GenericHookHelper for details. // UINT_PTR CALLBACK CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnChooseColor : NULL))); } UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); if(uiMsg==WM_INITDIALOG) SetFontProp(hdlg); return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnChooseFont : NULL))); } UINT_PTR CALLBACK PageSetupHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPageSetup : NULL))); } UINT_PTR CALLBACK PrintHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPrintDlg : NULL))); } UINT_PTR CALLBACK SetupHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); return(GenericHookHelper(hdlg, uiMsg, wParam, lParam, (lpgti ? lpgti->pfnPrintDlgSetup : NULL))); } UINT_PTR CALLBACK OFNHookProcOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); WNDPROC lpfn = (WNDPROC)(lpgti ? lpgti->pfnGetOpenFileNameOldStyle : NULL); return(OFNHookHelperOldStyle(hdlg, uiMsg, wParam, lParam, lpfn)); } UINT_PTR CALLBACK OFNHookProcOldStyleSave(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); WNDPROC lpfn = (WNDPROC)(lpgti ? lpgti->pfnGetSaveFileNameOldStyle : NULL); return(OFNHookHelperOldStyle(hdlg, uiMsg, wParam, lParam, lpfn)); } /*------------------------------- // FRHelper // // Used to handle lots of the shared code between the find and replace hook functions. Note // that some of it does not always apply: certain controls are hidden from the FIND dialog, etc. // But since all of the code other than the actual callbacks themselves is shared, this keeps // us from a lot of duplication. -------------------------------*/ UINT FRHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fFindText) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); LPFRHOOKPROC lpfn = 0; UINT RetVal; if(!lpgti) { if(lpfn) return((* lpfn)(hdlg, uiMsg, wParam, lParam)); else return(FALSE); } if(fFindText) lpfn = lpgti->pfnFindText; else lpfn = lpgti->pfnReplaceText; switch(uiMsg) { case WM_COMMAND: { LPFINDREPLACEW lpfr; if(fFindText) lpfr = lpgti->lpfrwFind; else lpfr = lpgti->lpfrwReplace; switch(GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: // FIND UpdateTextAndFlags(hdlg, lpfr, FR_FINDNEXT, fFindText); NotifyFindReplaceParent(lpfr, lpfn, msgFINDMSGSTRING, fFindText); RetVal = TRUE; break; case psh1: // REPLACE case psh2: // REPLACE ALL UpdateTextAndFlags(hdlg, lpfr, (psh1 ? FR_REPLACE : FR_REPLACEALL), fFindText); if (NotifyFindReplaceParent(lpfr, lpfn, msgFINDMSGSTRING, fFindText) == TRUE) { // Change button to if function // returns TRUE (IDCANCEL instead of psh1). // First load the string if we never have before if(m_szClose[0] == 0) GodotLoadStringW(GetComDlgHandle(), iszClose, m_szClose, CCHCLOSE); GodotSetWindowTextW(GetDlgItem(hdlg, IDCANCEL), (LPWSTR)m_szClose); } RetVal = TRUE; break; case pshHelp: // HELP if (msgHELPMSGSTRING && lpfr->hwndOwner) { UpdateTextAndFlags(hdlg, lpfr, 0, fFindText); NotifyFindReplaceParent(lpfr, lpfn, msgHELPMSGSTRING, fFindText); } RetVal = TRUE; break; case IDCANCEL: // CANCEL, both types case IDABORT: // They are destroying the dialog, so unhook ourselves // and clean up the dialog. Usually the caller will keep // a FINDREPLACEW lying around, so if we do not clean up // we might have some re-entrancy issues here. if(lpfn) lpfr->lpfnHook = lpfn; else { lpfr->lpfnHook = NULL; lpfr->Flags &= ~FR_ENABLEHOOK; } if(fFindText) { lpgti->lpfrwFind = NULL; lpgti->pfnFindText = NULL; } else { lpgti->lpfrwReplace = NULL; lpgti->pfnReplaceText = NULL; } // Fall through. We do not set the RetVal to TRUE, since // we need to make sure that comdlg32.dll cleans up default: // Everything else RetVal = FALSE; break; } break; } case WM_INITDIALOG: // Mark all the children now, before the user's proc gets to them EnumChildWindows(hdlg, &EnumChildToTagProc, 0); // Perhaps the caller's hook will override this, but we need to cover // our bases in case there the caller has no hook. The caller expects // us to return TRUE if they are drawing the dlg, FALSE if we are. RetVal = TRUE; break; default: RetVal = FALSE; break; } if(lpfn) RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam); return(RetVal); } /*------------------------------- // OFNHookHelperOldStyle // // Almost all of the old style fileopen/save code is shared, and this function shares it! -------------------------------*/ UINT_PTR OFNHookHelperOldStyle(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn) { UINT_PTR RetVal = FALSE; if(!msgSHAREVISTRING) msgSHAREVISTRING = RegisterWindowMessage(SHAREVISTRINGA); if(!msgFILEOKSTRING) msgFILEOKSTRING = RegisterWindowMessage(FILEOKSTRINGA); if(uiMsg==WM_INITDIALOG) { // Mark all the children now, before the user's proc gets to them EnumChildWindows(hdlg, &EnumChildToTagProc, 0); if(lpfn) RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam); else RetVal = TRUE; } else if(((uiMsg == msgFILEOKSTRING) || (uiMsg == msgSHAREVISTRING))) { WCHAR drive[_MAX_DRIVE + 1]; WCHAR dir[_MAX_DIR +1]; WCHAR file[_MAX_FNAME +1]; LPOPENFILENAMEA lpofnA = (LPOPENFILENAMEA)lParam; OPENFILENAMEW ofn; LPARAM lParamW; ALLOCRETURN arCustomFilter = arNoAlloc; ALLOCRETURN arFile = arNoAlloc; ALLOCRETURN arFileTitle = arNoAlloc; // lParam is an LPOPENFILENAMEA to be converted. Copy all the // members that the user might expect. ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W; arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter, lpofnA->nMaxCustFilter, &ofn.lpstrCustomFilter, g_acp); ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter); ofn.nFilterIndex = lpofnA->nFilterIndex; ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR); arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile, lpofnA->nMaxFile, &ofn.lpstrFile, g_acp); ofn.nMaxFile = gwcslen(ofn.lpstrFile); arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle, lpofnA->nMaxFileTitle, &ofn.lpstrFileTitle, g_acp); ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle); ofn.Flags = lpofnA->Flags; // nFileOffset and nFileExtension are to provide info about the // file name and extension location in lpstrFile, but there is // no reasonable way to get it from the return so we just recalc gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL); ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir)); ofn.nFileExtension = ofn.nFileOffset + gwcslen(file); lParamW = (LPARAM)&ofn; if(lpfn) RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParamW); // Free up some memory if we allocated any if(arCustomFilter==arAlloc) GodotHeapFree(ofn.lpstrCustomFilter); if(arFile==arAlloc) GodotHeapFree(ofn.lpstrFile); if(arFileTitle==arAlloc) GodotHeapFree(ofn.lpstrFileTitle); } else { if(lpfn) RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam); } return(RetVal); } /*------------------------------- // OFNHookHelper // // Almost all of the fileopen/save code is shared, and this function shares it! -------------------------------*/ UINT_PTR OFNHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, BOOL fOpenFile) { UINT_PTR RetVal = 0; LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); LPOFNHOOKPROC lpfn; if(fOpenFile) lpfn = (lpgti ? lpgti->pfnGetOpenFileName : NULL); else lpfn = (lpgti ? lpgti->pfnGetSaveFileName : NULL); if(uiMsg==WM_INITDIALOG) { HWND hwndParent; // Tag the window as a new style file open dlg SetNewFileOpenProp(hdlg); if(hwndParent = GetParent(hdlg)) SetNewFileOpenProp(hwndParent); // Mark all the children now, before the user's proc gets to them EnumChildWindows(hdlg, &EnumChildToTagProc, 0); RetVal = TRUE; } if(lpfn) { switch(uiMsg) { case WM_NOTIFY: RetVal = OFNotifyHelper(hdlg, lpfn, wParam, lParam); break; default: RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam); break; } } return(RetVal); } /*------------------------------- // OSNotifyHelper -------------------------------*/ UINT_PTR OFNotifyHelper(HWND hdlg, WNDPROC lpfn, WPARAM wParam, LPARAM lParam) { UINT_PTR RetVal; LPOFNOTIFYA lpofnfyA = (LPOFNOTIFYA)lParam; switch(lpofnfyA->hdr.code) { case CDN_FILEOK: case CDN_FOLDERCHANGE: case CDN_HELP: case CDN_INCLUDEITEM: case CDN_INITDONE: case CDN_SELCHANGE: case CDN_SHAREVIOLATION: case CDN_TYPECHANGE: { LPOPENFILENAMEA lpofnA = lpofnfyA->lpOFN; OFNOTIFYW ofnfy; OPENFILENAMEW ofn; WCHAR drive[_MAX_DRIVE]; WCHAR dir[_MAX_DIR]; WCHAR file[_MAX_FNAME]; ALLOCRETURN arCustomFilter = arNoAlloc; ALLOCRETURN arFile = arNoAlloc; ALLOCRETURN arFileTitle = arNoAlloc; ZeroMemory(&ofnfy, sizeof(OFNOTIFYW)); ofnfy.hdr = lpofnfyA->hdr; ofnfy.lpOFN = &ofn; ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W; ofn.Flags = lpofnA->Flags; ofn.hwndOwner = lpofnA->hwndOwner; ofn.hInstance = lpofnA->hInstance; ofn.lpfnHook = lpfn; arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter, lpofnA->nMaxCustFilter, &ofn.lpstrCustomFilter, g_acp); ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter); ofn.nFilterIndex = lpofnA->nFilterIndex; ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR); arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile, lpofnA->nMaxFile, &ofn.lpstrFile, g_acp); ofn.nMaxFile = gwcslen(ofn.lpstrFile); arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle, lpofnA->nMaxFileTitle, &ofn.lpstrFileTitle, g_acp); ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle); ofn.Flags = lpofnA->Flags; // nFileOffset and nFileExtension are to provide info about the // file name and extension location in lpstrFile, but there is // no reasonable way to get it from the return so we just recalc gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL); ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir)); ofn.nFileExtension = ofn.nFileOffset + gwcslen(file); if(ofnfy.hdr.code == CDN_SHAREVIOLATION) { WCHAR lpwzFile[MAX_PATH]; if(FSTRING_VALID(lpofnfyA->pszFile)) { MultiByteToWideChar(g_acp, 0, lpofnfyA->pszFile, -1, lpwzFile, MAX_PATH); ofnfy.pszFile = lpwzFile; } RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, (LPARAM)&ofnfy); } else { RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, (LPARAM)&ofnfy); } // Free up some memory if we allocated any if(arCustomFilter==arAlloc) GodotHeapFree(ofn.lpstrCustomFilter); if(arFile==arAlloc) GodotHeapFree(ofn.lpstrFile); if(arFileTitle==arAlloc) GodotHeapFree(ofn.lpstrFileTitle); break; } default: RetVal = (* lpfn)(hdlg, WM_NOTIFY, wParam, lParam); break; } return(RetVal); } /*------------------------------- // GenericHookHelper // // This function is to assist all the comdlg32 hook functions that serve // no real purpose that we know of, other than to make sure all the // child controls get marked as "Unicode" controls, etc. -------------------------------*/ UINT_PTR GenericHookHelper(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn) { UINT_PTR RetVal = FALSE; switch(uiMsg) { case WM_INITDIALOG: // Mark all the children now, before the user's proc gets to them EnumChildWindows(hdlg, &EnumChildToTagProc, 0); if(lpfn) RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam); else RetVal = TRUE; break; case WM_DESTROY: case WM_NCDESTROY: default: if(lpfn) RetVal = (* lpfn)(hdlg, uiMsg, wParam, lParam); break; } return(RetVal); } /*------------------------------- // UpdateTextAndFlags // // Get the text from the control, and update the flags. Use in // preparation for notifying the owner window that the user has // done something interesting. // // Modified from find.c from the comdlg32 project in the Shell // depot. It definitely needed its own special spin, though! // // chx1 is whether or not to match entire words // chx2 is whether or not case is relevant // chx3 is whether or not to wrap scans -------------------------------*/ VOID UpdateTextAndFlags(HWND hDlg, LPFINDREPLACEW lpfrw, DWORD dwActionFlag, BOOL fFindText) { // Only clear flags that this routine sets. The hook and template // flags should not be anded off here. lpfrw->Flags &= ~((DWORD)(FR_WHOLEWORD | FR_MATCHCASE | FR_REPLACE | FR_FINDNEXT | FR_REPLACEALL | FR_DOWN)); if (IsDlgButtonChecked(hDlg, chx1)) lpfrw->Flags |= FR_WHOLEWORD; if (IsDlgButtonChecked(hDlg, chx2)) lpfrw->Flags |= FR_MATCHCASE; // Set ACTION flag FR_{REPLACE,FINDNEXT,REPLACEALL}. lpfrw->Flags |= dwActionFlag; GodotGetDlgItemTextW(hDlg, edt1, lpfrw->lpstrFindWhat, lpfrw->wFindWhatLen); if (fFindText) { // Assume searching down. Check if UP button is NOT pressed, rather // than if DOWN button IS. So, if buttons have been hidden or // disabled, FR_DOWN flag will be set correctly. if (!IsDlgButtonChecked(hDlg, rad1)) lpfrw->Flags |= FR_DOWN; } else { GodotGetDlgItemTextW(hDlg, edt2, lpfrw->lpstrReplaceWith, lpfrw->wReplaceWithLen); lpfrw->Flags |= FR_DOWN; } } /*------------------------------- // NotifyFindReplaceParent // // Let hwndOwner know what is happening, since the user has // performed some kind of action that they might want to // know about. // // Modified from find.c from the comdlg32 project in the Shell // depot. It definitely needed its own special spin, though! -------------------------------*/ LRESULT NotifyFindReplaceParent(LPFINDREPLACEW lpfr, LPFRHOOKPROC lpfrhp, UINT uMsg, BOOL fFindText) { LRESULT RetVal; DWORD Flags; // Cache the flags setting since we may be mucking with it Flags = lpfr->Flags; // Now, muck with the structure a bit // to hide our hook from the user if(lpfrhp) lpfr->lpfnHook = lpfrhp; else { lpfr->lpfnHook = NULL; Flags &= ~FR_ENABLEHOOK; } RetVal = GodotSendMessageW(lpfr->hwndOwner, uMsg, 0, (DWORD_PTR)lpfr); // Restore the structure to what it was lpfr->Flags = Flags; if(fFindText) lpfr->lpfnHook = &FRHookProcFind; else lpfr->lpfnHook = &FRHookProcReplace; return(RetVal); } /*------------------------------- // IsFontDialog -------------------------------*/ BOOL IsFontDialog(HWND hdlg) { return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == CHOOSEFONT_DIALOG)); } /*------------------------------- // IsNewFileOpenDialog -------------------------------*/ BOOL IsNewFileOpenDialog(HWND hdlg) { return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == NEWFILEOPEN_DIALOG)); } /*------------------------------- // IsCaptureWindow -------------------------------*/ BOOL IsCaptureWindow(HWND hdlg) { return((BOOL)(GetPropA(hdlg, m_szComdlgClass) == CAPTURE_WINDOW)); } /*------------------------------- // RemoveComdlgPropIfPresent -------------------------------*/ void RemoveComdlgPropIfPresent(HWND hdlg) { if(GetPropA(hdlg, m_szComdlgClass) != 0) RemovePropA(hdlg, m_szComdlgClass); return; } /*------------------------------- // SetNewFileOpenProp -------------------------------*/ void SetNewFileOpenProp(HWND hdlg) { // We have to aggressively refcount our prop to keep anyone from // losing it. Thus we do an initial GlobalAddAtom on it and then // subsequently call SetPropA on it with the same string. if(!m_aComdlgClass) m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass); SetPropA(hdlg, m_szComdlgClass, (HANDLE)NEWFILEOPEN_DIALOG); return; } /*------------------------------- // SetFontProp -------------------------------*/ void SetFontProp(HWND hdlg) { // We have to aggressively refcount our prop to keep anyone from // losing it. Thus we do an initial GlobalAddAtom on it and then // subsequently call SetPropA on it with the same string. if(!m_aComdlgClass) m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass); SetPropA(hdlg, m_szComdlgClass, (HANDLE)CHOOSEFONT_DIALOG); return; } /*------------------------------- // SetCaptureWindowProp -------------------------------*/ void SetCaptureWindowProp(HWND hdlg) { // We have to aggressively refcount our prop to keep anyone from // losing it. Thus we do an initial GlobalAddAtom on it and then // subsequently call SetPropA on it with the same string. if(!m_aComdlgClass) m_aComdlgClass = GlobalAddAtomA(m_szComdlgClass); SetPropA(hdlg, m_szComdlgClass, (HANDLE)CAPTURE_WINDOW); return; }