mIRC Home    About    Download    Register    News    Help

Print Thread
Page 1 of 2 1 2
Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
I've been puzzling around this for quite some time, so I figured I might ask around here...
I'm currently trying to create a small DLL that makes use of Windows 7 features such as the Thumbnail Toolbar (see this image to get the idea).
So far, I got pretty much everything in place (adding the buttons, relaying the clicks back to mIRC)...except button images.

Other DLLs seem to use Picture windows to get their icons, so I thought I'll just go ahead and try the same.
$window(@picwin).hwnd gets me the HWND of it, but I was having troubles getting the actual content - my tries so far ended up being all white (thus empty). Currently, I tried to use BitBlt, whereas some other page suggested the use of WM_PRINT - both might not work with mIRC it seems. Or I'm simply doing-it-wrong(TM).

Would anyone happen to have a few pointers or code snippets to get me started on this? Or is there probably another preferred way on getting HICONs/HBITMAPs from PicWins into one-owns DLL?
Any help on that would be appreciated!

- BhaaL

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Realize that the hwnd is the window handle of the picwin container not the picwin contents. There's another hwnd inside where mIRC draws. You should use a tool like Spy++ to find out what the window structure looks like and you can FindWindow to get the hwnd from the parent hwnd that mIRC gives you.

After you get the real hwnd you need to hook into the window (SetWindowsHook) and handle the WM_PAINT (not PRINT) message.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Originally Posted By: argv0
After you get the real hwnd you need to hook into the window (SetWindowsHook) and handle the WM_PAINT (not PRINT) message.


Is this the preferred way to do this, or just an idea of what might work? Wouldn't hooking WM_PAINT affect how mIRC draws the window, since it only tells mIRC to go and do the painting?

In the meantime, i found a semi-working solution with PrintWindow on $window(@picwin).hwnd and PW_CLIENTONLY. After converting it to an ICONINFO struct and calling CreateIconIndirect, I get a suitable HICON that works with the THUMBBUTTON struct.
However, I can't get the hbmMask right to have a transparent background on that Icon (and thats probably not part of the support i should be getting here - after all, thats my lack of knowledge in the GDI/WinAPI department).

Either way, thanks for your reply.

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Well, you're right. I misunderstood your initial post, it seems (not sure why).

All you really need is the HWND to the contents of the picwin-- so you still need Spy++ to find out which child window it is (use that info in your FindWindow call). After you have the HWND you can BitBlt the data out. That part is as easy as:

Code:
    HWND hwnd = THE_HWND;
    RECT size;
    GetClientRect(hwnd, &size);
    int w = size.right - size.left;
    int h = size.bottom - size.top;
    HDC hDC = GetDC(hwnd);
    HDC hMemDC = CreateCompatibleDC(hDC);
    HBITMAP bmp = CreateCompatibleBitmap(hDC, w, h);
    if (bmp) {
        HDC hOld = (HDC)SelectObject(hMemDC, bmp);
        BitBlt(hMemDC, 0, 0, w, h, hDC, 0, 0, SRCCOPY);
        SelectObject(hMemDC, hOld);
        DeleteDC(hMemDC);
        ReleaseDC(hwnd, hDC);
    }


You would now have an HBITMAP in bmp. You can convert that to an HICON if you want (not sure what your goal is) but it might not be necessary.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Originally Posted By: argv0
Well, you're right. I misunderstood your initial post, it seems (not sure why).

Maybe an image helps:


That thing needs either a IMAGELIST set before, or the icon as HICON to be specified directly. Hence the need for HICON:
Originally Posted By: argv0
You would now have an HBITMAP in bmp. You can convert that to an HICON if you want (not sure what your goal is) but it might not be necessary.


My Problem right now is to get that thing transparent.
ICONINFO has a hbmMask, which should possibly be (as far as i believe I understood it) all black where it should be transparent. I played around with SRCCOPY, SRCAND and the likes, but never really got anything different than the solid white background.

Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Uh, damn, new problem here.
I usually create my drawing windows as /window -ph @pic, and that breaks PrintScreen(hWnd, PW_CLIENTONLY) - the resulting pic is just black (surprise, thats what was also in the docs and on google).

How else could I go and capture the contents of a PicWin that is hidden (using /window -h) from a DLL?
mIRC itself has to do it somehow (see /toolbar stuff), how does it do that?

Last edited by BhaaL; 01/01/10 04:24 PM.
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
I wouldn't be asking around here if the problem was finding the API to use...

PrintWindow works as it should, unless I hide the window. WM_PRINT only works with PRF_NONCLIENT, returning me the window border - the content is always black for some reason.
Code:
int Width = GetSystemMetrics(SM_CXSMICON);
int Height = GetSystemMetrics(SM_CYSMICON);
HWND hWndSrc = hWndOfMircPicWin;

