CTLVIEW.CPP
//=--------------------------------------------------------------------------= 
// CtlView.Cpp 
//=--------------------------------------------------------------------------= 
// Copyright 1995 - 1998 Microsoft Corporation.  All Rights Reserved. 
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF  
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO  
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A  
// PARTICULAR PURPOSE. 
//=--------------------------------------------------------------------------= 
// 
// implementation of the IViewObjectEx interface, which is a moderately 
// non-trivial bunch of code. 
// 
#include "IPServer.H" 
 
#include "CtrlObj.H" 
#include "Globals.H" 
#include "Util.H" 
 
// for ASSERT and FAIL 
// 
SZTHISFILE 
 
// local functions we're going to find useful 
// 
HDC _CreateOleDC(DVTARGETDEVICE *ptd); 
 
//=--------------------------------------------------------------------------= 
// COleControl::Draw    [IViewObject2] 
//=--------------------------------------------------------------------------= 
// Draws a representation of an object onto the specified device context.  
// 
// Parameters: 
//    DWORD                - [in] draw aspect 
//    LONG                 - [in] part of object to draw [not relevant] 
//    void *               - NULL 
//    DVTARGETDEVICE *     - [in] specifies the target device 
//    HDC                  - [in] information context for target device 
//    HDC                  - [in] target device context 
//    LPCRECTL             - [in] rectangle in which the object is drawn 
//    LPCRECTL             - [in] window extent and origin for metafiles 
//    BOOL (*)(DWORD)      - [in] callback for continuing or cancelling drawing 
//    DWORD                - [in] parameter to pass to callback. 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
//    - we support the following OCX 96 extensions 
//        a. flicker free drawing [multi-pass drawing] 
//        b. pvAspect != NULL for optimized DC handling 
//        c. prcBounds == NULL for windowless inplace active objects 
// 
STDMETHODIMP COleControl::Draw 
( 
    DWORD            dwDrawAspect, 
    LONG             lIndex, 
    void            *pvAspect, 
    DVTARGETDEVICE  *ptd, 
    HDC              hicTargetDevice, 
    HDC              hdcDraw, 
    LPCRECTL         prcBounds, 
    LPCRECTL         prcWBounds, 
    BOOL (__stdcall *pfnContinue)(DWORD dwContinue), 
    DWORD            dwContinue 
) 
{ 
    HRESULT hr; 
    RECTL rc; 
    POINT pVp, pW; 
    BOOL  fOptimize = FALSE; 
    int iMode; 
    BYTE fMetafile = FALSE; 
    BYTE fDeleteDC = FALSE; 
 
    // support the aspects required for multi-pass drawing 
    // 
    switch (dwDrawAspect) { 
        case DVASPECT_CONTENT: 
        case DVASPECT_OPAQUE: 
        case DVASPECT_TRANSPARENT: 
            break; 
        default: 
            return DV_E_DVASPECT; 
    } 
 
    // first, have to do a little bit to support printing. 
    // 
    if (GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE) { 
 
        // We are dealing with a metafile. 
        // 
        fMetafile = TRUE; 
 
        // If attributes DC is NULL, create one, based on ptd. 
        // 
        if (!hicTargetDevice) { 
 
            // Does _CreateOleDC have to return an hDC 
            // or can it be flagged to return an hIC  
            // for this particular case? 
            // 
            hicTargetDevice = _CreateOleDC(ptd); 
            fDeleteDC = TRUE; 
        } 
    } 
 
    // check to see if we have any flags passed in the pvAspect parameter. 
    // 
    if (pvAspect && ((DVASPECTINFO *)pvAspect)->cb == sizeof(DVASPECTINFO)) 
        fOptimize = (((DVASPECTINFO *)pvAspect)->dwFlags & DVASPECTINFOFLAG_CANOPTIMIZE) ? TRUE : FALSE; 
 
    // if we are windowless, then we just pass this on to the end control code. 
    // 
    if (m_fInPlaceActive) { 
 
        // give them a rectangle with which to draw 
        // 
        //ASSERT(!m_fInPlaceActive || !prcBounds, "Inplace active and somebody passed in prcBounds!!!"); 
        if (prcBounds) 
memcpy(&rc, prcBounds, sizeof(rc)); 
else 
memcpy(&rc, &m_rcLocation, sizeof(rc)); 
    } else { 
 
        // first -- convert the DC back to MM_TEXT mapping mode so that the 
        // window proc and OnDraw can share the same painting code.  save 
        // some information on it, so we can restore it later [without using 
        // a SaveDC/RestoreDC] 
        // 
        rc = *prcBounds; 
 
        // Don't do anything to hdcDraw if it's a metafile. 
        // The control's Draw method must make the appropriate 
        // accomodations for drawing to a metafile 
        // 
        if (!fMetafile) { 
            LPtoDP(hdcDraw, (POINT *)&rc, 2); 
            SetViewportOrgEx(hdcDraw, 0, 0, &pVp); 
            SetWindowOrgEx(hdcDraw, 0, 0, &pW); 
            iMode = SetMapMode(hdcDraw, MM_TEXT); 
        } 
    } 
 
    // prcWBounds is NULL and not used if we are not dealing with a metafile. 
    // For metafiles, we pass on rc as *prcBounds, we should also include 
    // prcWBounds 
    // 
    hr = OnDraw(dwDrawAspect, hdcDraw, &rc, prcWBounds, hicTargetDevice, fOptimize); 
 
    // clean up the DC when we're done with it, if appropriate. 
    // 
    if (!m_fInPlaceActive) { 
        SetViewportOrgEx(hdcDraw, pVp.x, pVp.y, NULL); 
        SetWindowOrgEx(hdcDraw, pW.x, pW.y, NULL); 
        SetMapMode(hdcDraw, iMode); 
    } 
 
    // if we created a dc, blow it away now 
    // 
    if (fDeleteDC) DeleteDC(hicTargetDevice); 
    return hr; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::DoSuperClassPaint 
//=--------------------------------------------------------------------------= 
// design time painting of a subclassed control. 
// 
// Parameters: 
//    HDC                - [in]  dc to work with 
//    LPCRECTL           - [in]  rectangle to paint to.  should be in pixels 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
HRESULT COleControl::DoSuperClassPaint 
( 
    HDC      hdc, 
    LPCRECTL prcBounds 
) 
{ 
    HWND hwnd; 
    RECT rcClient; 
    int  iMapMode; 
    POINT ptWOrg, ptVOrg; 
    SIZE  sWOrg, sVOrg; 
 
    // make sure we have a window. 
    // 
    hwnd = CreateInPlaceWindow(0,0, FALSE); 
    if (!hwnd) 
        return E_FAIL; 
 
    GetClientRect(hwnd, &rcClient); 
 
    // set up the DC for painting.  this code largely taken from the MFC CDK 
    // DoSuperClassPaint() fn.  doesn't always get things like command 
    // buttons quite right ... 
    // 
    // NOTE: there is a windows 95 problem in which the font instance manager 
    // will leak a bunch of bytes in the global GDI pool whenever you  
    // change your extents and have an active font.  this code gets around 
    // this for on-screen cases, but not for printing [which shouldn't be 
    // too serious, because you're not often changing your control size and 
    // printing rapidly in succession] 
    // 
    if ((rcClient.right - rcClient.left != prcBounds->right - prcBounds->left) 
        && (rcClient.bottom - rcClient.top != prcBounds->bottom - prcBounds->top)) { 
 
        iMapMode = SetMapMode(hdc, MM_ANISOTROPIC); 
        SetWindowExtEx(hdc, rcClient.right, rcClient.bottom, &sWOrg); 
        SetViewportExtEx(hdc, prcBounds->right - prcBounds->left, prcBounds->bottom - prcBounds->top, &sVOrg); 
    } 
 
    SetWindowOrgEx(hdc, 0, 0, &ptWOrg); 
    SetViewportOrgEx(hdc, prcBounds->left, prcBounds->top, &ptVOrg); 
 
#if STRICT 
    CallWindowProc((WNDPROC)SUBCLASSWNDPROCOFCONTROL(m_ObjectType), hwnd, (g_fSysWin95Shell) ? WM_PRINT : WM_PAINT, (WPARAM)hdc, (LPARAM)(g_fSysWin95Shell ? PRF_CHILDREN | PRF_CLIENT : 0)); 
#else 
    CallWindowProc((FARPROC)SUBCLASSWNDPROCOFCONTROL(m_ObjectType), hwnd, (g_fSysWin95Shell) ? WM_PRINT : WM_PAINT, (WPARAM)hdc, (LPARAM)(g_fSysWin95Shell ? PRF_CHILDREN | PRF_CLIENT : 0)); 
#endif // STRICT 
 
    return S_OK; 
} 
 
 
//=--------------------------------------------------------------------------= 
// COleControl::GetColorSet    [IViewObject2] 
//=--------------------------------------------------------------------------= 
// Returns the logical palette that the control will use for drawing in its 
// IViewObject::Draw method with the corresponding parameters. 
// 
// Parameters: 
//    DWORD                - [in]  how the object is to be represented 
//    LONG                 - [in]  part of the object to draw [not relevant] 
//    void *               - NULL 
//    DVTARGETDEVICE *     - [in]  specifies the target device 
//    HDC                  - [in]  information context for the target device 
//    LOGPALETTE **        - [out] where to put palette 
// 
// Output: 
//    S_OK                 - Control has a palette, and returned it through the out param. 
//    S_FALSE              - Control does not currently have a palette. 
//    E_NOTIMPL            - Control will never have a palette so optimize handling of this control. 
// 
// Notes: 
// 
STDMETHODIMP COleControl::GetColorSet 
( 
    DWORD            dwDrawAspect, 
    LONG             lindex, 
    void            *IgnoreMe, 
    DVTARGETDEVICE  *ptd, 
    HDC              hicTargetDevice, 
    LOGPALETTE     **ppColorSet 
) 
{ 
    if (dwDrawAspect != DVASPECT_CONTENT) 
        return DV_E_DVASPECT; 
 
    *ppColorSet = NULL; 
    return (OnGetPalette(hicTargetDevice, ppColorSet)) ? ((*ppColorSet) ? S_OK : S_FALSE) : E_NOTIMPL; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::Freeze    [IViewObject2] 
//=--------------------------------------------------------------------------= 
// Freezes a certain aspect of the object's presentation so that it does not 
// change until the IViewObject::Unfreeze method is called. 
// 
// Parameters: 
//    DWORD            - [in] aspect 
//    LONG             - [in] part of object to draw 
//    void *           - NULL 
//    DWORD *          - [out] for Unfreeze 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::Freeze 
( 
    DWORD   dwDrawAspect, 
    LONG    lIndex, 
    void   *IgnoreMe, 
    DWORD  *pdwFreeze 
) 
{ 
    return E_NOTIMPL; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::Unfreeze    [IVewObject2] 
//=--------------------------------------------------------------------------= 
// Releases a previously frozen drawing. The most common use of this method 
// is for banded printing. 
// 
// Parameters: 
//    DWORD        - [in] cookie from freeze 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::Unfreeze 
( 
    DWORD dwFreeze 
) 
{ 
    return E_NOTIMPL; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::SetAdvise    [IViewObject2] 
//=--------------------------------------------------------------------------= 
// Sets up a connection between the control and an advise sink so that the 
// advise sink can be notified about changes in the control's view. 
// 
// Parameters: 
//    DWORD            - [in] aspect 
//    DWORD            - [in] info about the sink 
//    IAdviseSink *    - [in] the sink 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::SetAdvise 
( 
    DWORD        dwAspects, 
    DWORD        dwAdviseFlags, 
    IAdviseSink *pAdviseSink 
) 
{ 
    // if it's not a content aspect, we don't support it. 
    // 
    if (!(dwAspects & DVASPECT_CONTENT)) { 
        return DV_E_DVASPECT; 
    } 
 
    // set up some flags  [we gotta stash for GetAdvise ...] 
    // 
    m_fViewAdvisePrimeFirst = (dwAdviseFlags & ADVF_PRIMEFIRST) ? TRUE : FALSE; 
    m_fViewAdviseOnlyOnce = (dwAdviseFlags & ADVF_ONLYONCE) ? TRUE : FALSE; 
 
    RELEASE_OBJECT(m_pViewAdviseSink); 
    m_pViewAdviseSink = pAdviseSink; 
    ADDREF_OBJECT(m_pViewAdviseSink); 
 
    // prime them if they want it [we need to store this so they can get flags later] 
    // 
    if (m_fViewAdvisePrimeFirst) 
        ViewChanged(); 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::GetAdvise    [IViewObject2] 
//=--------------------------------------------------------------------------= 
// Retrieves the existing advisory connection on the control if there is one. 
// This method simply returns the parameters used in the most recent call to 
// the IViewObject::SetAdvise method. 
// 
// Parameters: 
//    DWORD *            - [out]  aspects 
//    DWORD *            - [out]  advise flags 
//    IAdviseSink **     - [out]  the sink 
// 
// Output: 
//    HRESULT 
// 
// Notes; 
// 
STDMETHODIMP COleControl::GetAdvise 
( 
    DWORD        *pdwAspects, 
    DWORD        *pdwAdviseFlags, 
    IAdviseSink **ppAdviseSink 
) 
{ 
    // if they want it, give it to them 
    // 
    if (pdwAspects) 
        *pdwAspects = DVASPECT_CONTENT; 
 
    if (pdwAdviseFlags) { 
        *pdwAdviseFlags = 0; 
        if (m_fViewAdviseOnlyOnce) *pdwAdviseFlags |= ADVF_ONLYONCE; 
        if (m_fViewAdvisePrimeFirst) *pdwAdviseFlags |= ADVF_PRIMEFIRST; 
    } 
 
    if (ppAdviseSink) { 
        *ppAdviseSink = m_pViewAdviseSink; 
        ADDREF_OBJECT(*ppAdviseSink); 
    } 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::GetExtent    [IViewObject2] 
//=--------------------------------------------------------------------------= 
// Returns the size that the control will be drawn on the 
// specified target device. 
// 
// Parameters: 
//    DWORD            - [in] draw aspect 
//    LONG             - [in] part of object to draw 
//    DVTARGETDEVICE * - [in] information about target device 
//    LPSIZEL          - [out] where to put the size 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::GetExtent 
( 
    DWORD           dwDrawAspect, 
    LONG            lindex, 
    DVTARGETDEVICE *ptd, 
    LPSIZEL         psizel 
) 
{ 
    // we already have an implementation of this [from IOleObject] 
    // 
    return GetExtent(dwDrawAspect, psizel); 
} 
 
 
//=--------------------------------------------------------------------------= 
// COleControl::OnGetPalette    [overridable] 
//=--------------------------------------------------------------------------= 
// called when the host wants palette information.  ideally, people should use 
// this sparingly and carefully. 
// 
// Parameters: 
//    HDC            - [in]  HIC for the target device 
//    LOGPALETTE **  - [out] where to put the palette 
// 
// Output: 
//    BOOL           - TRUE means we processed it, false means nope. 
// 
// Notes: 
// 
BOOL COleControl::OnGetPalette 
( 
    HDC          hicTargetDevice, 
    LOGPALETTE **ppColorSet 
) 
{ 
    return FALSE; 
} 
 
 
//=--------------------------------------------------------------------------= 
// COleControl::GetRect    [IViewObjectEx] 
//=--------------------------------------------------------------------------= 
// returns a rectnagle describing a given drawing aspect 
// 
// Parameters: 
//    DWORD             - [in]  aspect 
//    LPRECTL           - [out] region rectangle 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::GetRect 
( 
    DWORD    dvAspect, 
    LPRECTL  prcRect 
) 
{ 
    RECTL rc; 
    BOOL  f; 
 
    // call the user routine and let them return the size 
    // 
    f = OnGetRect(dvAspect, &rc); 
    if (!f) return DV_E_DVASPECT; 
 
    // transform these dudes. 
    // 
    PixelToHiMetric((LPSIZEL)&rc, (LPSIZEL)prcRect); 
    PixelToHiMetric((LPSIZEL)(LPBYTE)&rc + sizeof(SIZEL), (LPSIZEL)((LPBYTE)prcRect + sizeof(SIZEL))); 
 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::GetViewStatus    [IViewObjectEx] 
//=--------------------------------------------------------------------------= 
// returns information about the opactiy of the object and what drawing 
// aspects are supported 
// 
// Parameters: 
//    DWORD *            - [out] the status 
// 
/// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::GetViewStatus 
( 
    DWORD *pdwStatus 
) 
{ 
    // depending on the flag in the CONTROLOBJECTINFO structure, indicate our 
    // transparency vs opacity. 
    // OVERRIDE:  controls that wish to support multi-pass drawing should 
    // override this routine and return, in addition to the flags indication 
    // opacity, flags indicating what sort of drawing aspects they support. 
    // 
    *pdwStatus = FCONTROLISOPAQUE(m_ObjectType) ? VIEWSTATUS_OPAQUE : 0; 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::QueryHitPoint    [IViewObjectEx] 
//=--------------------------------------------------------------------------= 
// indicates whether a point is within a given aspect of an object. 
// 
// Parameters: 
//    DWORD                - [in]  aspect 
//    LPCRECT              - [in]  Bounds rectangle 
//    POINT                - [in]  hit location client coordinates 
//    LONG                 - [in]  what the container considers close 
//    DWORD *              - [out] info about the hit 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::QueryHitPoint 
( 
    DWORD    dvAspect, 
    LPCRECT  prcBounds, 
    POINT    ptLocation, 
    LONG     lCloseHint, 
    DWORD   *pdwHitResult 
) 
{ 
    // OVERRIDE: override me if you want to provide additional [non-opaque] 
    // functionality 
    // 
    if (dvAspect != DVASPECT_CONTENT) 
        return DV_E_DVASPECT; 
 
    *pdwHitResult = PtInRect(prcBounds, ptLocation) ? HITRESULT_HIT : HITRESULT_OUTSIDE; 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::QueryHitRect    [IViewObjectEx] 
//=--------------------------------------------------------------------------= 
// indicates wheter any point in a rectangle is within a given drawing aspect 
// of an object. 
// 
// Parameters: 
//    DWORD            - [in]  aspect 
//    LPCRECT          - [in]  bounds 
//    LPCRECT          - [in]  location 
//    LONG             - [in]  what host considers close 
//    DWORD *          - [out] hit result 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::QueryHitRect 
( 
    DWORD     dvAspect, 
    LPCRECT   prcBounds, 
    LPCRECT   prcLocation, 
    LONG      lCloseHint, 
    DWORD    *pdwHitResult 
) 
{ 
    RECT rc; 
 
    // OVERRIDE: override this for additional behaviour 
    // 
    if (dvAspect != DVASPECT_CONTENT) 
        return DV_E_DVASPECT; 
 
    *pdwHitResult = IntersectRect(&rc, prcBounds, prcLocation) ? HITRESULT_HIT : HITRESULT_OUTSIDE; 
    return S_OK; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::GetNaturalExtent    [IViewObjectEx] 
//=--------------------------------------------------------------------------= 
// supports two types of control sizing, content and integral. 
// 
// Parameters: 
//    DWORD            - [in]  aspect 
//    LONG             - [in]  index 
//    DVTARGETDEVICE * - [in]  target device information 
//    HDC              - [in]  HIC 
//    DVEXTENTINFO *   - [in]  sizing data 
//    LPSIZEL          - [out] sizing data retunred by control 
// 
// Output: 
//    HRESULT 
// 
// Notes: 
// 
STDMETHODIMP COleControl::GetNaturalExtent 
( 
    DWORD           dvAspect, 
    LONG            lIndex, 
    DVTARGETDEVICE *ptd, 
    HDC             hicTargetDevice, 
    DVEXTENTINFO   *pExtentInfo, 
    LPSIZEL         pSizel 
) 
{ 
    return E_NOTIMPL; 
} 
 
//=--------------------------------------------------------------------------= 
// COleControl::OnGetRect    [overridable 
//=--------------------------------------------------------------------------= 
// returns our rectangle 
// 
// Parameters: 
//    DWORD              - [in]  aspect they want the rect for 
//    RECTL *            - [out] the rectangle that matches this aspect 
// 
// Output: 
//    BOOL               - false means we don't like the aspect 
// 
// Notes: 
// 
BOOL COleControl::OnGetRect 
( 
    DWORD   dvAspect, 
    RECTL  *pRect 
) 
{ 
    // by default, we only support content drawing. 
    // 
    if (dvAspect != DVASPECT_CONTENT) 
        return FALSE; 
 
    // just give them our bounding rectangle 
    // 
    *((LPRECT)pRect) = m_rcLocation; 
    return TRUE; 
} 
 
//=--------------------------------------------------------------------------= 
// _CreateOleDC 
//=--------------------------------------------------------------------------= 
// creates an HDC given a DVTARGETDEVICE structure. 
// 
// Parameters: 
//    DVTARGETDEVICE *              - [in] duh. 
// 
// Output: 
//    HDC 
// 
// Notes: 
// 
HDC _CreateOleDC 
( 
    DVTARGETDEVICE *ptd 
) 
{ 
    LPDEVMODEW   pDevModeW; 
    DEVMODEA     DevModeA, *pDevModeA; 
    LPOLESTR     lpwszDriverName; 
    LPOLESTR     lpwszDeviceName; 
    LPOLESTR     lpwszPortName; 
    HDC          hdc; 
 
    // return screen DC for NULL target device 
    // 
    if (!ptd) 
        return CreateDC("DISPLAY", NULL, NULL, NULL); 
 
    if (ptd->tdExtDevmodeOffset == 0) 
        pDevModeW = NULL; 
    else 
        pDevModeW = (LPDEVMODEW)((LPSTR)ptd + ptd->tdExtDevmodeOffset); 
 
    lpwszDriverName = (LPOLESTR)((BYTE*)ptd + ptd->tdDriverNameOffset); 
    lpwszDeviceName = (LPOLESTR)((BYTE*)ptd + ptd->tdDeviceNameOffset); 
    lpwszPortName   = (LPOLESTR)((BYTE*)ptd + ptd->tdPortNameOffset); 
 
    MAKE_ANSIPTR_FROMWIDE(pszDriverName, lpwszDriverName); 
    MAKE_ANSIPTR_FROMWIDE(pszDeviceName, lpwszDeviceName); 
    MAKE_ANSIPTR_FROMWIDE(pszPortName,   lpwszPortName); 
 
    // wow, this sucks. 
    // 
    if (pDevModeW) { 
        WideCharToMultiByte(CP_ACP, 0, pDevModeW->dmDeviceName, -1, (LPSTR)DevModeA.dmDeviceName, CCHDEVICENAME, NULL, NULL); 
memcpy(&DevModeA.dmSpecVersion, &pDevModeW->dmSpecVersion, 
offsetof(DEVMODEA, dmFormName) - offsetof(DEVMODEA, dmSpecVersion)); 
        WideCharToMultiByte(CP_ACP, 0, pDevModeW->dmFormName, -1, (LPSTR)DevModeA.dmFormName, CCHFORMNAME, NULL, NULL); 
memcpy(&DevModeA.dmLogPixels, &pDevModeW->dmLogPixels, sizeof(DEVMODEA) - offsetof(DEVMODEA, dmLogPixels)); 
        if (pDevModeW->dmDriverExtra) { 
            pDevModeA = (DEVMODEA *)HeapAlloc(g_hHeap, 0, sizeof(DEVMODEA) + pDevModeW->dmDriverExtra); 
            if (!pDevModeA) return NULL; 
            memcpy(pDevModeA, &DevModeA, sizeof(DEVMODEA)); 
            memcpy(pDevModeA + 1, pDevModeW + 1, pDevModeW->dmDriverExtra); 
        } else 
            pDevModeA = &DevModeA; 
 
DevModeA.dmSize = sizeof(DEVMODEA); 
    } else 
        pDevModeA = NULL; 
 
    hdc = CreateDC(pszDriverName, pszDeviceName, pszPortName, pDevModeA); 
    if (pDevModeA != &DevModeA) HeapFree(g_hHeap, 0, pDevModeA); 
    return hdc; 
}