/*************************************************************************
    Project:    AXAccess
    Module:     PATCH.C

    Author:     Laura Butler
    Date:       05/01/96
    
    Notes:      16-bit DDI patching dll
      This DLL can be used by multiple applications.  It will post messages 
      with strings allocated out of the app's local heap to a window the
      app gave it.  When it is unhooked, it will post a final message to 
      the application, so the app can ssync itself and know when the output
      flow is over.

    Copyright (C) 1996 by Microsoft Corporation.  All rights reserved.
    See bottom of file for disclaimer
    
    History:     LauraBu Created.
*************************************************************************/

#include <windows.h>

typedef struct tagDRAWMODE FAR*     LPDRAWMODE;
typedef struct tagGDIINFO  FAR*     LPGDIINFO;

#include <winddi.h>
#include "patch.h"


//
// Structures
//
typedef struct tagPATCH
{
    HDDI    hddi;               // 0x00
    HWND    hwndPost;           // 0x04
    HGLOBAL hheapPost;          // 0x06
    DWORD   cCalls;             // 0x08
    DWORD   idThread;           // 0x0C
}
PATCH;                          // 0x10


//
// Globals
//
HINSTANCE   hInstance   = NULL;
char        szDdiString[1024];


//
// Functions
//
int     WINAPI      LibMain(HINSTANCE, UINT, UINT, LPSTR);
DWORD   WINAPI      DdiHookProc(HDDI, LONG, DDITYPE, LPDDIPARAMS);

extern  LPVOID NEAR PASCAL HeapAlloc(HGLOBAL, UINT);
extern  BOOL   NEAR PASCAL HeapFree(LPVOID);



// --------------------------------------------------------------------------
//
//  LibMain()
// 
// --------------------------------------------------------------------------
int WINAPI
LibMain(HINSTANCE hInst, UINT uDataSeg, UINT uHeapSize, LPSTR lpszCmdLine)
{
    hInstance = hInst;
    return(TRUE);
}



// --------------------------------------------------------------------------
//
//  PatchDdi()
//
//  Patches the DDI for the caller
//
// --------------------------------------------------------------------------
HPATCH WINAPI
PatchDdi(HWND hwnd, HGLOBAL hHeap, UINT uType)
{
    HPATCH  hpatch;

    //
    // Allocate a structure.
    //
    hpatch = (HPATCH)LocalAlloc(LPTR, sizeof(PATCH));
    if (!hpatch)
        return(NULL);

    //
    // Save info away
    //
    hpatch->hwndPost = hwnd;
    hpatch->hheapPost = hHeap;
    hpatch->idThread = (DWORD)GetWindowTask(hwnd);

    //
    // Set the ddi hook
    //
    hpatch->hddi = SetDDIHook(NULL, hInstance, MAKELONG(hpatch, 0), uType,
        DdiHookProc);

    //
    // Installation failed, clean up.
    //
    if (!hpatch->hddi)
    {
        LocalFree((HANDLE)hpatch);
        hpatch = NULL;
    }

    return(hpatch);
}



// --------------------------------------------------------------------------
//
//  UnpatchDDI()
//
//  Unpatches the DDI for the caller
//
// --------------------------------------------------------------------------
BOOL WINAPI
UnpatchDdi(HPATCH hpatch)
{
    if (!LocalHandle((HANDLE)hpatch))
        return(FALSE);

    //
    // Unhook the DDI
    //
    UnhookDDIHook(hpatch->hddi);

    //
    // Post a message to the caller.
    //
    PostThreadMessage32(hpatch->idThread, WM_ENDDDIPATCH, 0, hpatch->cCalls, 0);

    //
    // Free structure
    //
    LocalFree((HANDLE)hpatch);

    return(TRUE);
}