HWND hWnd = hWndSrc;
//get first parent of picwin
//HWND hWnd = FindWindowEx(hWndSrc, NULL, NULL, NULL);

HDC hDCMem = CreateCompatibleDC(NULL);
HDC hDC = GetWindowDC(hWnd);
HBITMAP hBmp = CreateCompatibleBitmap(hDC, Width, Height);
ReleaseDC(hWnd, hDC);

HGDIOBJ hOld = SelectObject(hDCMem, hBmp);
PrintWindow(hWnd, hDCMem, PW_CLIENTONLY);
//SendMessage(hWnd, WM_PRINT, (WPARAM) hDCMem, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);
//BitBlt(hDCMem, 0, 0, Width, Height, hDC, windowBorderLeft, windowBorderTop, SRCCOPY);
SelectObject(hDCMem, hOld);
DeleteObject(hDCMem);


Sometimes I get the odd feeling that I'm always the first person to try something...and fail miserably.

Khaled, in case you happen to read this, how does mIRC do that for /toolbar, where theres no problem to use hidden PicWins at all?

Last edited by BhaaL; 03/01/10 12:35 PM.
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Quote:

PrintWindow works as it should, unless I hide the window


Which means it doesn't work, which is why I linked you to a topic which *specifically* deals with hidden windows. Did you bother to read it? Have you tried not using PW_CLIENTONLY? Are you saying that proposed solution is wrong? If so, try this: http://www.tech-archive.net/Archive/Deve...0/msg00087.html (it uses BitBlt as I suggested multiple times already)


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Originally Posted By: argv0
Are you saying that proposed solution is wrong?

I never said it was wrong, i just said it doesn't work. And I did read both links, the following problem just stumped me on that.

Problem somewhat solved, that call to ReleaseDC should have been down by a few lines, *after* the BitBlt call.
The only thing left is now to get the right thing captured, something isnt quite right there yet (but I do get a result now, non-black).

Thanks for now, I'll check back later once this thing is up and running properly (and after some major code cleanup, this thing is fubared with attempts on getting the window right).

Sigh, BitBlt shows the same stuff now, blank for hidden windows.
The only differences are that BitBlt captures the topmost window when the picwin is overlapped, while PrintWindow still does fine. However, PrintWindow captures black while BitBlt captures transparent for hidden picwins. WM_PRINT works for hidden windows, but the client area is black there.
PicWins are not WS_EX_LAYERED according to Spy++, so I'd rather not try the long way rerouting UpdateLayeredWindow.
I'll play around a bit more, the code is basically the same as in my last post (except the ReleaseDC call, which is now moved back to the DeleteObject call that should be DeleteDC)

Last edited by BhaaL; 04/01/10 08:45 PM.
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
The mIRC display routine checks to see if the Window is visible or not. If it is not visible it does not update the display. The reason it does this is that the display routine is called directly by a number of other routines not just by WM_PAINT. So I don't think you'll be able to capture the client area for hidden windows currently. I will change this in the next version to allow capture via the WM_PRINT command and will add support for WM_PRINTCLIENT as well.

Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Great, looking forward to it! smile

Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Hey, congratz on releasing 7.1! Just updated (6.35 didn't notify me, tho), and...it doesn't work for me?
Or I'm still doing something wrong.

BitBlt returns black (when the window is hidden) or the topmost text (when the window is clipped).
WM_PRINT (and WM_PRINTCLIENT) with PRF_CLIENT plays nicer than before - the result is filled with the configured border color (Display > Options, Styles, Border Color) instead of black.
PrintWindow is still black.

Could it be I'm doing something wrong (again, still)?
Just to make sure, I used argv0's code this time:
Code:
HWND hwnd = hWndSrc; //$window(@picwin).hwnd
int w = 16;
int h = 16;
HDC hDC = GetDC(hwnd);
HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP bmp = CreateCompatibleBitmap(hDC, w, h);
HICON icon = NULL;
if (bmp) {
    HDC hOld = (HDC)SelectObject(hMemDC, bmp);

    //BitBlt(hMemDC, 0, 0, w, h, hDC, 0, 0, SRCCOPY);
    //SendMessage(hwnd, WM_PRINT, (WPARAM)hMemDC, PRF_CLIENT | PRF_CHILDREN);
    SendMessage(hwnd, WM_PRINTCLIENT, (WPARAM)hMemDC, PRF_ERASEBKGND | PRF_CLIENT | PRF_CHILDREN | PRF_OWNED);
    //PrintWindow(hwnd, hMemDC, PW_CLIENTONLY);

    SelectObject(hMemDC, hOld);
    icon = HIconFromHBitmap(bmp);
    DeleteDC(hMemDC);
    ReleaseDC(hwnd, hDC);
}

return icon;


Any ideas on whats wrong here?

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
I have been testing this out and have had no luck either. As far as I can tell, PrintWindow() does not send WM_PRINTCLIENT, it sends WM_PAINT and it does not send WM_PAINT to hidden windows, so it cannot be used to capture hidden windows. With WM_PRINT, if a window is hidden, it does not capture the window frame or titlebar but will capture window contents if windows respond to WM_PRINTCLIENT. And with WM_PRINTCLIENT, similar issues to WM_PRINT.

I had the best results with SendMessage(hwnd, WM_PRINT, (WPARAM)hMemDC, PRF_ERASEBKGND | PRF_CLIENT | PRF_CHILDREN | PRF_OWNED) which allowed me to capture those parts of mIRC windows that support WM_PRINTCLIENT, such as the text display area.

It also turns out that rich editboxes do not support WM_PRINT/PRINTCLIENT, so the editbox in mIRC will appear blank. PrintWindow(), which uses WM_PAINT, does work with rich editboxes.

Which begs the question: do you know of any applications that are capable of reliably capturing hidden window contents? :-) Every discussion on this issue I have found through Google eventually concludes that it is not possible to do this reliably.

Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Odd, when even you're stumped, I don't really know what else could be done.

