///////////////////////////////////////////////////////////////////////////// // DynamicDialogTemplate.h - Class used to construct an in-memory dialog // template, along with controls to go on the dialog // // Written by Daniel Bowen (dbowen@es.com) // Copyright (c) 2003-2004 Daniel Bowen. // // This code may be used in compiled form in any way you desire. This // file may be redistributed by any means PROVIDING it is // not sold for profit without the authors written consent, and // providing that this notice and the authors name is included. // // This file is provided "as is" with no expressed or implied warranty. // The author accepts no liability if it causes any damage to you or your // computer whatsoever. // // If you find bugs, have suggestions for improvements, etc., // please contact the author. // // // CDynamicDialogTemplate - Used to construct an in-memory dialog template // based on DLGTEMPLATE and DLGITEMTEMPLATE // // CDynamicDialogExTemplate - Used to construct an in-memory dialog template // based on DLGTEMPLATEEX and DLGITEMTEMPLATEEX #ifndef __DynamicDialogTemplate_h__ #define __DynamicDialogTemplate_h__ // To find out more about in-memory dialog templates, see the MSDN library // for a description of DLGTEMPLATE and DLGITEMTEMPLATE namespace DynamicDialog { struct DynamicDialogItemSize { public: short x, y, cx, cy; DynamicDialogItemSize(short left = 0, short top = 0, short width = 0, short height = 0) : x(left), y(top), cx(width), cy(height) { } void Set(short left, short top, short width, short height) { x=left, y=top, cx=width, cy=height; } void Set(RECT rect) { x=(short)rect.left; y=(short)rect.top; cx=(short)(rect.right - rect.left); cy=(short)(rect.bottom - rect.top); } }; class CDynamicDialogTemplate { protected: HGLOBAL m_hDialogTemplateMemory; size_t m_bytesAllocated; size_t m_bytesUsed; protected: enum { byteAllocationChunk = 256 }; // See help on DLGITEMTEMPLATE enum ClassAtom { eClassAtom_Button = 0x0080, eClassAtom_Edit = 0x0081, eClassAtom_Static = 0x0082, eClassAtom_ListBox = 0x0083, eClassAtom_ScrollBar = 0x0084, eClassAtom_ComboBox = 0x0085, }; // Constructor/Destructor public: CDynamicDialogTemplate() : m_hDialogTemplateMemory(NULL), m_bytesAllocated(0), m_bytesUsed(0) { } virtual ~CDynamicDialogTemplate() { this->Destroy(); } // Operators public: operator bool() { return (m_hDialogTemplateMemory != NULL); } operator HGLOBAL() { return m_hDialogTemplateMemory; } operator DLGTEMPLATE*() { return this->GetDLGTEMPLATE(); } HGLOBAL GetHGLOBAL(void) { return m_hDialogTemplateMemory; } DLGTEMPLATE* GetDLGTEMPLATE(void) { DLGTEMPLATE* dialogTemplate = (DLGTEMPLATE*)::GlobalLock(m_hDialogTemplateMemory); ::GlobalUnlock(m_hDialogTemplateMemory); // This should be valid at least until something causes a GlobalReAlloc. // No one should call this (explicitly or implictly) until the dialog // resource has been fully constructed return dialogTemplate; } public: bool Create( UINT cxDLU, UINT cyDLU, UINT style, UINT styleEx, const wchar_t* dialogTitle, const wchar_t* fontName = L"MS Sans Serif", UINT fontSize = 8) { if(m_hDialogTemplateMemory) { return false; } m_hDialogTemplateMemory = ::GlobalAlloc((GMEM_MOVEABLE | GMEM_ZEROINIT), byteAllocationChunk); if(m_hDialogTemplateMemory) { m_bytesAllocated = byteAllocationChunk; DLGTEMPLATE* pDialogTemplate = (DLGTEMPLATE*) ::GlobalLock(m_hDialogTemplateMemory); if(pDialogTemplate) { // Define dialog template pDialogTemplate->style = style; pDialogTemplate->dwExtendedStyle = styleEx; pDialogTemplate->cdit = 0; pDialogTemplate->x = 0; pDialogTemplate->y = 0; pDialogTemplate->cx = (short)cxDLU; pDialogTemplate->cy = (short)cyDLU; // Get a pointer to the WORD after the structure WORD* pWordPtr = (WORD*)(pDialogTemplate + 1); // no menu *pWordPtr++ = 0; // default dialog box class *pWordPtr++ = 0; // Dialog title if(dialogTitle) { wchar_t* unicodeString = (wchar_t*) pWordPtr; // dialog caption lstrcpyW(unicodeString, dialogTitle); pWordPtr += ::lstrlenW(unicodeString); } // NUL termination for string *pWordPtr++ = 0; if((style & DS_SETFONT) == DS_SETFONT) { // font size *pWordPtr++ = (WORD)fontSize; if(fontName) { wchar_t* unicodeString = (wchar_t*) pWordPtr; // font name lstrcpyW(unicodeString, fontName); pWordPtr += ::lstrlenW(unicodeString); } // NUL termination for string *pWordPtr++ = 0; } m_bytesUsed = (size_t)((BYTE*)pWordPtr - (BYTE*)pDialogTemplate); ::GlobalUnlock(m_hDialogTemplateMemory); } } return m_hDialogTemplateMemory ? true : false; } void Destroy(void) { if(m_hDialogTemplateMemory) { ::GlobalFree(m_hDialogTemplateMemory); m_hDialogTemplateMemory = NULL; m_bytesAllocated = 0; m_bytesUsed = 0; } } bool AddControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text, size_t textWordCount, const wchar_t* classStringOrAtom, size_t classStringOrAtomWordCount) { if(m_hDialogTemplateMemory == NULL) { return false; } // padding (class atom, NULL termination, etc.) const size_t padding = 16; size_t estimatedBytesNeeded = (size_t) sizeof(DLGITEMTEMPLATE) + (textWordCount*sizeof(WORD)) + (classStringOrAtomWordCount*sizeof(WORD)) + padding; if((m_bytesAllocated - m_bytesUsed) < estimatedBytesNeeded) { // Align the bytesToAllocate to be a multiple of the chunk size size_t bytesToAllocate = (((m_bytesUsed + estimatedBytesNeeded) / byteAllocationChunk) * byteAllocationChunk) + byteAllocationChunk; HGLOBAL hDialogTemplateReallocatedMemory = ::GlobalReAlloc(m_hDialogTemplateMemory, bytesToAllocate, (GMEM_MOVEABLE | GMEM_ZEROINIT)); if(hDialogTemplateReallocatedMemory == NULL) { return false; } else { m_bytesAllocated = bytesToAllocate; m_hDialogTemplateMemory = hDialogTemplateReallocatedMemory; } } DLGTEMPLATE* pDialogTemplate = (DLGTEMPLATE*) ::GlobalLock(m_hDialogTemplateMemory); if(pDialogTemplate) { // Increment the control count pDialogTemplate->cdit += 1; // Get a pointer to the "end" BYTE* pOffset = (BYTE*)pDialogTemplate + m_bytesUsed; // Fill out the structure and following bytes for the control. // DLGITEMTEMPLATE structures should be aligned on DWORD boundaries. DLGITEMTEMPLATE* pDialogItem = (DLGITEMTEMPLATE*) (((DWORD_PTR)pOffset + 3) & ~3); pDialogItem->style = style; pDialogItem->dwExtendedStyle = dwExtendedStyle; pDialogItem->x = x; pDialogItem->y = y; pDialogItem->cx = cx; pDialogItem->cy = cy; pDialogItem->id = id; // Get a pointer to the WORD after the structure WORD* pWordPtr = (WORD*) (pDialogItem + 1); // Class Array (string or atom) if(classStringOrAtom) { ::CopyMemory(pWordPtr, classStringOrAtom, classStringOrAtomWordCount * sizeof(WORD)); pWordPtr += classStringOrAtomWordCount; // NOTE! "classStringOrAtomWordCount" should include the // terminating NUL (if its not an atom value) } else { // Default to "Static" control if no control class or atom is provided *pWordPtr++ = 0xFFFF; *pWordPtr++ = eClassAtom_Static; } // Title Array (text or resource id) if(text) { ::CopyMemory(pWordPtr, text, textWordCount * sizeof(WORD)); pWordPtr += textWordCount; // NOTE! "textWordCount" should include the // terminating NUL (if its not a resource ID value) } else { // default to 0 length string *pWordPtr++ = 0; } // no creation data *pWordPtr++ = 0; m_bytesUsed = (size_t)((BYTE*)pWordPtr - (BYTE*)pDialogTemplate); ::GlobalUnlock(m_hDialogTemplateMemory); } return true; } bool AddControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text, const wchar_t* classString) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, text ? ::lstrlenW(text) + 1 : 0, classString, classString ? ::lstrlenW(classString) + 1 : 0); } bool AddControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text, const wchar_t* classString) { return this->AddControl(style, dwExtendedStyle, dialogItemSize.x, dialogItemSize.y, dialogItemSize.cx, dialogItemSize.cy, id, text, text ? ::lstrlenW(text) + 1 : 0, classString, classString ? ::lstrlenW(classString) + 1 : 0); } bool AddControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text, ClassAtom atom) { const long classAtomWordCount = 2; WORD classAtom[classAtomWordCount] = {0xFFFF, (WORD)atom}; return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, text ? ::lstrlenW(text) + 1 : 0, (const wchar_t*)classAtom, classAtomWordCount); } bool AddControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text, ClassAtom atom) { const long classAtomWordCount = 2; WORD classAtom[classAtomWordCount] = {0xFFFF, (WORD)atom}; return this->AddControl(style, dwExtendedStyle, dialogItemSize.x, dialogItemSize.y, dialogItemSize.cx, dialogItemSize.cy, id, text, text ? ::lstrlenW(text) + 1 : 0, (const wchar_t*)classAtom, classAtomWordCount); } bool AddButtonControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, eClassAtom_Button); } bool AddButtonControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, dialogItemSize, id, text, eClassAtom_Button); } bool AddEditControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, eClassAtom_Edit); } bool AddEditControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, dialogItemSize, id, text, eClassAtom_Edit); } bool AddStaticControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, eClassAtom_Static); } bool AddStaticControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, dialogItemSize, id, text, eClassAtom_Static); } bool AddListBoxControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, eClassAtom_ListBox); } bool AddListBoxControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, dialogItemSize, id, text, eClassAtom_ListBox); } bool AddScrollBarControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, eClassAtom_ScrollBar); } bool AddScrollBarControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, dialogItemSize, id, text, eClassAtom_ScrollBar); } bool AddComboBoxControl( DWORD style, DWORD dwExtendedStyle, short x, short y, short cx, short cy, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, x, y, cx, cy, id, text, eClassAtom_ComboBox); } bool AddComboBoxControl( DWORD style, DWORD dwExtendedStyle, const DynamicDialogItemSize dialogItemSize, WORD id, const wchar_t* text) { return this->AddControl(style, dwExtendedStyle, dialogItemSize, id, text, eClassAtom_ComboBox); } }; class CDynamicDialogExTemplate { }; // CDynamicDialogImpl is just like CDialogImpl, but it uses CDynamicDialogTemplate, and // DialogBoxIndirectParam instead of DialogBoxParam and // CreateDialogIndirectParam instead of CreateDialogParam #if (_ATL_VER >= 0x0700) // Important! If ATL::CDialogImpl ever changes, reflect those changes here. // We don't inherit from CDialogImpl at all and completely duplicate (and // appropriately modify) everything it does. template class ATL_NO_VTABLE CDynamicDialogImpl : public ATL::CDialogImplBaseT< TBase > { protected: TDynamicDialogTemplate m_dynamicDialogTemplate; // Overrideables public: // You always need to override ConstructDialogResource //bool ConstructDialogResource(void) { } public: #ifdef _DEBUG bool m_bModal; CDynamicDialogImpl() : m_bModal(false) { } #endif //_DEBUG // modal dialogs INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow(), LPARAM dwInitParam = NULL) { ATLASSERT(m_hWnd == NULL); T* pT = static_cast(this); pT->ConstructDialogResource(); ATLASSERT((bool)m_dynamicDialogTemplate); _AtlWinModule.AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBaseT< TBase >*)this); #ifdef _DEBUG m_bModal = true; #endif //_DEBUG //return ::DialogBoxParam(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(static_cast(this)->IDD), // hWndParent, T::StartDialogProc, dwInitParam); return ::DialogBoxIndirectParam(_AtlBaseModule.GetResourceInstance(), m_dynamicDialogTemplate, hWndParent, T::StartDialogProc, dwInitParam); } BOOL EndDialog(int nRetCode) { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(m_bModal); // must be a modal dialog return ::EndDialog(m_hWnd, nRetCode); } // modeless dialogs HWND Create(HWND hWndParent, LPARAM dwInitParam = NULL) { ATLASSERT(m_hWnd == NULL); T* pT = static_cast(this); pT->ConstructDialogResource(); ATLASSERT((bool)m_dynamicDialogTemplate); _AtlWinModule.AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBaseT< TBase >*)this); #ifdef _DEBUG m_bModal = false; #endif //_DEBUG //HWND hWnd = ::CreateDialogParam(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCE(static_cast(this)->IDD), // hWndParent, T::StartDialogProc, dwInitParam); HWND hWnd = ::CreateDialogIndirectParam(_AtlBaseModule.GetResourceInstance(), m_dynamicDialogTemplate, hWndParent, T::StartDialogProc, dwInitParam); ATLASSERT(m_hWnd == hWnd); return hWnd; } // for CComControl HWND Create(HWND hWndParent, RECT&, LPARAM dwInitParam = NULL) { return Create(hWndParent, dwInitParam); } BOOL DestroyWindow() { ATLASSERT(::IsWindow(m_hWnd)); ATLASSERT(!m_bModal); // must not be a modal dialog return ::DestroyWindow(m_hWnd); } }; #endif // (_ATL_VER >= 0x0700) // CDynamicPropertyPageImpl inherits from CPropertyPageImpl, but it uses CDynamicDialogTemplate // to construct an in-memory dialog resource (instead of using a dialog template whose resource // identifier is aliased by "IDD" in the derived class). This in-memory dialog resource is created // either during a "Create" call, or when there's an implicit or explict cast to PROPSHEETPAGE* // (such as when "AddPage" is called on the sheet with the page as the argument). template class ATL_NO_VTABLE CDynamicPropertyPageImpl : public WTL::CPropertyPageImpl< T, TBase > { protected: typedef WTL::CPropertyPageImpl< T, TBase > baseClass; protected: bool m_dialogResourceInitialized; TDynamicDialogTemplate m_dynamicDialogTemplate; // Constructors public: CDynamicPropertyPageImpl(ATL::_U_STRINGorID title = (LPCTSTR)NULL) : baseClass(title), m_dialogResourceInitialized(false) { // We do this after the constructor but before // the property page is created. //T* pT = static_cast(this); //pT->ConstructDialogResource(); //m_psp.dwFlags |= PSP_DLGINDIRECT; //m_psp.pResource = m_dynamicDialogTemplate; } // Enumerations public: // Since we're going to provide the dialog template dynamically, // have IDD be 0. We're still going to inherit from CPropertyPageImpl // so this will be used in its constructor (but we'll change things // in InitializeDialogResource so "pResource" is used instead of "pszTemplate"). enum { IDD = 0 }; // CPropertyPageImpl Overrides: public: // This Create() isn't called by WTL at all, but just in case someone else // calls it, we need to override it to ensure the dialog resource is initialized. HPROPSHEETPAGE Create() { T* pT = static_cast(this); pT->InitializeDialogResource(); return baseClass::Create(); } public: // We'll do a post-constructor construction by overriding the // operator PROPSHEETPAGE*(), and calling "ConstructDialogResource" // which is overrideable. This should get called when you do // "AddPage", "InsertPage", . The reason we need ConstructDialogResource // to be called outside of the constructor is because we want it // to be overrideable, and have the version in the most derived class called. // In the case of a normal dialog, we can override DoModal and Create. // However, with a property page, those functions don't get called // (they do get called by the sheet though, so something might be added // there to call some kind of "FinalConstruct" like with COM). //operator PROPSHEETPAGE*() { return &m_psp; } operator PROPSHEETPAGE*() { T* pT = static_cast(this); pT->InitializeDialogResource(); return baseClass::operator PROPSHEETPAGE*(); } // Overrideables public: // You always need to override ConstructDialogResource //bool ConstructDialogResource(void) { } void InitializeDialogResource(void) { if(!m_dialogResourceInitialized) { m_dialogResourceInitialized = true; T* pT = static_cast(this); if(pT->ConstructDialogResource()) { m_psp.dwFlags |= PSP_DLGINDIRECT; m_psp.pResource = m_dynamicDialogTemplate; } } } }; }; // namespace DynamicDialog #endif //__DynamicDialogTemplate_h__