From dce1c97c7d3e76c18e4f80d4ffe288d2933a74bc Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 27 Oct 2021 15:29:19 -0500 Subject: Make thmutil automatically load controls during window creation. Add control loaded event. --- src/api/burn/balutil/inc/BAFunctions.h | 15 ++ src/api/burn/balutil/inc/BalBaseBAFunctions.h | 10 ++ src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h | 12 ++ src/api/burn/balutil/inc/IBAFunctions.h | 9 + src/api/burn/balutil/inc/balutil.h | 2 + src/api/burn/bextutil/inc/bextutil.h | 2 + .../WixStandardBootstrapperApplication.cpp | 188 +++++++++++++-------- src/libs/dutil/WixToolset.DUtil/inc/thmutil.h | 43 ++--- src/libs/dutil/WixToolset.DUtil/thmutil.cpp | 137 ++++++++++----- src/samples/thmviewer/display.cpp | 58 +++---- src/samples/thmviewer/thmviewer.cpp | 15 +- .../Manual/BafThmutilTesting/BafThmUtilTesting.cpp | 103 +++++------ .../theme/BafThmUtilTestingTheme.xml | 21 +++ .../theme/BafThmUtilTestingThemeLoose.xml | 158 +++++++++++++++++ 14 files changed, 542 insertions(+), 231 deletions(-) create mode 100644 src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingThemeLoose.xml (limited to 'src') diff --git a/src/api/burn/balutil/inc/BAFunctions.h b/src/api/burn/balutil/inc/BAFunctions.h index ab6ea4d7..0eda95a1 100644 --- a/src/api/burn/balutil/inc/BAFunctions.h +++ b/src/api/burn/balutil/inc/BAFunctions.h @@ -88,6 +88,7 @@ enum BA_FUNCTIONS_MESSAGE BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADING, BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMCOMMAND, BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMNOTIFY, + BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADED, }; typedef HRESULT(WINAPI *PFN_BA_FUNCTIONS_PROC)( @@ -115,6 +116,20 @@ struct BA_FUNCTIONS_CREATE_RESULTS LPVOID pvBAFunctionsProcContext; }; +struct BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS +{ + DWORD cbSize; + LPCWSTR wzName; + WORD wId; + HWND hWnd; +}; + +struct BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS +{ + DWORD cbSize; + BOOL fProcessed; +}; + struct BA_FUNCTIONS_ONTHEMECONTROLLOADING_ARGS { DWORD cbSize; diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h index f6c33f58..d97df350 100644 --- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h +++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h @@ -874,6 +874,16 @@ public: // IBAFunctions return S_OK; } + virtual STDMETHODIMP OnThemeControlLoaded( + __in LPCWSTR /*wzName*/, + __in WORD /*wId*/, + __in HWND /*hWnd*/, + __inout BOOL* /*pfProcessed*/ + ) + { + return S_OK; + } + protected: CBalBaseBAFunctions( __in HMODULE hModule, diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h index 1d51c5b6..2af231e8 100644 --- a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h +++ b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h @@ -51,6 +51,15 @@ static HRESULT BalBaseBAFunctionsProcOnThemeControlWmNotify( return pBAFunctions->OnThemeControlWmNotify(pArgs->lParam, pArgs->wzName, pArgs->wId, pArgs->hWnd, &pResults->fProcessed, &pResults->lResult); } +static HRESULT BalBaseBAFunctionsProcOnThemeControlLoaded( + __in IBAFunctions* pBAFunctions, + __in BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS* pArgs, + __inout BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS* pResults + ) +{ + return pBAFunctions->OnThemeControlLoaded(pArgs->wzName, pArgs->wId, pArgs->hWnd, &pResults->fProcessed); +} + /******************************************************************* BalBaseBAFunctionsProc - requires pvContext to be of type IBAFunctions. Provides a default mapping between the message based BAFunctions interface and @@ -161,6 +170,9 @@ static HRESULT WINAPI BalBaseBAFunctionsProc( case BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMNOTIFY: hr = BalBaseBAFunctionsProcOnThemeControlWmNotify(pBAFunctions, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; + case BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADED: + hr = BalBaseBAFunctionsProcOnThemeControlLoaded(pBAFunctions, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; } } diff --git a/src/api/burn/balutil/inc/IBAFunctions.h b/src/api/burn/balutil/inc/IBAFunctions.h index 63395e1e..2e71608d 100644 --- a/src/api/burn/balutil/inc/IBAFunctions.h +++ b/src/api/burn/balutil/inc/IBAFunctions.h @@ -61,4 +61,13 @@ DECLARE_INTERFACE_IID_(IBAFunctions, IBootstrapperApplication, "0FB445ED-17BD-49 __inout BOOL* pfProcessed, __inout LRESULT* plResult ) = 0; + + // OnThemeControlLoaded - Called after a control was created for the theme. + // + STDMETHOD(OnThemeControlLoaded)( + __in LPCWSTR wzName, + __in WORD wId, + __in HWND hWnd, + __inout BOOL* pfProcessed + ) = 0; }; diff --git a/src/api/burn/balutil/inc/balutil.h b/src/api/burn/balutil/inc/balutil.h index 82fd1fe5..0c47301a 100644 --- a/src/api/burn/balutil/inc/balutil.h +++ b/src/api/burn/balutil/inc/balutil.h @@ -11,6 +11,7 @@ extern "C" { #define BalExitOnFailureSource(d, x, f, ...) if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } #define BalExitOnRootFailureSource(d, x, f, ...) if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#define BalExitWithRootFailureSource(d, x, e, f, ...) { x = FAILED(e) ? e : E_FAIL; BalLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } #define BalExitOnLastErrorSource(d, x, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } } #define BalExitOnNullSource(d, p, x, e, f, ...) if (NULL == p) { x = e; BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } #define BalExitOnNullWithLastErrorSource(d, p, x, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } @@ -18,6 +19,7 @@ extern "C" { #define BalExitOnFailure(x, f, ...) BalExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) #define BalExitOnRootFailure(x, f, ...) BalExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) +#define BalExitWithRootFailure(x, e, f, ...) BalExitWithRootFailureSource(DUTIL_SOURCE_DEFAULT, x, e, f, __VA_ARGS__) #define BalExitOnLastError(x, f, ...) BalExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) #define BalExitOnNull(p, x, e, f, ...) BalExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, f, __VA_ARGS__) #define BalExitOnNullWithLastError(p, x, f, ...) BalExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, f, __VA_ARGS__) diff --git a/src/api/burn/bextutil/inc/bextutil.h b/src/api/burn/bextutil/inc/bextutil.h index ac9c0062..facaf2e8 100644 --- a/src/api/burn/bextutil/inc/bextutil.h +++ b/src/api/burn/bextutil/inc/bextutil.h @@ -11,6 +11,7 @@ extern "C" { #define BextExitOnFailureSource(d, x, f, ...) if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } #define BextExitOnRootFailureSource(d, x, f, ...) if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } +#define BextExitWithRootFailureSource(d, x, e, f, ...) { x = FAILED(e) ? e : E_FAIL; BextLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } #define BextExitOnLastErrorSource(d, x, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } } #define BextExitOnNullSource(d, p, x, e, f, ...) if (NULL == p) { x = e; BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } #define BextExitOnNullWithLastErrorSource(d, p, x, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } @@ -18,6 +19,7 @@ extern "C" { #define BextExitOnFailure(x, f, ...) BextExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) #define BextExitOnRootFailure(x, f, ...) BextExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) +#define BextExitWithRootFailure(x, e, f, ...) BextExitWithRootFailureSource(DUTIL_SOURCE_DEFAULT, x, e, f, __VA_ARGS__) #define BextExitOnLastError(x, f, ...) BextExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) #define BextExitOnNull(p, x, e, f, ...) BextExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, f, __VA_ARGS__) #define BextExitOnNullWithLastError(p, x, f, ...) BextExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, f, __VA_ARGS__) diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp index 2283880c..5546b74f 100644 --- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp +++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp @@ -2389,6 +2389,11 @@ private: hr = ThemeRegisterVariableCallbacks(m_pTheme, EvaluateVariableConditionCallback, FormatVariableStringCallback, GetVariableNumericCallback, SetVariableNumericCallback, GetVariableStringCallback, SetVariableStringCallback, NULL); BalExitOnFailure(hr, "Failed to register variable theme callbacks."); + C_ASSERT(COUNT_WIXSTDBA_PAGE == countof(vrgwzPageNames)); + C_ASSERT(countof(m_rgdwPageIds) == countof(vrgwzPageNames)); + + ThemeGetPageIds(m_pTheme, vrgwzPageNames, m_rgdwPageIds, countof(m_rgdwPageIds)); + hr = ThemeLocalize(m_pTheme, m_pWixLoc); BalExitOnFailure(hr, "Failed to localize theme: %ls", sczThemePath); @@ -2697,6 +2702,8 @@ private: hr = ThemeCreateParentWindow(m_pTheme, 0, wc.lpszClassName, m_pTheme->sczCaption, dwWindowStyle, x, y, HWND_DESKTOP, m_hModule, this, THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, &m_hWnd); ExitOnFailure(hr, "Failed to create window."); + OnThemeLoaded(); + hr = S_OK; LExit: @@ -2831,16 +2838,12 @@ private: return lres; } - case WM_CREATE: - if (!pBA->OnCreate(hWnd)) - { - return -1; - } - break; - case WM_THMUTIL_LOADING_CONTROL: return pBA->OnThemeLoadingControl(reinterpret_cast(wParam), reinterpret_cast(lParam)); + case WM_THMUTIL_LOADED_CONTROL: + return pBA->OnThemeLoadedControl(reinterpret_cast(wParam), reinterpret_cast(lParam)); + case WM_QUERYENDSESSION: fCancel = true; pBA->OnSystemShutdown(static_cast(lParam), &fCancel); @@ -2896,74 +2899,14 @@ private: // - // OnCreate - finishes loading the theme. + // OnThemeLoaded - finishes loading the theme. // - BOOL OnCreate( - __in HWND /*hWnd*/ - ) + BOOL OnThemeLoaded() { HRESULT hr = S_OK; - LPWSTR sczLicenseFormatted = NULL; - LPWSTR sczLicensePath = NULL; - LPWSTR sczLicenseDirectory = NULL; - LPWSTR sczLicenseFilename = NULL; BA_FUNCTIONS_ONTHEMELOADED_ARGS themeLoadedArgs = { }; BA_FUNCTIONS_ONTHEMELOADED_RESULTS themeLoadedResults = { }; - hr = ThemeLoadControls(m_pTheme); - BalExitOnFailure(hr, "Failed to load theme controls."); - - C_ASSERT(COUNT_WIXSTDBA_PAGE == countof(vrgwzPageNames)); - C_ASSERT(countof(m_rgdwPageIds) == countof(vrgwzPageNames)); - - ThemeGetPageIds(m_pTheme, vrgwzPageNames, m_rgdwPageIds, countof(m_rgdwPageIds)); - - // Load the RTF EULA control with text if the control exists. - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT)) - { - hr = (m_sczLicenseFile && *m_sczLicenseFile) ? S_OK : E_INVALIDDATA; - if (SUCCEEDED(hr)) - { - hr = StrAllocString(&sczLicenseFormatted, m_sczLicenseFile, 0); - if (SUCCEEDED(hr)) - { - hr = LocLocalizeString(m_pWixLoc, &sczLicenseFormatted); - if (SUCCEEDED(hr)) - { - // Assume there is no hidden variables to be formatted - // so don't worry about securely freeing it. - hr = BalFormatString(sczLicenseFormatted, &sczLicenseFormatted); - if (SUCCEEDED(hr)) - { - hr = PathRelativeToModule(&sczLicensePath, sczLicenseFormatted, m_hModule); - if (SUCCEEDED(hr)) - { - hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory); - if (SUCCEEDED(hr)) - { - hr = StrAllocString(&sczLicenseFilename, PathFile(sczLicenseFormatted), 0); - if (SUCCEEDED(hr)) - { - hr = LocProbeForFile(sczLicenseDirectory, sczLicenseFilename, m_sczLanguage, &sczLicensePath); - if (SUCCEEDED(hr)) - { - hr = ThemeLoadRichEditFromFile(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT, sczLicensePath, m_hModule); - } - } - } - } - } - } - } - } - - if (FAILED(hr)) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load file into license richedit control from path '%ls' manifest value: %ls", sczLicensePath, m_sczLicenseFile); - hr = S_OK; - } - } - if (m_pfnBAFunctionsProc) { themeLoadedArgs.cbSize = sizeof(themeLoadedArgs); @@ -2975,11 +2918,6 @@ private: } LExit: - ReleaseStr(sczLicenseFilename); - ReleaseStr(sczLicenseDirectory); - ReleaseStr(sczLicensePath); - ReleaseStr(sczLicenseFormatted); - return SUCCEEDED(hr); } @@ -3034,6 +2972,108 @@ private: return fProcessed || FAILED(hr); } + BOOL OnThemeLoadedControl( + __in const THEME_LOADEDCONTROL_ARGS* pArgs, + __in THEME_LOADEDCONTROL_RESULTS* pResults + ) + { + HRESULT hr = S_OK; + BOOL fProcessed = FALSE; + BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS themeControlLoadedArgs = { }; + BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS themeControlLoadedResults = { }; + + if (WIXSTDBA_CONTROL_EULA_RICHEDIT == pArgs->pThemeControl->wId) + { + // Best effort to load the RTF EULA control with text. + OnLoadedEulaRtfControl(pArgs->pThemeControl); + fProcessed = TRUE; + ExitFunction(); + } + + if (m_pfnBAFunctionsProc) + { + themeControlLoadedArgs.cbSize = sizeof(themeControlLoadedArgs); + themeControlLoadedArgs.wzName = pArgs->pThemeControl->sczName; + themeControlLoadedArgs.wId = pArgs->pThemeControl->wId; + themeControlLoadedArgs.hWnd = pArgs->pThemeControl->hWnd; + + themeControlLoadedResults.cbSize = sizeof(themeControlLoadedResults); + + hr = m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADED, &themeControlLoadedArgs, &themeControlLoadedResults, m_pvBAFunctionsProcContext); + + if (E_NOTIMPL == hr) + { + hr = S_OK; + } + else + { + BalExitOnFailure(hr, "BAFunctions OnThemeControlLoaded failed."); + + if (themeControlLoadedResults.fProcessed) + { + fProcessed = TRUE; + } + } + } + + LExit: + pResults->hr = hr; + return fProcessed || FAILED(hr); + } + + HRESULT OnLoadedEulaRtfControl( + const THEME_CONTROL* pThemeControl + ) + { + HRESULT hr = S_OK; + LPWSTR sczLicenseFormatted = NULL; + LPWSTR sczLicensePath = NULL; + LPWSTR sczLicenseDirectory = NULL; + LPWSTR sczLicenseFilename = NULL; + + if (!m_sczLicenseFile || !*m_sczLicenseFile) + { + ExitWithRootFailure(hr, E_INVALIDDATA, "No license file in manifest."); + } + + hr = StrAllocString(&sczLicenseFormatted, m_sczLicenseFile, 0); + ExitOnFailure(hr, "Failed to copy manifest license file."); + + hr = LocLocalizeString(m_pWixLoc, &sczLicenseFormatted); + ExitOnFailure(hr, "Failed to localize manifest license file."); + + hr = BalFormatString(sczLicenseFormatted, &sczLicenseFormatted); + ExitOnFailure(hr, "Failed to expand localized manifest license file."); + + hr = PathRelativeToModule(&sczLicensePath, sczLicenseFormatted, m_hModule); + ExitOnFailure(hr, "Failed to get relative path for license file."); + + hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory); + ExitOnFailure(hr, "Failed to get license file directory."); + + hr = StrAllocString(&sczLicenseFilename, PathFile(sczLicenseFormatted), 0); + ExitOnFailure(hr, "Failed to copy license file name."); + + hr = LocProbeForFile(sczLicenseDirectory, sczLicenseFilename, m_sczLanguage, &sczLicensePath); + ExitOnFailure(hr, "Failed to probe for localized license file."); + + hr = ThemeLoadRichEditFromFile(m_pTheme, pThemeControl->wId, sczLicensePath, m_hModule); + ExitOnFailure(hr, "Failed to load license file into richedit control."); + + LExit: + if (FAILED(hr)) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load file into license richedit control from path '%ls' manifest value: %ls", sczLicensePath, m_sczLicenseFile); + } + + ReleaseStr(sczLicenseFilename); + ReleaseStr(sczLicenseDirectory); + ReleaseStr(sczLicensePath); + ReleaseStr(sczLicenseFormatted); + + return hr; + } + // // OnShowFailure - display the failure page. diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h index 9557c11c..eda81485 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h @@ -6,6 +6,10 @@ extern "C" { #endif +// forward declare + +typedef struct _THEME THEME; + #define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( @@ -111,6 +115,10 @@ typedef enum _WM_THMUTIL // wparam is THEME_CONTROLWMNOTIFY_ARGS* and lparam is THEME_CONTROLWMNOTIFY_RESULTS*. // Return code is TRUE to prevent further processing of the message. WM_THMUTIL_CONTROL_WM_NOTIFY = WM_APP - 3, + // Sent after created a control. + // wparam is THEME_LOADEDCONTROL_ARGS* and lparam is THEME_LOADEDCONTROL_RESULTS*. + // Return code is TRUE if it was processed. + WM_THMUTIL_LOADED_CONTROL = WM_APP - 4, } WM_THMUTIL; struct THEME_COLUMN @@ -287,6 +295,7 @@ struct THEME_CONTROL // state variables that should be ignored HWND hWnd; DWORD dwData; // type specific data + THEME* pTheme; }; @@ -340,7 +349,7 @@ struct THEME_FONT }; -struct THEME +typedef struct _THEME { WORD wNextControlId; @@ -403,7 +412,7 @@ struct THEME PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; LPVOID pvVariableContext; -}; +} THEME; typedef struct _THEME_CONTROLWMCOMMAND_ARGS { @@ -431,6 +440,18 @@ typedef struct _THEME_CONTROLWMNOTIFY_RESULTS LRESULT lResult; } THEME_CONTROLWMNOTIFY_RESULTS; +typedef struct _THEME_LOADEDCONTROL_ARGS +{ + DWORD cbSize; + const THEME_CONTROL* pThemeControl; +} THEME_LOADEDCONTROL_ARGS; + +typedef struct _THEME_LOADEDCONTROL_RESULTS +{ + DWORD cbSize; + HRESULT hr; +} THEME_LOADEDCONTROL_RESULTS; + typedef struct _THEME_LOADINGCONTROL_ARGS { DWORD cbSize; @@ -527,24 +548,6 @@ HRESULT DAPI ThemeCreateParentWindow( __out_opt HWND* phWnd ); -/******************************************************************** - ThemeLoadControls - creates the windows for all the theme controls - using the window created in ThemeCreateParentWindow. - -*******************************************************************/ -HRESULT DAPI ThemeLoadControls( - __in THEME* pTheme - ); - -/******************************************************************** - ThemeUnloadControls - resets all the theme control windows so the theme - controls can be reloaded. - -*******************************************************************/ -void DAPI ThemeUnloadControls( - __in THEME* pTheme - ); - /******************************************************************** ThemeLocalize - Localizes all of the strings in the theme. diff --git a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp index a3c5d80c..068638f6 100644 --- a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp @@ -205,6 +205,7 @@ static HRESULT ParseControl( __in_opt THEME_PAGE* pPage ); static void InitializeThemeControl( + THEME* pTheme, THEME_CONTROL* pControl ); static HRESULT ParseActions( @@ -273,6 +274,12 @@ static HRESULT FindImageList( __in_z LPCWSTR wzImageListName, __out HIMAGELIST *phImageList ); +static HRESULT LoadThemeControls( + __in THEME* pTheme + ); +static void UnloadThemeControls( + __in THEME* pTheme + ); static HRESULT OnLoadingControl( __in THEME* pTheme, __in const THEME_CONTROL* pControl, @@ -452,6 +459,10 @@ static BOOL OnNotifyEnMsgFilter( __in const THEME_CONTROL* pThemeControl, __in MSGFILTER* msgFilter ); +static BOOL OnPanelCreate( + __in THEME_CONTROL* pControl, + __in HWND hWnd + ); static BOOL OnWmCommand( __in THEME* pTheme, __in WPARAM wParam, @@ -888,35 +899,6 @@ LExit: return hr; } - -DAPI_(HRESULT) ThemeLoadControls( - __in THEME* pTheme - ) -{ - HRESULT hr = S_OK; - - if (!pTheme->hwndParent) - { - ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created."); - } - - hr = LoadControls(pTheme, NULL); - -LExit: - return hr; -} - - -DAPI_(void) ThemeUnloadControls( - __in THEME* pTheme - ) -{ - UnloadControls(pTheme->cControls, pTheme->rgControls); - - pTheme->hwndHover = NULL; - pTheme->hwndParent = NULL; -} - DAPI_(HRESULT) ThemeLocalize( __in THEME *pTheme, __in const WIX_LOCALIZATION *pWixLoc @@ -1089,6 +1071,17 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc( } break; + case WM_CREATE: + if (FAILED(LoadThemeControls(pTheme))) + { + return -1; + } + break; + + case WM_DESTROY: + UnloadThemeControls(pTheme); + break; + case WM_NCHITTEST: if (pTheme->dwStyle & WS_POPUP) { @@ -3406,7 +3399,7 @@ static HRESULT ParseControl( BOOL fAnyTextChildren = FALSE; BOOL fAnyNoteChildren = FALSE; - InitializeThemeControl(pControl); + InitializeThemeControl(pTheme, pControl); hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control Name attribute."); @@ -3715,6 +3708,7 @@ LExit: } static void InitializeThemeControl( + THEME* pTheme, THEME_CONTROL* pControl ) { @@ -3722,6 +3716,7 @@ static void InitializeThemeControl( pControl->dwFontId = THEME_INVALID_ID; pControl->dwFontSelectedId = THEME_INVALID_ID; pControl->uStringId = UINT_MAX; + pControl->pTheme = pTheme; } @@ -3854,7 +3849,7 @@ static HRESULT ParseBillboardPanels( pControl = pParentControl->rgControls + pParentControl->cControls; pParentControl->cControls += 1; pControl->type = THEME_CONTROL_TYPE_PANEL; - InitializeThemeControl(pControl); + InitializeThemeControl(pTheme, pControl); if (pPage) { @@ -5400,6 +5395,24 @@ static BOOL OnNotifyEnMsgFilter( return fProcessed; } +static BOOL OnPanelCreate( + __in THEME_CONTROL* pControl, + __in HWND hWnd + ) +{ + HRESULT hr = S_OK; + + ThmExitOnNull(pControl, hr, E_INVALIDSTATE, "Null control for OnPanelCreate"); + + pControl->hWnd = hWnd; + + hr = LoadControls(pControl->pTheme, pControl); + ThmExitOnFailure(hr, "Failed to load panel controls."); + +LExit: + return SUCCEEDED(hr); +} + static BOOL OnWmCommand( __in THEME* pTheme, __in WPARAM wParam, @@ -6017,17 +6030,24 @@ static LRESULT CALLBACK PanelWndProc( ) { LRESULT lres = 0; - THEME* pTheme = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + THEME_CONTROL* pControl = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); switch (uMsg) { case WM_NCCREATE: { LPCREATESTRUCTW lpcs = reinterpret_cast(lParam); - pTheme = reinterpret_cast(lpcs->lpCreateParams); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pTheme)); + pControl = reinterpret_cast(lpcs->lpCreateParams); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pControl)); + break; } - break; + + case WM_CREATE: + if (!OnPanelCreate(pControl, hWnd)) + { + return -1; + } + break; case WM_NCDESTROY: lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -6039,7 +6059,7 @@ static LRESULT CALLBACK PanelWndProc( break; } - return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); + return ControlGroupDefWindowProc(pControl ? pControl->pTheme : NULL, hWnd, uMsg, wParam, lParam); } static LRESULT CALLBACK StaticOwnerDrawWndProc( @@ -6087,6 +6107,30 @@ static HRESULT OnLoadingControl( return hr; } +static HRESULT LoadThemeControls( + __in THEME* pTheme + ) +{ + HRESULT hr = S_OK; + + ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "LoadThemeControls called before theme parent window created."); + + hr = LoadControls(pTheme, NULL); + +LExit: + return hr; +} + +static void UnloadThemeControls( + __in THEME* pTheme + ) +{ + UnloadControls(pTheme->cControls, pTheme->rgControls); + + pTheme->hwndHover = NULL; + pTheme->hwndParent = NULL; +} + static HRESULT LoadControls( __in THEME* pTheme, __in_opt THEME_CONTROL* pParentControl @@ -6103,10 +6147,15 @@ static HRESULT LoadControls( int h = 0; int x = 0; int y = 0; + THEME_LOADEDCONTROL_ARGS loadedControlArgs = { }; + THEME_LOADEDCONTROL_RESULTS loadedControlResults = { }; GetControls(pTheme, pParentControl, cControls, rgControls); ::GetClientRect(hwndParent, &rcParent); + loadedControlArgs.cbSize = sizeof(loadedControlArgs); + loadedControlResults.cbSize = sizeof(loadedControlResults); + for (DWORD i = 0; i < cControls; ++i) { THEME_CONTROL* pControl = rgControls + i; @@ -6124,8 +6173,7 @@ static HRESULT LoadControls( switch (pControl->type) { - case THEME_CONTROL_TYPE_BILLBOARD: - __fallthrough; + case THEME_CONTROL_TYPE_BILLBOARD: __fallthrough; case THEME_CONTROL_TYPE_PANEL: wzWindowClass = vsczPanelClass; dwWindowExBits |= WS_EX_CONTROLPARENT; @@ -6309,7 +6357,7 @@ static HRESULT LoadControls( pControl->dwStyle &= ~WS_VISIBLE; } - pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pTheme); + pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast(wControlId), NULL, pControl); ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); if (pControl->sczTooltip) @@ -6445,10 +6493,15 @@ static HRESULT LoadControls( } } - if (pControl->cControls) + loadedControlArgs.pThemeControl = pControl; + loadedControlResults.hr = E_NOTIMPL; + if (::SendMessageW(pTheme->hwndParent, WM_THMUTIL_LOADED_CONTROL, reinterpret_cast(&loadedControlArgs), reinterpret_cast(&loadedControlResults))) { - hr = LoadControls(pTheme, pControl); - ThmExitOnFailure(hr, "Failed to load child controls."); + if (E_NOTIMPL != loadedControlResults.hr) + { + hr = loadedControlResults.hr; + ThmExitOnFailure(hr, "ThmLoadedControl failed"); + } } } diff --git a/src/samples/thmviewer/display.cpp b/src/samples/thmviewer/display.cpp index c0e6c7e1..e64f79c6 100644 --- a/src/samples/thmviewer/display.cpp +++ b/src/samples/thmviewer/display.cpp @@ -21,9 +21,10 @@ static LRESULT CALLBACK DisplayWndProc( __in WPARAM wParam, __in LPARAM lParam ); -static BOOL DisplayOnCreate( +static BOOL DisplayOnThmLoadedControl( __in THEME* pTheme, - __in HWND hWnd + __in const THEME_LOADEDCONTROL_ARGS* args, + __in THEME_LOADEDCONTROL_RESULTS* results ); @@ -270,13 +271,6 @@ static LRESULT CALLBACK DisplayWndProc( } break; - case WM_CREATE: - if (!DisplayOnCreate(pHandleTheme->pTheme, hWnd)) - { - return -1; - } - break; - case WM_TIMER: if (!lParam && SUCCEEDED(ThemeSetProgressControl(pHandleTheme->pTheme, wParam, dwProgress))) { @@ -308,47 +302,45 @@ static LRESULT CALLBACK DisplayWndProc( } break; - case WM_DESTROY: - ThemeUnloadControls(pHandleTheme->pTheme); - ::PostQuitMessage(0); - break; - case WM_NCDESTROY: DecrementHandleTheme(pHandleTheme); ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + ::PostQuitMessage(0); break; + + case WM_THMUTIL_LOADED_CONTROL: + if (pHandleTheme) + { + return DisplayOnThmLoadedControl(pHandleTheme->pTheme, reinterpret_cast(wParam), reinterpret_cast(lParam)); + } } return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam); } -static BOOL DisplayOnCreate( +static BOOL DisplayOnThmLoadedControl( __in THEME* pTheme, - __in HWND hWnd + __in const THEME_LOADEDCONTROL_ARGS* args, + __in THEME_LOADEDCONTROL_RESULTS* results ) { HRESULT hr = S_OK; - - hr = ThemeLoadControls(pTheme); - ExitOnFailure(hr, "Failed to load theme controls"); + const THEME_CONTROL* pControl = args->pThemeControl; // Pre-populate some control types with data. - for (DWORD i = 0; i < pTheme->cControls; ++i) + if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) { - THEME_CONTROL* pControl = pTheme->rgControls + i; - if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) - { - hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL)); - ExitOnFailure(hr, "Failed to load richedit text."); - } - else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) - { - DWORD dwId = ::SetTimer(hWnd, pControl->wId, 500, NULL); - dwId = dwId; // prevents warning in "ship" build. - Assert(dwId == pControl->wId); - } + hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL)); + ExitOnFailure(hr, "Failed to load richedit text."); + } + else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) + { + DWORD dwId = ::SetTimer(pTheme->hwndParent, pControl->wId, 500, NULL); + dwId = dwId; // prevents warning in "ship" build. + Assert(dwId == pControl->wId); } LExit: - return SUCCEEDED(hr); + results->hr = hr; + return TRUE; } diff --git a/src/samples/thmviewer/thmviewer.cpp b/src/samples/thmviewer/thmviewer.cpp index e593d6ad..1d941323 100644 --- a/src/samples/thmviewer/thmviewer.cpp +++ b/src/samples/thmviewer/thmviewer.cpp @@ -353,16 +353,7 @@ static LRESULT CALLBACK MainWndProc( case WM_NCDESTROY: DecrementHandleTheme(pHandleTheme); ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - break; - - case WM_CREATE: - { - HRESULT hr = ThemeLoadControls(vpTheme); - if (FAILED(hr)) - { - return -1; - } - } + ::PostQuitMessage(0); break; case WM_THMVWR_THEME_LOAD_BEGIN: @@ -377,10 +368,6 @@ static LRESULT CALLBACK MainWndProc( OnNewTheme(vpTheme, hWnd, reinterpret_cast(lParam)); return 0; - case WM_DESTROY: - ::PostQuitMessage(0); - break; - case WM_THMUTIL_LOADING_CONTROL: return OnThemeLoadingControl(reinterpret_cast(wParam), reinterpret_cast(lParam)); diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp index a5bcba3e..8304403a 100644 --- a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp +++ b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp @@ -230,18 +230,14 @@ private: return lres; } - case WM_CREATE: - if (!pBaf->OnCreate(hWnd)) - { - return -1; - } - break; - case WM_THMUTIL_LOADING_CONTROL: return pBaf->OnThemeLoadingControl(reinterpret_cast(wParam), reinterpret_cast(lParam)); + case WM_THMUTIL_LOADED_CONTROL: + return pBaf->OnThemeLoadedControl(hWnd, reinterpret_cast(wParam), reinterpret_cast(lParam)); + case WM_TIMER: - if (!lParam && pBaf) + if (!lParam && BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_IMAGE == wParam && pBaf) { pBaf->UpdateProgressBarProgress(); @@ -253,33 +249,14 @@ private: return ThemeDefWindowProc(pBaf ? pBaf->m_pBafTheme : NULL, hWnd, uMsg, wParam, lParam); } - BOOL OnCreate( - __in HWND hWnd + HRESULT OnCreatedListView( + __in HWND hWndListView ) { HRESULT hr = S_OK; LVITEMW lvitem = { }; LVGROUP lvgroup = { }; static UINT puColumns[] = { 0, 1, 2 }; - HWND hwndTopLeft = NULL; - HWND hwndTopRight = NULL; - HWND hwndBottomLeft = NULL; - HWND hwndBottomRight = NULL; - - hr = ThemeLoadControls(m_pBafTheme); - BalExitOnFailure(hr, "Failed to load theme controls."); - - hwndTopLeft = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_LEFT); - BalExitOnNull(hwndTopLeft, hr, E_INVALIDSTATE, "Failed to get top left list view hWnd."); - - hwndTopRight = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_RIGHT); - BalExitOnNull(hwndTopRight, hr, E_INVALIDSTATE, "Failed to get top right list view hWnd."); - - hwndBottomLeft = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_LEFT); - BalExitOnNull(hwndBottomLeft, hr, E_INVALIDSTATE, "Failed to get bottom left list view hWnd."); - - hwndBottomRight = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_RIGHT); - BalExitOnNull(hwndBottomRight, hr, E_INVALIDSTATE, "Failed to get bottom right list view hWnd."); lvgroup.cbSize = sizeof(LVGROUP); lvgroup.mask = LVGF_GROUPID | LVGF_TITLEIMAGE | LVGF_DESCRIPTIONTOP | LVGF_HEADER; @@ -295,10 +272,7 @@ private: hr = StrAllocFormatted(&lvgroup.pszHeader, L"Header_%d", i); BalExitOnFailure(hr, "Failed to alloc list view group header."); - ListView_InsertGroup(hwndTopLeft, -1, &lvgroup); - ListView_InsertGroup(hwndTopRight, -1, &lvgroup); - ListView_InsertGroup(hwndBottomLeft, -1, &lvgroup); - ListView_InsertGroup(hwndBottomRight, -1, &lvgroup); + ListView_InsertGroup(hWndListView, -1, &lvgroup); lvitem.mask = LVIF_COLUMNS | LVIF_GROUPID | LVIF_IMAGE | LVIF_TEXT; lvitem.iItem = i; @@ -312,10 +286,7 @@ private: lvitem.cColumns = countof(puColumns); lvitem.puColumns = puColumns; - ListView_InsertItem(hwndTopLeft, &lvitem); - ListView_InsertItem(hwndTopRight, &lvitem); - ListView_InsertItem(hwndBottomLeft, &lvitem); - ListView_InsertItem(hwndBottomRight, &lvitem); + ListView_InsertItem(hWndListView, &lvitem); for (int j = 0; j < 3; ++j) { @@ -325,23 +296,16 @@ private: hr = StrAllocFormatted(&lvitem.pszText, L"%d_%d", j, i); BalExitOnFailure(hr, "Failed to alloc list view subitem text."); - ListView_InsertItem(hwndTopLeft, &lvitem); - ListView_InsertItem(hwndTopRight, &lvitem); - ListView_InsertItem(hwndBottomLeft, &lvitem); - ListView_InsertItem(hwndBottomRight, &lvitem); + ListView_InsertItem(hWndListView, &lvitem); } } - ListView_EnableGroupView(hwndTopRight, TRUE); - - ::SetTimer(hWnd, BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_IMAGE, 500, NULL); - LExit: ReleaseStr(lvgroup.pszDescriptionTop); ReleaseStr(lvgroup.pszHeader); ReleaseStr(lvitem.pszText); - return SUCCEEDED(hr); + return hr; } BOOL OnThemeLoadingControl( @@ -349,17 +313,60 @@ private: __in THEME_LOADINGCONTROL_RESULTS* pResults ) { + HRESULT hr = S_OK; + BOOL fProcessed = FALSE; + for (DWORD iAssignControl = 0; iAssignControl < countof(vrgInitControls); ++iAssignControl) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pArgs->pThemeControl->sczName, -1, vrgInitControls[iAssignControl].wzName, -1)) { + fProcessed = TRUE; pResults->wId = vrgInitControls[iAssignControl].wId; break; } } - pResults->hr = S_OK; - return TRUE; + pResults->hr = hr; + return fProcessed || FAILED(hr); + } + + BOOL OnThemeLoadedControl( + __in HWND hWndParent, + __in const THEME_LOADEDCONTROL_ARGS* pArgs, + __in THEME_LOADEDCONTROL_RESULTS* pResults + ) + { + HRESULT hr = S_OK; + BOOL fProcessed = FALSE; + + switch (pArgs->pThemeControl->wId) + { + case BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_LEFT: + case BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_RIGHT: + case BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_LEFT: + case BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_RIGHT: + fProcessed = TRUE; + + hr = OnCreatedListView(pArgs->pThemeControl->hWnd); + ExitOnFailure(hr, "Failed to populate list view."); + + if (BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_RIGHT == pArgs->pThemeControl->wId) + { + ListView_EnableGroupView(pArgs->pThemeControl->hWnd, TRUE); + } + + break; + + case BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_STANDARD: + fProcessed = TRUE; + + ::SetTimer(hWndParent, BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_IMAGE, 500, NULL); + break; + } + + LExit: + pResults->hr = hr; + return fProcessed || FAILED(hr); } void UpdateProgressBarProgress() diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml b/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml index 0d0dabf5..7491ab1c 100644 --- a/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml +++ b/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml @@ -124,6 +124,27 @@ There are currently four states for a button: default, focus, hover, and selecte Back + + + + + + + + + + + + + + + + + + TL One + TL Two + TL Three + + + TR One + TR Two + TR Exp + + + BL One + BL Two + BL Three + + + BR One + BR Two + BR Exp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3-55-g6feb