Link to home
Start Free TrialLog in
Avatar of dessi
dessi

asked on

Reading a covered/minmized window?

I am currently reading the pixels of a window that is generated by a 3rd party application.  I am trying to access the same screen information when the window is covered or minimized.

---------EXPLANATION------


I have several exe's which run on my desktop and display various stock/commodity market conditions.  They communicate with their vendors by internet using heavy encryption.

My task is to build an application which could read the conditions and recommendations from various vendors' apps, and give notice when certain market conditions exist.

I generate mouseclicks to navigate to different screens, and I read the information which is shown via controls.   However,  other information is displayed as bitmaps on the screen.  (BUY , SELL, HOLD, ETC). The way I have been reading them so far, is to check the pixels (GETPIXEL)in a few locations and by color determine which bitmap is being displayed.( and therefore what the recommendation is)

 The limitation of this technique, is that the all of the screens need to be visible in order to check the pixels.  

Ideally I would like to have the screens minimized (or at least covered) so that other work can be done, and only when the user needs to make a decision, does the app display itself.

I had tried to use PRINTCLIENT (though not exactly how nietod outlined which I am trying now)  However, if the vendor chose simply not to implement PRINTCLIENT will this work?  Also I read somewhere that PRINTCLIENT does not work if the window is generated by another process.  

Thanks in advance for your help guys.
Avatar of nietod
nietod

It isn't.

There is no "data buffer"

when a window needs to be drawn, the OS calculates the area that is visible (which may be non-rectanguler) then it sends a WM_PAINT message to the program using this update region.  The program then draws in this update region.    The areas of the window that are covered are never drawn.   There is no "record" of what "appears" int he covvered parts of the window.


However, you can try sending a WM_PRINT or WM_PRINTCLIENT message to the windopw    A well-behaved program will draw its entire contents to the specified DC in this case.  (there is no guarantee that it will though, try it and see.)
Note to send a WM_PRINT or WM_PRINTCLIENT message for this purposes, you would probably do the following.

1.  Create a bitmap that is at least as large as the window (or its client area)   Use CreateCompatibleBitmap with the display.

2.  Create a memory DC for the bitmap.  (use CreateCompatibleDC with the display.)

3.  Select the bitmap into the DC (and save the returned bitmal--important).

4.  Send the message to the target window specifying the memory DC in the parameters.

5.  the program will draw to the DC, savign the image in your bitmap.

6.  Select the saved bitmap (from3 step 3) into the memory DC

7.  delete the memory DC.

8.  Eventually delete the memory bitmap.

There is a lot of steps involved in this.  (those 8 are just the highlights)  I can elaborate on this if you have questions, but you should at least look into this stuff first, if you are not familiar with it.  
Avatar of DanRollins
It might turn out to be easiest to force the window open, grab its image, then put back the way it was.

Why (exactly, specifically) do you need this window image?  Perhaps there is another way to attack the problem.

-- Dan
I agree.  (not with trying to force it open that is likely to cause headaches--the user is goign to see it, the user mightaccidentally interact with it, etc etc)   However, trying to do this is probably a bad idea.

Usually when someone wants to do this sort of thing, they are trying to "build" an application using some other program to do most of the work.   The results--if they ever get it working--look like they did just that.  You get a patched up final program that has weird behaviors and can be crashed or caused to do incorrect thingsif the user trys anythng the programmer didn't expect.     Its very likely that your whole approach is a bad idea.   If we kne what you were trying to accomplish, we might be able to suggest alternatives.
Avatar of dessi

ASKER

I have several exe's which run on my desktop and display various stock/commodity market conditions.  They communicate with their vendors by internet using heavy encryption.

My task is to build an application which could read the conditions and recommendations from various vendors'apps, and give notice when certain market conditions exist.

I generate mouseclicks to navigate to different screens, and I read the information which is shown via controls.   However,  other information is displayed as bitmaps on the screen.  (BUY , SELL, HOLD, ETC).  The way I have been reading them so far, is to check the pixels (GETPIXEL)in a few locations and by color determine which bitmap is being displayed.( and therefore what the recommendation is)

