Thread: Inject Dll, handle WM_PAINT...

  1. #1
    Registered User
    Join Date
    Nov 2005
    Posts
    4

    Inject Dll, handle WM_PAINT...

    Hi,
    I need to inject dll into some application and then handle WM_PAINT message of some window (or replace ::OnDraw with another). The application is compiled with VC++ 6 MFC (static lib)

    The reason I want to do so is to wrap ::OnDraw with another ::OnDraw function which will resides in the injected dll and will provide double buffering (flicker free painting) of that window / CView

    in dll (pseudo code):

    Code:
     DLLInit()
    {
       // find original OnDraw function of target CView
       // or
       // Replace WndProc and Handle WM_PAINT of targer window
    
       // ... in both cases also handle WM_ERASEBKGND message and
       // return TRUE;
    }
      
    
    void CSomeView::OnDraw(CDC* pDC) 
    {
       CMemDC dc(pDC);
       orgCView.OnDraw(&dc);
      // destructor of CMemDC whill render the dc obj to original pDC (bitblt)
    }
    
    // or
    
    void thiscall__ OnDraw(CDC* pDC) // (this in ECX)
    
    {
       CMemDC dc(pDC);
       OriginalOnDraw(ECX,&dc);
      // destructor of CMemDC whill render the dc obj to original pDC (bitblt)
    }
    I dont really need dll injection because the targer application is designed to load EVERY dll which resides in its startup folder.

    The question is: Is this possible at all and if the answer is true is there anyone that can help me with this?

    Tnank you.

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    I fairly certain this can't be done the way you are trying to do it. MFC is just a framework build onto the Windows API. You can't over-ride the OnDraw function of the target because there is no OnDraw() function on the target. OnDraw() is just an abstraction created by MFC, and it is not a library function that exists in any application.

    Now you could hook the messages of the target application, and handle its WM_PAINT message, but there is no way you can perform double buffering for it. The reason is that the painting DC is not passing via the WM_PAINT message. The DC is created when BeginPaint() is called in the WM_PAINT handler.

    Now there might be some other way to perform double buffering, but I can't think of anything.

  3. #3
    Registered User
    Join Date
    Nov 2005
    Posts
    4
    you missunderstend me... my english is not so good...
    well i know all of that. There is no OnDraw right. But there is message_map entry of that CView descendant which is called on receiving WM_PAINT...

    and yes, if I handle WM_PAINT i dont expect to receive CDC... but I still can draw on temp dc (and temp bitmap) and render it to the original dc by bitblt...

  4. #4
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    I don't see a way to do this without modifing the original app.

    Most of the work in double buffering is not done in the WM_PAINT handler.

    The idea of double buffering is to do all the drawing THEN post a paint msg.
    In the paint handler a single BitBlit() is used to draw the results to the screen (redawing the smallest area possible).
    ie Double buffering is making the WM_PAINT handler as quick and simple as possible.

    So you need to find when the app needs to change its screen and modify that code...(as well as ensuring that the app uses your new paint handler.)

    So you would have to intercept all events that cause a paint. This includes time, user actions and processing returns. Lots of work to find them all.
    Then you have to ensure that you initialise / clean up properly ie hook the open and close msgs.

    As you are using a pre .NET version of MSVC you will have to be very careful of GDI leaks.....
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  5. #5
    Registered User
    Join Date
    Nov 2005
    Posts
    4
    In my opinion there is another way to do double buffering. Let me explain.

    Let we have the following class (simplified):
    Code:
    // interface 
    
    class CFlickerView : public CView
    {
    protected:
    	CFlickerView();
    public:
    	virtual void OnDraw(CDC* pDC);  
    public:
    	virtual ~CFlickerView();
    protected:
    	//{{AFX_MSG(CFlickerView)
    	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    	//}}AFX_MSG
    	DECLARE_MESSAGE_MAP()
    };
    
    // implementation
    
    void CFlickerView::OnDraw(CDC* pDC)
    {
    	CFlickerDoc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	pDC->TextOut(0,0,"Hello world!");
    	pDC->SelectStockObject(GRAY_BRUSH);
    	pDC->Ellipse(CRect(0,20,100,120));
    }
    
    BOOL CFlickerView::OnEraseBkgnd(CDC* pDC) 
    {
    	return TRUE; // for now use the default handling (e.g. fill with solid color)
    }
    Now, what happen? Windows sends WM_ERASEBKGND and then WM_PAINT (again this is simplified; I know that most of you have solid knowledge about the subject and I’m not trying to teach anybody here but get helped).
    The OnEraseBackgroud event is triggered (if there is message map entry for it) and it should erase (e.g. fillrect) the invalidated region. Then OnDraw event is triggered and we painting here on the already erased surface (region). As a result the view flickers like hell... (I know that every one knows that, but let me finish).

    So, if we do it like this:

    Code:
    BOOL CFlickerFreeView::OnEraseBkgnd(CDC* pDC) 
    {
    	return TRUE; // do not erase bkgnd and tell framework we handle it
    }
    
    void CFlickerFreeView::OnDraw(CDC* pDC)
    {
    	CBitmap    TempBitmap;
        	CBitmap* OldBitmap;
    	CDC	NewDC;
        	CRect      rect;	
      	
    	pOriginalDC=pDC;
    	OldBitmap=null;
    	
    	pDC->GetClipBox(&rect);
    	CDC.CreateCompatibleDC(pDC);
    
    	TempBitmap.CreateCompatibleBitmap(pDC, rect.Width(),rect.Height());
                OldBitmap = TempDC.SelectObject(&TempBitmap);
                TempDC.FillSolidRect(rect, pDC->GetBkColor());
    
    	// the painting
    	TempDC.TextOut(0,0,"Hello world!");
    	TempDC.SelectStockObject(GRAY_BRUSH);
    	TempDC.Ellipse(CRect(0,20,100,120));
      
                 pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), this, rect.left, rect.top, SRCCOPY);            
                 TempDC.SelectObject(OldBitmap);        
    }
    Fill background (painting on TempDC,TempBitmap) draw the rest, copy bitmap to orgDC.

    My idea is to patch message map, vtable or whatever needs to , save the originals and put there another proc (which will reside in dll, assuming dll is loaded by the app) like this:

    Code:
    pseudo code:
    
    DllInit()
    {
      //save originals and patch message map or vtable or ??? to point
     to WrapperProcOnDraw
    }
    
    WrapperProcOnDraw(CDC *pDC)
    { 
       TempDC=CreateTempDCAndBmp; // compatible with the original
       FillBackground;
       OriginalOnDraw(&TempDC); // call original painting routine
       RenderTempDC(pDC); //draw all at once
    }
    The real question is how to do so? How can i find/patch vtable or message map from within dll? I've already found message map entries for OnEraseBackground and for OnDraw, patched it directly into the exe and it works, but I don't wanna modify the exe. I want to modify running image (.rsrc segment) from within the dll, but don't know how to.

    Any ideas are appreciated.

  6. #6
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I'm not familiar with MFC internals so I won't comment on hooking MFC functions as a solution.

    I'll stick to commenting on the subclassing solution.
    Code:
    BOOL CFlickerFreeView::OnEraseBkgnd(CDC* pDC) 
    {
    	return TRUE; // do not erase bkgnd and tell framework we handle it
    }
    This is trivial to achieve with subclassing. Simply override the WM_ERASEBKGND message.
    Code:
    WrapperProcOnDraw(CDC *pDC)
    { 
       TempDC=CreateTempDCAndBmp; // compatible with the original
       FillBackground;
       OriginalOnDraw(&TempDC); // call original painting routine
       RenderTempDC(pDC); //draw all at once
    }
    This can be achieved by overriding the WM_PAINT message. However, as outlined by Bithub, the problem may be the highlighted line. The WM_PAINT message typically does not take a HDC (although some common controls do allow a HDC to be passed). The HDC is obtained by calling BeginPaint. This makes it difficult to get WM_PAINT to draw on anything but the screen.

    There are a couple of potential solutions. The first is the WM_PRINT message. This message is similar to WM_PAINT, except that it will paint on a provided HDC. The issue is that many controls do not handle WM_PRINT (although it is very simple to do so). If your window correctly handles WM_PRINT this may be a neat solution to your problem.

    The other is to hook BeginPaint to provide a HDC of your choosing. Of course, this is undocumented and liable to break on future platforms. This method is outlined in Window Contents Capturing using WM_PRINT Message. Down the bottom of the page is an example(capture.zip) that you could probably modify to achieve your aims.

  7. #7
    Registered User
    Join Date
    Nov 2005
    Posts
    4
    Quote Originally Posted by anonytmouse
    ...There are a couple of potential solutions. The first is the WM_PRINT message...
    Well first I want to ask who is going to send this message to the application? I mean what is the mechanism (is it send only while printing or?).

    And the second: There are not separate WndProc for distinct window in MFC. As far as I know (I’ve read it somewhere on the net) there is only one WindowProc (called AfxWndProc) that acts like handler for every message sended to the application. Then this proc calls another framework proc (OnWndMsg) where the actual message dispatching is performed. But OnWndMsg proc doesn't switch on WM_XX but calls other function that retrieve MESSAGE_MAP entries for the particular CWnd,CFrame,CView (or whatever it is) and then call appropriate method (like OnDraw,OnPaint,OnEraseBackground, etc…) pointed by pfn member of the message entry structure. If I do SetWindowLong and replace original WndProc (which is only one at all) I will handle messages to all windows of the application. I’ve already try it that way and this is exactly what’s happened (doesn’t work). Now, the message maps are statically compiled and reside in the .rdata (or .rsrc) segment of the exe. And by examine disassembly I’ve found all of them. I need a method for overwriting (patching) the message map within the dll at runtime.

    Sorry for the English and the long explanations, I doing my best.

  8. #8
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> Well first I want to ask who is going to send this message to the application? I mean what is the mechanism (is it send only while printing or?). <<

    You would call it from the subclassed window procedure when you needed the window rendered to your memory DC. Something like this:
    Code:
    LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	if (uMsg == WM_PAINT)
    	{
    		// TempDC=CreateTempDCAndBmp; // compatible with the original
    		// FillBackground;
    
    		// Get the original window procedure to render onto our memory DC...
    		CallWindowProc(pOldWndProc, WM_PRINT, (WPARAM) TempDC, PRF_CLIENT);
    
    		// RenderTempDC(pDC); //draw
    
    		return 0;
    	}
    
    	return CallWindowProc(pOldWndProc, hwnd, uMsg, wParam, lParam);
    }
    >> If I do SetWindowLong and replace original WndProc (which is only one at all) I will handle messages to all windows of the application. <<

    This is not how subclassing works. SetWindowLong will change the window procedure function pointer for the specified window. It will not affect the old window procedure. It will not affect other windows, even if they use the same window procedure function.

    >> Now, the message maps are statically compiled and reside in the .rdata (or .rsrc) segment of the exe. And by examine disassembly I’ve found all of them. I need a method for overwriting (patching) the message map within the dll at runtime. <<

    You can find a resource using FindResource function, load it with LoadResource and lock it with LockResource. This will give you a pointer to the memory containing the resource. This can be made writable with the VirtualProtect function. You can then use the CopyMemory function to copy in your patched version. There is an example of patching a resource in the current EXE here.

  9. #9
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    As of Windows 2000 and newer, you cannot use SetWindowLong to change the window procedure for another application. You need to install a hook instead.

  10. #10
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Quote Originally Posted by bithub
    As of Windows 2000 and newer, you cannot use SetWindowLong to change the window procedure for another application. You need to install a hook instead.
    If the DLL is loaded into the original application, I think it effectively becomes part of the application. I used SetWindowLongPtr in cbNotepad without a problem.

  11. #11
    Reverse Engineer maxorator's Avatar
    Join Date
    Aug 2005
    Location
    Estonia
    Posts
    2,318
    Quote Originally Posted by bithub
    As of Windows 2000 and newer, you cannot use SetWindowLong to change the window procedure for another application. You need to install a hook instead.
    That's nice, because I use Win 98SE

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Two Step DLL Inection (How to get thread handle?)
    By Takeshi in forum Windows Programming
    Replies: 1
    Last Post: 01-11-2009, 12:09 PM
  2. Getting other processes class names
    By Hawkin in forum Windows Programming
    Replies: 3
    Last Post: 03-20-2008, 04:02 PM
  3. Direct3D problem
    By cboard_member in forum Game Programming
    Replies: 10
    Last Post: 04-09-2006, 03:36 AM
  4. dll communicating between each other
    By cloudy in forum C++ Programming
    Replies: 5
    Last Post: 06-17-2005, 02:20 AM
  5. DLL and std::string woes!
    By Magos in forum C++ Programming
    Replies: 7
    Last Post: 09-08-2004, 12:34 PM