GetDesktopWindow from non-interactive service...

From: Robb Shearer (robert.shearer_at_citicorp.com)
Date: 09/13/03


Date: Fri, 12 Sep 2003 15:23:33 -0700


We have a service that is monitoring production desktop.
It can determine when something bad has happend on the
production desktop, and generate an e-mail notification to
a 'OnCall' e-mail box.

I have cobbled together some examples from MSDN and other
places, and added the ability for the service to take a
snapshot of the desktop, and include it as an attachment.

This works GREAT on my workstation -- running as a
service. But on a test machine (and in production) it will
receive an ACCESS DENIED error with the BitBlt.

Here is the code I am using to do the snapshot -- minus
all the AddTheAceWindowStation -- which are from MSDN.

I have tried it w/ and w/o the #define
TRY_USING_MS_ADDACE_EXAMPLE. Which AddTheAceWindowStation
fails.

Any idea / help would be GREATLY appreciated.

Thank you.

PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp)
{
    BITMAP bmp;
    PBITMAPINFO pbmi;
    WORD cClrBits;

    // Retrieve the bitmap color format, width, and
height.
    if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
                return NULL;

    // Convert the color format to a count of bits.
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
    if (cClrBits == 1)
        cClrBits = 1;
    else if (cClrBits <= 4)
        cClrBits = 4;
    else if (cClrBits <= 8)
        cClrBits = 8;
    else if (cClrBits <= 16)
        cClrBits = 16;
    else if (cClrBits <= 24)
        cClrBits = 24;
    else cClrBits = 32;

    // Allocate memory for the BITMAPINFO structure. (This
structure
    // contains a BITMAPINFOHEADER structure and an array
of RGBQUAD
    // data structures.)

     if (cClrBits != 24)
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
                    sizeof(BITMAPINFOHEADER) +
                    sizeof(RGBQUAD) * (1<< cClrBits));

     // There is no RGBQUAD array for the 24-bit-per-pixel
format.

     else
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
                    sizeof(BITMAPINFOHEADER));

    // Initialize the fields in the BITMAPINFO structure.

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = bmp.bmWidth;
    pbmi->bmiHeader.biHeight = bmp.bmHeight;
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    if (cClrBits < 24)
        pbmi->bmiHeader.biClrUsed = (1<<cClrBits);

    // If the bitmap is not compressed, set the BI_RGB
flag.
    pbmi->bmiHeader.biCompression = BI_RGB;

    // Compute the number of bytes in the array of color
    // indices and store the result in biSizeImage.
    // For Windows NT, the width must be DWORD aligned
unless
    // the bitmap is RLE compressed. This example shows
this.
    // For Windows 95/98/Me, the width must be WORD
aligned unless the
    // bitmap is RLE compressed.
    pbmi->bmiHeader.biSizeImage = ((pbmi-
>bmiHeader.biWidth * cClrBits +31) & ~31) /8
                                  * pbmi-
>bmiHeader.biHeight;
    // Set biClrImportant to 0, indicating that all of the
    // device colors are important.
     pbmi->bmiHeader.biClrImportant = 0;
     return pbmi;
}

HBITMAP CreateBitmapOfDesktop(HWND desktopWindow)
{
        HWND hwnd = desktopWindow;
        HDC hScreenDC = GetDC(hwnd);
        int ScreenWidth = GetDeviceCaps(hScreenDC,
HORZRES);
        int ScreenHeight = GetDeviceCaps(hScreenDC,
VERTRES);

        HBITMAP bitmap = CreateCompatibleBitmap(hScreenDC,
ScreenWidth, ScreenHeight);
        HDC bitmapDC = CreateCompatibleDC(hScreenDC);
        HBITMAP oldBitmap = (HBITMAP)SelectObject
(bitmapDC, bitmap);

        if ( BitBlt(bitmapDC, 0, 0, ScreenWidth,
ScreenHeight, hScreenDC, 0, 0, SRCCOPY | CAPTUREBLT) == 0 )
                return NULL;

        SelectObject(bitmapDC, oldBitmap);
        DeleteObject(bitmapDC);
        ReleaseDC(hwnd, hScreenDC);
        return bitmap;
}

 
void SaveBitmapToFile(LPTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HWND desktopWindow)
{
        HANDLE hf; // file handle
        BITMAPFILEHEADER hdr; // bitmap file-header
        PBITMAPINFOHEADER pbih; // bitmap info-header
        LPBYTE lpBits; // memory pointer
        DWORD dwTotal; // total count of
bytes
        DWORD cb; // incremental count
of bytes
        BYTE *hp; // byte pointer
        DWORD dwTmp;

    pbih = (PBITMAPINFOHEADER) pbi;
    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih-
>biSizeImage);

    if (!lpBits)
                return;

        HWND hwnd = desktopWindow;
        HDC hScreenDC = GetDC(hwnd);

    // Retrieve the color table (RGBQUAD array) and the