The limitation of this technique, is that the all of the screens need to be visible in order to check the pixels.  

Ideally I would like to have the screens minimized (or at least covered)so that other work can be done, and only when the user needs to make a decision, does the app display itself.

I had tried to use PRINTCLIENT (though not exactly how neotid outlined which I am trying now)  however if the vendor chose simply not to implement PRINTCLIENT will this work?

Thanks in advance for your help guys.

Doesn't that sound like a round-about way to make the application work?

It woudl be best to try to read the data yourself and thus do eveyrthing inone application.  Or You might look to see if any of the applications have COM interfaces that would allow yout to interact with them in a programmatic manner.  Like you may be able to enquire them for data.

>> however if the vendor chose simply not to
>> implement PRINTCLIENT will this work?
Nope.  But any MFC application should impliment it and any application that uses standard Windows OS windows.  User-defined windows may or may not impliment it.
Here's a (literally) outside-of-the-box idea:

Move the app 'off screen' to a location such as a (-2000,-2000).  See if GetPixel still works.  If so you can keep the app 'normalized' without it cluttering the screen.

-- Dan
GetPixel will not work outside of the device's current clipping region.   It returns CLR_INVALID.

Think about it.  Where would the pixel value be stored?
Avatar of dessi

ASKER

The vendors do not expose any COM interfaces or any other means to get at thier data.  The apps are meant strictly for users and they would not provide me an API or any other normal programmatic means to retrieve the information.

How about subclassing and hooking?  Can I hack the machine code to get at the data?
>> How about subclassing and hooking?  Can I hack the machine code to get at the data?
Of course.  You can inject a DLL into the process and then retreive the data directly from the process'es memory?  You know the address where its stored, right?  opps no.

Yeah it can be done, but It might be faster and easier to write everything from scratch.
Avatar of dessi

ASKER


NIETOD - Here is my implementation of PrintClient...
It results in a black box when I try to draw it...
Is it correct?



CDC * hDC_Window = m_pWndMain->GetDC();

CDC hDC_Mem ;
hDC_Mem.CreateCompatibleDC(hDC_Window);

CBitmap clientmap:
clientmap.CreateCompatibleBitmap(hDC_Window, 1000, 1000);


hDC_Mem.SelectObject(clientmap);

\\  -------- Bitblt works so I assume DC's are correct
\\  hDC_Mem.BitBlt(0,0,500,500,hDC_Window,0,0,SRCCOPY  ) ;
\\  hDC_Window->BitBlt(0,0,50,50, &hDC_Mem, 5,5,SRCCOPY);
     

m_pWndMain->PrintClient(&hDC_Mem,PRF_CLIENT);

\\\\     ----- or ------

m_pWndMain->SendMessage(WM_PRINTCLIENT,(WPARAM)
hDC_Mem.GetSafeHdc(), PRF_CLIENT);


\\\  hDC_Mem drawn ...  results in black box

                   

         

>> hDC_Mem.SelectObject(clientmap);
One problem here. You need to save the handle to the bitmap returned from this.  This bitmap must be selectede back into the DC when you are done with the DC.  i.e. the DC must be deleted with the same bitmap in it that it had when it was created.  (This is a 1x1 x1 bitmap.)  failure to do this does cause memory leaks and other problems.

NOTE I don;t use MFC.  So there is some guesswork here.  But I don;t think MFC does the clean-up for this.  I think you need to.


>> m_pWndMain->PrintClient(&hDC_Mem,PRF_CLIENT);
What window is this?    Is that your main window?  It needs to be the window of the app that you want to get the data from.
>> Think about it.  Where would the pixel value be stored?
You're right it's no good.  But if it had worked it would be pretty cool though... :)

dessi,
Check the return value from the SendMessage() .. there's no rule that an app needs to process WM_PRINTCLIENT.

A couple of other ideas:  When this app is open, can you normally press Ctrl+C and copy the image?  How about right-click and "copy" or "Save picture as" ?  

