DOCUMENT:Q242994 18-JUN-2002 [visualc] TITLE :PRB:ATL IOleInPlaceSite::OnPosRectChange Doesn't Resize Control PRODUCT :Microsoft C Compiler PROD/VER::3.0,6.0 OPER/SYS: KEYWORDS:kbActiveX kbATLWC kbCOMt kbCtrlCreate KbUIDesign kbVC600 kbATL300 kbGrpDSMFCATL ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - The Microsoft Active Template Library (ATL) 3.0, included with: - Microsoft Visual C++, 32-bit Enterprise Edition, version 6.0 - Microsoft Visual C++, 32-bit Professional Edition, version 6.0 - Microsoft Visual C++, 32-bit Learning Edition, version 6.0 ------------------------------------------------------------------------------- SYMPTOMS ======== ActiveX controls hosted in an ATL container are unable to resize themselves dynamically. The container could be a composite control or any window using ATL containment as described in the following Knowledge Base article: Q192560 HOWTO: Adding ATL Control Containment Support to Any Window CAUSE ===== When an ActiveX control calls IOleInPlaceSite::OnPosRectChange, the container must call the control's IOleInPlaceObject::SetObjectRects to specify the new position of the in-place window and the clip rectangle. Only then does the object resize its window. The CAxHostWindow implementation of IOleInPlaceSite::OnPosRectChange currently returns E_NOTIMPL, which doesn't give the control the opportunity to resize. RESOLUTION ========== This can be worked around by modifying ATLHOST.h and making changes to the OnPosRectChange() function. The function should be changed such that it calls SetObjectRects() on it's containing control. A sample implementation is as below: STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect) { ATLTRACE2(atlTraceHosting, 0, _T("IOleInPlaceSite::OnPosRectChange")); // Use MoveWindow() to resize the CAxHostWindow. // The CAxHostWindow handler for OnSize() will // take care of calling IOleInPlaceObject::SetObjectRects(). // Convert to parent window coordinates for MoveWindow(). RECT rect = *lprcPosRect; ClientToScreen( &rect ); HWND hWnd = GetParent(); // Check to make sure it's a non-top-level window. if(hWnd != NULL) { CWindow wndParent(hWnd); wndParent.ScreenToClient(&rect); wndParent.Detach (); } // Do the actual move. MoveWindow( &rect); return S_OK; } Make these modifications to a copy of AtlHost.h, for instance, FixAtlHost.h. Then, in Stdafx.h, comment out AtlHost.h and use FixAtlHost.h instead: // #include #include "FixAtlHost.h" This technique will only work in Debug or ReleaseMinDependency builds. It will not work in ReleaseMinSize builds as ATL.dll would be used, not the code in FixAtlHost.h. MORE INFORMATION ================ For illustration, the following discussions make use of a modified version of POLYGON: The ATL Tutorial. The only modification made to the project is in file PolyCtl.h, in the function CPolyCtl::OnLButtonDown(): LRESULT CPolyCtl::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // a toggle value used to toggle control size. static int iToggleVal = 10; HRGN hRgn; WORD xPos = LOWORD(lParam); // horizontal position of cursor WORD yPos = HIWORD(lParam); // vertical position of cursor CalcPoints(m_rcPos); // Create a region from our list of points hRgn = CreatePolygonRgn(&m_arrPoint[0], m_nSides, WINDING); // If the clicked point is in our polygon then fire the ClickIn // event otherwise we fire the ClickOut event if (PtInRegion(hRgn, xPos, yPos)) Fire_ClickIn(xPos, yPos); else Fire_ClickOut(xPos, yPos); // Delete the region that we created DeleteObject(hRgn); // Toggle control size here. RECT newPos; newPos = m_rcPos; newPos.right -= iToggleVal; newPos.bottom -= iToggleVal; iToggleVal = -iToggleVal; HRESULT hRet; CComPtr spCtlSite; hRet = InternalGetSite(IID_IOleInPlaceSite, (void**)&spCtlSite); if (SUCCEEDED(hRet)) { if (spCtlSite != NULL) { // (Try) to change control size, // provided the container cooperates. hRet = spCtlSite->OnPosRectChange (&newPos); } } return 0; } As can be seen earlier, the Polygon control attempts to resize its window every time you click the left mouse button. Accordingly, on a left button click, the control does the following: - Calculates a new rectangle. - Calls IOleInPlaceSite::OnPosRectChange() with that new rectangle. - Resizes itself (by calling SetWindowPos() in CComControlBase::IOleInPlaceObject_SetObjectRects()) only when the container in turn calls the control's implementation of IOleInPlaceObject::SetObjectRects. The last bullet is crucial: Well behaved controls resize themselves only at the behest of the container. So, if the container doesn't cooperate, the control won't resize. Steps to Reproduce Behavior --------------------------- 1. Using the AppWizard, create a new ATL EXE project. 2. Using the Insert menu, select New ATL Object, Miscellaneous category, add a Dialog class. 3. Insert the modified Polygon control on the dialog box. 4. Create the dialog box in place of the boilerplate message loop in _tWinMain(): CResizeDlg dlg; dlg.DoModal (); /* Message loop commented out. MSG msg; while (GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg); */ 5. (Try to) toggle the size of the control by clicking inside it. 6. Notice that the size doesn't change. 7. Now make the changes required to FixAtlHost.h, and notice that this time around, the control resizes as expected. REFERENCES ========== For more information look at the following Web sites: - http://msdn.microsoft.com/library/psdk/com/oin_oe2k_1t9v.htm - http://msdn.microsoft.com/library/psdk/com/oin_oe2k_5g6d.htm (c) Microsoft Corporation 1999, All Rights Reserved. Contributions by S. Ganesh, Microsoft Corporation Additional query words: ====================================================================== Keywords : kbActiveX kbATLWC kbCOMt kbCtrlCreate KbUIDesign kbVC600 kbATL300 kbGrpDSMFCATL Technology : kbVCsearch kbAudDeveloper kbATLsearch Version : :3.0,6.0 Issue type : kbprb ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2002.