// --------------------------------------------------------------------------
//
//  DdiHookProc()
//
//  This makes a string of the DDI params, and posts it to the caller.
//
// --------------------------------------------------------------------------
DWORD WINAPI
DdiHookProc(HDDI hddi, LONG lPrivateData, DDITYPE ddiCode, LPDDIPARAMS lpddi)
{
    HPATCH  hpatch = (HPATCH)LOWORD(lPrivateData);

    if (!hpatch)
        DebugBreak();

    //
    // Use data variable to avoid chewing stack space; we are protected by
    // the win16lock.
    //
    *szDdiString = 0;

    switch (ddiCode)
    {
        case DDI_BITBLT:
            #define lpbitblt    ((LPBITBLT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "BitBlt:\tstart {%04d, %04d}, size {%04d, %04d}",
                lpbitblt->xDst, lpbitblt->yDst, lpbitblt->cxSrc, lpbitblt->cySrc);
            break;

        case DDI_COLORINFO:
            #define lpcolorinfo ((LPCOLORINFO_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "ColorInfo:\tfor color %08lx", lpcolorinfo->rgbColor);
            break;

        case DDI_CONTROL:
            #define lpcontrol ((LPCONTROL_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "Control:\tcode %04d", lpcontrol->uCode);
            break;

        case DDI_DISABLE:
            lstrcpy(szDdiString, "Disable");
            break;

        case DDI_ENABLE:
            #define lpenable ((LPENABLE_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "Enable:\tstyle %04d, device %s",
                lpenable->uStyle, lpenable->lpDeviceType);
            break;

        case DDI_ENUMDEVICEFONTS:
            #define lpenumdevfonts ((LPENUMDEVICEFONTS_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "EnumDeviceFonts:\tface name %s",
                lpenumdevfonts->lpszFaceName);
            break;

        case DDI_ENUMOBJECTS:
            #define lpenumobj ((LPENUMOBJECTS_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "EnumObjects:\ttype %04d", lpenumobj->uStyle);
            break;

        case DDI_OUTPUT:
            #define lpoutput ((LPOUTPUT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "Output:\tstyle %04d, point count %04d",
                lpoutput->uStyle, lpoutput->uPointCount);
            break;

        case DDI_PIXEL:
            #define lppixel ((LPPIXEL_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "Pixel:\tat {%04d, %04d}, color %08lx",
                lppixel->x, lppixel->y, lppixel->rgbColor);
            break;

        case DDI_REALIZEOBJECT:
            #define lprealizeobj ((LPREALIZEOBJECT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "RealizeObject:\ttype %04d", lprealizeobj->uObjectId);
            break;

        case DDI_STRINGBLT:
            #define lpstringblt ((LPSTRINGBLT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "StringBlt:\tstart {%04d, %04d}, char count %04d, string %s",
                lpstringblt->xStart, lpstringblt->yStart, lpstringblt->cchText,
                lpstringblt->lpszText);
            break;

        case DDI_SCANLEFTRIGHT:
            #define lpscanlr ((LPSCANLEFTRIGHT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "ScanLR:\tstart {%04d, %04d}, style %04d, color %08lx",
                lpscanlr->xStart, lpscanlr->yStart, lpscanlr->uStyle, lpscanlr->rgbSearch);
            break;

        case DDI_DEVICEMODE:
            #define lpdevmode ((LPDEVICEMODE_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "DeviceMode:\thwnd %04d, module %04d, device %s",
                lpdevmode->hwnd, lpdevmode->hmod, lpdevmode->lpDeviceName);
            break;

        case DDI_EXTTEXTOUT:
            #define lpextto ((LPEXTTEXTOUT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "ExtTextOut:\tstart {%04d, %04d}, flags %04x, char count %04d, string %s",
                lpextto->xStart, lpextto->yStart, lpextto->uOptions, lpextto->cchText,
                ((lpextto->cchText && lpextto->lpszText) ? lpextto->lpszText : ""));
            break;

        case DDI_GETCHARWIDTH:
            #define lpcharw ((LPGETCHARWIDTH_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "GetCharWidths:\tstart char %04d, end char %04d",
                lpcharw->ichFirst, lpcharw->ichLast);
            break;

        case DDI_DEVICEBITMAP:
            #define lpdevbit ((LPDEVICEBITMAP_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "DeviceBitmap:\tcommand %04d", lpdevbit->uCommand);
            break;

        case DDI_FASTBORDER:
            #define lpfastb ((LPFASTBORDER_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "FastBorder:\thorz thickness %04d, vert thickness %04d, rop %08lx",
                lpfastb->cxBorder, lpfastb->cyBorder, lpfastb->dwRop);
            break;

        case DDI_SETATTRIBUTE:
            #define lpsetattr ((LPSETATTRIBUTE_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "SetAttribute:\tattribute %04d, index %04d, value %08ld",
                lpsetattr->uStateNum, lpsetattr->uIndex, lpsetattr->dwAttribute);
            break;

        case DDI_DIBBLT:
            #define lpdiblt ((LPDIBBLT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "DibBits:\tget %04d, start scan %04d, scan count %04d",
                lpdiblt->fGet, lpdiblt->iScan, lpdiblt->cScans);
            break;

        case DDI_CREATEDIBITMAP:
            lstrcpy(szDdiString, "CreateDib");
            break;

        case DDI_DIBTODEVICE:
            #define lpdibto ((LPDIBTODEVICE_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "DibToDevice:\tstart pt {%04d, %04d}, start scan %04d, scan count %04d",
                lpdibto->xStart, lpdibto->yStart, lpdibto->iScan, lpdibto->cScans);
            break;

        case DDI_SETPALETTE:
        case DDI_GETPALETTE:
            #define lppal ((LPGETSETPALETTE_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "%s:\tstart entry %04d, entry count %04d",
                (ddiCode == DDI_SETPALETTE ? (LPSTR)"SetPalette" : (LPSTR)"GetPalette"),
                lppal->iEntryStart, lppal->cEntries);
            break;

        case DDI_SETPALETTETRANSLATE:
            lstrcpy(szDdiString, "SetPaletteTranslate");
            break;

        case DDI_GETPALETTETRANSLATE:
            lstrcpy(szDdiString, "GetPaletteTranslate");
            break;

        case DDI_UPDATECOLORS:
            #define lpupcol ((LPUPDATECOLORS_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "UpdateColors:\tstart pt {%04d, %04d}, size {%04d, %04d}",
                lpupcol->xStart, lpupcol->yStart, lpupcol->cxUpdate, lpupcol->cyUpdate);
            break;

        case DDI_STRETCHBLT:
            #define lpstretch ((LPSTRETCHBLT_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "StretchBlt:\tsrc pt {%04d, %04d}, src size {%04d, %04d}, dst pt {%04d, %04d}, dst size {%04d, %04d}, rop %08lx",
                lpstretch->xSrc, lpstretch->ySrc, lpstretch->cxSrc, lpstretch->cySrc,
                lpstretch->xDst, lpstretch->yDst, lpstretch->cxDst, lpstretch->cyDst,
                lpstretch->dwRop);
            break;

        case DDI_STRETCHDIBITS:
            #define lpsdib ((LPSTRETCHDIBITS_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "StretchDibBits:\tget %04d, src pt {%04d, %04d}, src size {%04d, %04d}, dst pt {%04d, %04d}, dst size {%04d, %04d}, rop %08lx",
                lpsdib->fGet, lpsdib->xSrc, lpsdib->ySrc, lpsdib->cxSrc, lpsdib->cySrc,
                lpsdib->xDst, lpsdib->yDst, lpsdib->cxDst, lpsdib->cyDst,
                lpsdib->dwRop);
            break;

        case DDI_SELECTBITMAP:
            #define lpselb ((LPSELECTBITMAP_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "SelectBitmap:\tflags %08lx", lpselb->dwFlags);
            break;

        case DDI_BITMAPBITS:
            #define lpbitb ((LPBITMAPBITS_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "BitmapBits:\tflags %08lx, count %08ld",
                lpbitb->dwFlags, lpbitb->dwByteCount);
            break;

        case DDI_REENABLE:
            lstrcpy(szDdiString, "ReEnable");
            break;

        case DDI_GAMMARAMP:
            #define lpgammar ((LPGAMMARAMP_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "GammaRamp:\tset %04d",  lpgammar->fSet);
            break;

        case DDI_ICMCOLORINFO:
            #define lpicmcol ((LPICMCOLORINFO_DDIPARAMS)lpddi)
            wsprintf(szDdiString, "ICMColorInfo:\tcolor %08lx", lpicmcol->rgbColor);
            break;

        case DDI_DRAWESCAPE:
            wsprintf(szDdiString, "DrawEscape");
            break;
    }

    if (*szDdiString)
    {
        LPSTR   lpszPostClient;
        UINT    cchString;

        cchString = lstrlen(szDdiString);
        lpszPostClient = (LPSTR)HeapAlloc(hpatch->hheapPost, cchString+1);
        if (lpszPostClient)
        {
            lstrcpy(lpszPostClient, szDdiString);

            //
            // If this fails (like because USER is out of memory), free the
            // pointer ourself.  The app will never get it.  Note that we
            // use PostThreadMessage32 for two good reasons instead of
            // PostMessage:
            //      (1) PostMessage uses a fixed interrupt-level queue from
            // which posted messages get flushed to app queues later, so that
            // VXDs et al. can use it.  It is very easy to fill it up, so
            // we use PostThreadMessage32() instead which skips a step.
            //      (2) Even if PostMessage() seems to succeed, it can fail
            // later when trying to disburse the message to the app queue, if
            // USER runs out of memory.  Hence we could still leak strings.
            // PostThreadMessage32() however tells us with certainty of 
            // success or failure.
            //
            if (! PostThreadMessage32(hpatch->idThread, WM_DDICALL, ddiCode, (LPARAM)lpszPostClient, 0))
                HeapFree(lpszPostClient);
        }
    }

    return(CallNextDDI(hddi, ddiCode, lpddi));

}

/*
    THE INFORMATION AND CODE PROVIDED HEREUNDER (COLLECTIVELY REFERRED TO
    AS "SOFTWARE") IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    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.
*/