Use Spy++ to learn the window class.  There may be a clue there.  If the window is based upon a WebBrowser there are other ways to access its data.

-- Dan
I worked out what is happening.  We are passing an HDC with a bitmap that is in the local address space and the process can't do anything with it.  I wrote a DLL that creates a global hook and thereby injects itself into the address space of the target.

// MyHook.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "MyHook.h"

// ------ VERY IMPORTANT.  make a file MyHook.def that looks like this:
/*
=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=--=-=-=- begin .DEF file
LIBRARY      "MyHook"
DESCRIPTION  'MyHook Windows Dynamic Link Library'
SECTIONS
   .shared   Read Write Shared

EXPORTS
    ; Explicit exports can go here
=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=--=-=-=- end .DEF file
*/

// Shared Data
#pragma data_seg(".shared")     // Make a new section that we'll make shared
HHOOK ghHook=            0; // HHOOK from SetWindowsHookEx
HWND  ghwndNotif=        0;
HWND  ghwndToHook=       0;
UINT  gnMyRegisteredMsg= 0;
#pragma data_seg() // Back to regular, nonshared data

// Per process data
BOOL gfIsHookManagerExe= FALSE;

int     DoAction( HWND hWnd );
LRESULT CALLBACK HookProcGetMsg( int eCode, WPARAM wParam, LPARAM lParam);

//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
BOOL APIENTRY DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
{
     gnMyRegisteredMsg= RegisterWindowMessage( CSTR_MHRegisteredMessage );

    if ( dwReason == DLL_PROCESS_ATTACH )  {
        DisableThreadLibraryCalls( hinstDLL );
        if ( lpReserved ) { // Is this main process that sets the hook?
            if ( ghHook ) {  // yes, but already set... don't load twice
                return FALSE;
               }
            gfIsHookManagerExe= TRUE;
              ghHook= SetWindowsHookEx( WH_GETMESSAGE, (HOOKPROC)HookProcGetMsg, hinstDLL, 0 );
          }
     }
    if ( dwReason == DLL_PROCESS_DETACH ) {
        if ( gfIsHookManagerExe && (ghHook != 0) ) {
            UnhookWindowsHookEx( ghHook );          
            ghHook= 0;
        }
     }
     return TRUE;
}

MYHOOK_API void MH_SetNotifWindow( HWND hWnd )  { ghwndNotif=  hWnd; }
MYHOOK_API void MH_SetWindowToHook( HWND hWnd ) { ghwndToHook= hWnd; }
MYHOOK_API void MH_Unhook() {
     UnhookWindowsHookEx( ghHook );
     ghHook= 0;
}

//=============================================================================
// The hook function code
//=============================================================================
LRESULT CALLBACK HookProcGetMsg( int nCode, WPARAM wParam, LPARAM lParam )
{
     MSG* pMsg= (MSG*)lParam;

     if ( nCode >= 0 )  {
          if ( pMsg->message == gnMyRegisteredMsg ) {
               if ( (pMsg->wParam==CNUM_MHDoAction) && (pMsg->hwnd == ghwndToHook) ) {
                    int nRet= DoAction( ghwndToHook );
                    ::PostMessage( ghwndNotif, gnMyRegisteredMsg, CNUM_MHNotif, (LPARAM)nRet );
                    // MessageBeep( MB_ICONEXCLAMATION );
               }
          }
     }
    long lRet= CallNextHookEx( ghHook, nCode, wParam, lParam );
     return( lRet );
}

//--------------------------------------------------
// not used, but here is how to pass data back to
// main app.  It must handle WM_COPYDATA
//
void SendDataToExe( HWND hwndFrom, LPCSTR sText )
{
     COPYDATASTRUCT rCD;
     rCD.cbData= lstrlen(sText)+1;
     rCD.lpData= (void*)sText;
     ::SendMessage( ghwndNotif, WM_COPYDATA, (WPARAM)hwndFrom, (LPARAM)&rCD );
}
Here is the 'DoAction() fn.  It gets a bitmap of the window and (for testing) puts it on the clipboard.