bits
    // (array of palette indices) from the DIB.
    if (!GetDIBits(hScreenDC, hBMP, 0, (WORD) pbih-
>biHeight, lpBits, pbi, DIB_RGB_COLORS))
        {
                ReleaseDC(hwnd, hScreenDC);
                return;
        }
        ReleaseDC(hwnd, hScreenDC);

    // Create the .BMP file.
    hf = CreateFile(pszFile,
                   GENERIC_READ | GENERIC_WRITE,
                   (DWORD) 0,
                    NULL,
                   CREATE_ALWAYS,
                   FILE_ATTRIBUTE_NORMAL,
                   (HANDLE) NULL);
    if (hf == INVALID_HANDLE_VALUE)
                return;

    hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
    // Compute the size of the entire file.
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih-
>biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih-
>biSizeImage);
    hdr.bfReserved1 = 0;
    hdr.bfReserved2 = 0;

    // Compute the offset to the array of color indices.
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);

    // Copy the BITMAPFILEHEADER into the .BMP file.
    if (!WriteFile(hf, (LPVOID) &hdr, sizeof
(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL))
                return;

    // Copy the BITMAPINFOHEADER and RGBQUAD array into
the file.
    if (!WriteFile(hf, (LPVOID) pbih, sizeof
(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)) )
                return;

    // Copy the array of color indices into the .BMP file.
    dwTotal = cb = pbih->biSizeImage;
    hp = lpBits;
    if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD)
&dwTmp,NULL))
                return;

    // Close the .BMP file.
     if (!CloseHandle(hf))
                 return;

    // Free memory.
    GlobalFree((HGLOBAL)lpBits);
}

BOOL CALLBACK FindDesktopWindow(HWND hwnd, LPARAM p)
{
        char winName[256];
        char className[256];

        className[0] = 0;
        GetClassName(hwnd, className, 255);
        if ( stricmp(className, "#32769") == 0 )
        {
                DWORD* desktopWindow = (DWORD*)p;
                *desktopWindow = (long)hwnd;
                return FALSE;
        }
        HWND parent= hwnd;
        do {
                className[0] = 0;
                parent = GetParent(parent);
                GetClassName(parent, className, 255);
                if ( stricmp(className, "#32769") == 0 )
                {
                        DWORD* desktopWindow = (DWORD*)p;
                        *desktopWindow = (long)parent;
                        return FALSE;
                }
        } while ( parent != NULL );
        return TRUE;
}

// called with "winsta0, default"
STDMETHODIMP GrabDesktopSnapshot(char* UserWinStaName,
char* UserDesktopName)
{
        HDESK hdesk;
        HDESK hdeskold;
        HWINSTA hwinsta;

        hwinsta = OpenWindowStation(UserWinStaName, FALSE,
READ_CONTROL);
        if (hwinsta == NULL)
                return E_FAIL;
 
        HWINSTA hwinstaold = GetProcessWindowStation();
        if (hwinstaold == NULL)
                return E_FAIL;
  
        hdeskold = GetThreadDesktop(GetCurrentThreadId());

        //
        // set the windowstation to winsta0 so that you
obtain the
        // correct default desktop
        //

        if (!SetProcessWindowStation(hwinsta))
                return E_FAIL;

        //
        // obtain a handle to the user desktop
        //
        hdesk = OpenDesktop(UserDesktopName, 0, FALSE,
READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS |
DESKTOP_READOBJECTS );
        if (hdesk == NULL)
                return E_FAIL;

        SetThreadDesktop(hdesk);

#ifdef TRY_USING_MS_ADDACE_EXAMPLE

        HANDLE hToken;
        PSID psid;

        if (!LogonUser("Foo", "DomainLab", "Bar",
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
&hToken))
                return E_FAIL;

                // example function form MSDN
        if (!GetLogonSID(hToken, &psid))
                return E_FAIL;

        //
        // add the user to interactive windowstation ---
sample function from MSDN
        //
        if (!AddTheAceWindowStation(hwinsta, psid))
                return E_FAIL;

        //
        // add user to "default" desktop --
sample function from MSDN
        //
        if (!AddTheAceDesktop(hdesk, psid))
                return E_FAIL;

        //
        // free the buffer for the logon sid
        //
        RemoveSid(&psid);
#endif

        // Don't want to use GetDesktopWindow().. This
might eventually be used
        // in a TerminalServer environment.
        //
        // Or -- is that a totally mute point? --- Will
GetDesktopWindow() work if the
        // SetProcessWindowStation() and SetThreadDesktop
() are set correctly?
        //
        HWND DesktopWindow = 0;
        EnumDesktopWindows(hdesk, FindDesktopWindow, (long)
&DesktopWindow);
        if ( DesktopWindow == 0 )
                return E_FAIL;

        HBITMAP bitmap = CreateBitmapOfDesktop
(DesktopWindow);
        if ( bitmap == NULL )
                return E_FAIL;

        PBITMAPINFO bitmapInfo = CreateBitmapInfoStruct
(bitmap);

        char filename[256];
        SYSTEMTIME st;
        GetSystemTime(&st);
        SystemTimeToTzSpecificLocalTime(NULL, &st, &st);

        sprintf(filename, "c:\\temp\\Snapshot_%04.04d%
02.02d%02.02d_%02.02d%02.02d%02.02d.bmp", st.wYear,
st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

        SaveBitmapToFile(filename, bitmapInfo, bitmap,
DesktopWindow);

        DeleteObject(bitmap);

        SetThreadDesktop(hdeskold);
           if (!SetProcessWindowStation(hwinstaold))
           return E_FAIL;

        // we will continue even if this fails
        int closeRes = CloseWindowStation(hwinsta);
           
        if (!CloseDesktop(hdesk))
                return E_FAIL;
        CloseHandle(hToken);
        return S_OK;
}
 


Quantcast