Can't you simply watch out for WM_PRINT/CLIENT and draw to the passed HDC, regardless of whether the window is hidden or not? As far as I read from all those topics and blogposts on google, it will only work when the target application supports it.
My windows in question are (hidden) pic windows, no ordinary @windows or dialogs.

Another way could be making your internal API available to DLLs, so they can also go the way /toolbar and others take - I think i read somewhere you draw to a memory HBITMAP and WM_PAINT it on the @window surface when required? I'd also be thinking along the lines of $window(@picwin).hbitmap, similar to .hwnd to stay inside the scripting language.

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
The SendMessage() that I included in my previous reply works for me when capturing the client area of a picture @window. I tested it when the window was both visible and hidden.

Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Weird, regardless of whether I hide the window or not, SendMessage(WM_PRINT) to either $window(@pic).hwnd or FindWindowEx(hWnd, NULL, NULL, NULL) return nothing but garbage for me.
I checked the values with Spy++, and sending it to the window itself returns an image filled with the configured border color. Sending it to the child (which is supposed to be the thing containing the image, as argv[0] noted) only returns black.


Can you post your snippet that works? Maybe I'm messing up something else causing this (my snippet is still the same as posted above)

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
I performed a few more tests - it turns out that this was working only because the same copy of mIRC was sending the WM_PRINT message to itself.

After adding some debug code to monitor WM_PRINT and WM_PRINTCLIENT requests, it turns out that if mIRC receives a WM_PRINT from an external application, nothing else happens - the WM_PRINT is not propagated. If I send WM_PRINTCLIENT directly to the text display routine, it receives the message, however even if I perform a simple FillRect() on the memory dc, it remains black.

As far as I know, HDCs cannot be passed across processes, however I assumed that WM_PRINT would bypass that. It seems that it does not. Either that or it requires a special type of HDC that can be used across processes.

Joined: Mar 2008
Posts: 93
B
BhaaL Offline OP
Babel fish
OP Offline
Babel fish
B
Joined: Mar 2008
Posts: 93
Now the question is whether a DLL loaded with $dll counts as external application (or why WM_PRINT is not propagated).
Shouldn't that also mean that PrintClient could never work? Or other snippets you can find on google?

The surprising thing is that i do get something drawn, and thats the border color - so the blocking probably happens somewhere in between, and could possibly be changed by you.
Sending WM_PRINT to $window(@window).hwnd and changing Display > Options > Styles > Border color does affect the outcome.

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
If you use $dll() it is loaded into the same process, so it should work. Tested here using a DLL and $dll() and it captured the @window into a bitmap, whether visible or hidden, although when hidden, the titlebar and frame are missing, which are beyond mIRC's ability to control. Are you sure you are using the correct window handle? Have you tried copying the resulting bitmap to the clipboard instead?

Quote:
OpenClipboard(hWnd);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbm);
CloseClipboard();

I am using the same code as suggested earlier in this thread. Unfortunately I really cannot dedicate any more time to this - I have spent many hours on it already. I would suggest that you create a separate application that itself handles WM_PAINT/WM_PRINT/WM_PRINTCLIENT to see if you can get it to work.

Page 1 of 2 1 2

Link Copied to Clipboard