//--------------------------------------------------
// Get a bitmap of the ghwndToHook window
// Proof of concept: Copy to clipboard
//
int DoAction( HWND hWnd )
{
     HDC hDCMem= ::CreateCompatibleDC( NULL );

     RECT rc;
     // ::GetWindowRect( hWnd,&rect );  // little teeny rect when minimized!
     WINDOWPLACEMENT rWP; rWP.flags= sizeof(rWP);
     ::GetWindowPlacement( hWnd, &rWP );
     rc= rWP.rcNormalPosition;


     HBITMAP hBmp= NULL;
     HDC hDC= ::GetDC( hWnd );
     hBmp= CreateCompatibleBitmap( hDC, rc.right-rc.left, rc.bottom-rc.top );
     ::ReleaseDC( hWnd, hDC );

     HGDIOBJ hOld= SelectObject( hDCMem, hBmp );
     ::SendMessage( hWnd, WM_PRINT, (WPARAM)hDCMem, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);

     ::SelectObject( hDCMem, hOld );
     ::DeleteObject( hDCMem );

     //-------------------------- you could doe something else with it...
     ::OpenClipboard( hWnd );

     ::EmptyClipboard();
     ::SetClipboardData(CF_BITMAP, hBmp);
     ::CloseClipboard();

     return(1);
}
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Wow!  I never realized that had to be done.    Good catch.  I've used the message myself, but always within an application.   (And actually just to make sure it functioned.)  Nasty.

Now it remains to be seen of the apps will respond to it.... fingers crossed.
Notice that I sent WM_PRINT rather than WM_PRINTCLIENT, which seems more likely to be handled.

dessi,
When I used that technique with Notepad, there was an oddity in how it responded while minimized.  When minimized, it seems to draw just the client area, aligned top and left.  When not minimized, it draws the NC area as well.  So when you do your GetPixel, you will probably need to offset the (x,y) based upon the minimized-size vs. normal-sized status.  I think that will be available from the GetWindowPlacement() call.

=-=--==-=-=-
Incidently, the posted code should be a good generalized way to inject code into any program.  The DoAction() code executes entirely in the target program's address space so it should have access to (for instance) item text of ListView controls.  Many window messages that pass data pointers to data (such as RichText EM_FORMATRANGE message) should respond correctly.

-- Dan
>> Notice that I sent WM_PRINT rather than WM_PRINTCLIENT, which seems more likely to be handled.
Yes and no.  the default window procedure handles WM_PRINT by drawing the frame, then calling WM_PRINTCLIENT.   Since the client part is probably the only part you want, it doesn't matter much.
No fair looking in MDSN.  lol -- Dan
I didn't.  I just remember that from when i used (tested) those message a LONG while ago.
Avatar of dessi

ASKER

Wow Dan you sure know how to make a lady feel special!!

I tried to compile the code and got the following error
in the cpp file ...

C2491: 'MH_SetNotifWindow' : definition of dllimport function not allowed
C2491: 'MH_SetWindowToHook' : definition of dllimport function not allowed
C2491: 'MH_Unhook' : definition of dllimport function not allowed

can you email the zipped project files?

Dessi@comcast.net

Thanks so much for your help guys
Avatar of dessi

ASKER

Dan thanks for the code, I was trying to implement my own hook when you sent yours.


Ok the Dll is compiling and I have it integrated into my project.   However it shows only the frame and controls.  The background bitmaps are all black.  When I tried sending PRINTCLIENT the window came back all black.  

Now that we have access to process, can I try to access the memory of the application directly?  Granted it may be impossible to figure out where the proper inforamation is... but I am not sure what else to do.
Avatar of dessi

ASKER

The bitmap for any window does not seem to be captured.

How about GetCurrentBitmap?  I wonder if that will return me the Window's current bitmap?
I assume that you got the .H thing figgered out...

Try using Spy++ to figure out which actual window contains the bitmap.  Maybe it has a classname or a title that can be used.  (note: If you can isolate it with Spy++, just hardcode the HWND into the test program for now).

Odds are, it's a child of the window you are specifying (the PRF_CHILDREN flag ought to make it show, but these things are iffy).

For testing, I found it easiest to open a copy of MsPaint and use Ctrl+P after the capture (to paste from the clipbpoard).

-- Dan
>> Ok the Dll is compiling and I have it integrated into my project.  
>> However it shows only the frame and controls.  The background
>> bitmaps are all black.  When I tried sending PRINTCLIENT the
>> window came back all black.  
They are ignoring the message.

>> Now that we have access to process, can I try to access the
>> memory of the application directly?  Granted it may be impossible
>> to figure out where the proper inforamation is... but I am not sure
>> what else to
It may be far easier to write those apps yourself from scratch!

>> he bitmap for any window does not seem to be captured.
That sounds like a mistake.   Check a MFC app just to be sure.  They will do it autoamtically   (Check word or excel if you have them).   Maybe there is a problem with the code, not the message.   That would be the best.

>> I wonder if that will return me the Window's current bitmap?
What current bitmap?  A window doesn't have such a thing.   Not in MS widnwos.  In soem other OSs, like Mach they do, but not in Windows.

>>(the PRF_CHILDREN flag ought to make it show, but these things are iffy).
You definitely should be specifying that flag and PRF_ERASEBACKGROUND  Don't specify the check vissible flag--for this case.

When you specify PRF_CHILDREN, it goes through the children windows and sense the WM_PRITNCLIENT message to them recursivelky to get the window drawn.  This is exactly what it does when you do a WM_PAINT.

Avatar of dessi

ASKER

I Checked with Spy++....  the class of window came back... Dialog #32770

How does that impact trying to get bitmap background?
Well I suspect you don't actually want the contents of a dialog window.  It has none.  its jus the color of the standard background.    The only thing of interest would be the contents of the dialog window's children--in orther words its controls.

Did you specify the PRF_CHILDREN flag.   if you don't, you will get the dialog itself drawn, and none of its children.   (In which case it should be the color of the dialog's "background"  was it?   (You might also need to specify the erasebackgrund flag too, otherwise it might not do anything.)  
Avatar of dessi

ASKER

Yes, I'm using PRF_CHILDREN flag

The window I'm targeting is actually a composite bitmap (background) with controls (children) placed over it. My target is to get the background as well.
So far I'm getting only the frame window and the controls

For example, if you open any bitmap with MS Paint and try to run Dan's solution, it places in the clipboard only the frame window and the controls, but not the actual bitmap.

For Notepad and a text it works, for graphic editor and an image (my problem) it doesn't.
First, are these problems happening only when the window is minimized... or all of the time?

What if you target the child window directly?  Spy++ will probably show a list of them and one probably contains the image you need.  (I'm not in a position to test this myself right now).  

What is the class of the window that holds the payload bitmap?

Perhaps that window is an Owned window but actually a child of the desktop.  Are you using PRF_OWNED?

-- Dan
Avatar of dessi

ASKER

I Checked with Spy++....  the class of window came back... Dialog #32770

How does that impact trying to get bitmap background?
>> So far I'm getting only the frame window and the controls
So you are getting the controls?  Those are probably the most important part.    Do you meant that the only think you are not getting is the backgrund (non-controls) of the dialog window?     If so, it sounds like you need to specify the PRF_ERASEBCKGRND flag.
Avatar of dessi

ASKER

Dan,

I'm using PRF_CHILDREN | PRF_CLIENT| PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED

In the MS Paint scenario it happens all the time - printing only the frame window and the controls.
What is it NOT painting?
Spy++ shoudl show a tree of child windows beneath the Dialog #32770 window.  Can you target one of those?
Avatar of dessi

ASKER

Nietod, I guess I wasn't clear

The window I'm targeting is actually a composite bitmap (background) with controls (children) placed
over it. My target is to get the background as well.
So far I'm getting only the frame window and the controls without the bitmap background.

As I explained if you open a bitmap with MS Paint and try WM_PRINT with all the flags above everything BUT THE BITMAP gets printed

Avatar of dessi

ASKER

If I run any application on my desktop...

If the Spy++ registers an item as a control... it is visible from the hook tool.

However many of the windows have a background picture in what would normally be a grey area of the window. This background picture is assembled from bitmaps on my disk, however they do not register as controls with Spy++.

EXAMPLE...
I opened a bitmap with my editor(bitmap viewed does not register on SPY++)
I tried to Hook and print it
The Grey editor window came back
However the bitmap I was viewing was not returned


That sounds like the windows you are testing are not responding to the WM_PRINTCLIENT message.  Only the WM_PRINT message.  The print message prints the window frame--which you do see, then calls WM_PRINTCLIENT to fill in the client area, which is being ignored.
I open MS Paint.  Then I open Spy++.  I search through the "Windows 1" treeview list until I find
   "untitled - Paint"
When I click the little [+] to open that up, all of the palettes and toolbars etc are listed.  The first child window is AfxFrameOrView42U.  Its child window's class is "Afx:1000000:8"  and *that* is the windows that displays the bitmap.

What happens if you target the HWND of that window?

-- Dan
Avatar of dessi

ASKER

When I try to paint the child it returns a white screen... and the grandchild returns a black screen

Either way the bitmap image in the window is not returned.

Yes dessi,
I am getting EXACTLY the same symptoms you describe.  It doesn't make a lot of sense.... Why should it be able to paint the frame but not the little squares of color in the palette or the tools in the toolbar?  Color is not the problem because the icon in the top left is rendered perfectly.

I'm trying some new stuff.  I'll let you know...

-- Dan
Avatar of dessi

ASKER

Thanks Dan, I appreciate your help.
I keep trying as well.
hi dessi,
Are you still interested in this?  Have you made any progress?  I have a couple of new ideas:
* By intercepting GetUpdateRect and BeginPaint, i might be able to coerce the window to paint itself.
* Hiding the window, then moving it into an onscreen area before triggering the painting might work.  The Taskbar icon would flash off an on...

=-=-=-=-=-=-=-
But I think the real solution is to find the URL of the graphic image and just download it yourself.

-- Dan
Dear dessi

I think you forgot this question. I will ask Community Support to close it unless you finalize it within 7 days. You can always request to keep this question open. But remember, experts can only help you if you provide feedback to their questions.
Unless there is objection or further activity,  I will suggest to split between

     "DanRollins & nietod"

comment(s) as an answer.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
======
Werner
Avatar of dessi

ASKER

Dear Support,

I don't think this question should be closed since it never get answered. It's a serious matter and I'm still working on it trying to find an answer myself.

If the community policy states that a question can not wait for an answer more than x days then I'm definetelly giving all the points to DanRollins for giving me a right direction, providing clean and nice source code adn spending valuable time working on this matter. I do appreciate nietod's comments, but most of them were too general statements, which had nothing to do with MFC and the nature of my problem of reading bitmap from memory.

Let DanRollins have the points, I'll re-post the question with bigger prize and more details.
dessi

The last posting came from DanRollins asking you if you need more help. You never answered his question, therfore I suspected the questions is abandoned. As I stated already, if you do not give feedback to the experts, they won't post any further help ... EE is about sharing knowledge, sharing includes participitation from both sides ...

There is no problem keeping this question open, but if there is no activity, it will be closed eventually.

======
Werner
>>Let DanRollins have the points, I'll re-post the question...

dessi,
The easy way to make that happen is to click the link labeled "Accept Comment as Answer" next to one of my posts.  In the next screen click the "A Excellent" radio button and then click Submit button.

I'll watch for your new question and keep thinking of solutions to help you.  Thanks!

-- Dan
Avatar of dessi

ASKER

Thanks Dan, for your help.  I have implemented a hook as you suggested and I am now subclassing the window.