SendMessage(WM_PRINTCLIENT) to external app?

Hello and thanks in advance for reading.

I'm creating a web application that needs some advanced printing capabilities, but since web technologies don't support printing outside of initiating the standard PrintDlg(), I must resort to writing a native windows component.

I'm new to this, but enjoying it very much. So far I've successfully been able to get a list of installed printers using EnumPrinters(), send a string to print using OpenPrinter(), StartDocPrinter(), StartPagePrinter(), WritePrinter(), EndPagePrinter(), EndDocPrinter(), and ClosePrinter() (phew!), and lastly I can grab the public parts of the DevMode structure returned from a call to PrintDlg().

Where I'm stuck is that I need to print more than just a string of text, I need to print the contents of the browser's current page (my web app). I can't simply screenshot it, because the browser renders the page very differently for a printer ("print stylesheets").

So here's my question...

Is it possible to use SendMessage() to tell an external application to print?

I see in Google Chrome's source code that it does handle WM_PRINTCLIENT messages, but it's difficult for me to understand what it's doing. I can successfully grab a handle to it's window and use SendMessage() to WM_CLOSE it, but passing WM_PRINTCLIENT with a printer DC does nothing.

1
2
3
4
5
6
LRESULT results = SendMessage(
	hWndOwner, //this is a handle to Chrome's window
	WM_PRINTCLIENT,
	(WPARAM)PrinterContext,
	PRF_CLIENT
);


Is my understanding of WM_PRINTCLIENT completely wrong? I'm simply sending the message as shown above and hoping that Chrome will do the printing. Is there some way to retrieve something from that call? MSDN says it's used "most commonly in a printer device context". (http://msdn.microsoft.com/en-us/library/windows/desktop/dd145217(v=vs.85).aspx)

Your experience is greatly appreciated.
You can't (easily) send the WM_PRINTCLIENT message across a process boundary. If your "native windows component" is in a separate process from Chrome, then when the message loop in Chrome's process processes the WM_PRINTCLIENT message the PrinterContext (the wParam parameter) is a handle in your "native windows component" process and doesn't mean anything in the Chrome process.
Ahh, I wasn't aware of that. Thanks cburn.

The "native windows component" is actually a node module, and the application will be packaged with Node-Webkit. It's probable that it will be the same process, or that both processes will share the same parent (if that even makes any sense).

My development environment isn't currently setup that way (I'm using node and Google Chrome separately), so I'll run off and package it and test... I hadn't realized it would make a difference.

Any chance you could confirm that I'm at least approaching it correctly?... is it correct to assume that I can send a valid PrinterDC with SendMessage() and the printing (to an actual printer) will be handled by the receiving app?

FYI, I just stumbled on PrintWindow(), which seems to just be a wrapper around what I'm currently doing, but is recommended so I'll probably switch.

Thanks again cburn, I really appreciate it.
This website provides tutorials for both web image printing and windows image printing. You can also use free imaging SDK from this website to build a powerful web printing application with comprehensive image editing and processing capacities.

http://www.rasteredge.com/how-to/asp-net-imaging/imaging-saving-printing/
http://www.rasteredge.com/how-to/winforms-net-imaging/imaging-saving-printing/
CBurn,

The window handle that I passed to PrintDlgEx() belongs to Chrome, would that make a difference in this case? The print dialog that displays is correctly a modal of Chrome. Also, Chrome DOES respond to my app's WM_CLOSE message.

I think maybe I'm just misunderstanding something fundamental about the process. Any chance you could glance at my code below and tell me if I'm doing something really stupid? {I'm sure I must be :)}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*********************************
 * Initialize some things
 ********************************/
HRESULT hResult;
PRINTDLGEX pdx = {0};
LPPRINTPAGERANGE pPageRanges = NULL;
HWND hWndOwner = GetForegroundWindow(); //In this case, this is Chrome


pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));


pdx.lStructSize = sizeof(PRINTDLGEX);
pdx.hwndOwner = hWndOwner; //attach the PrintDlg() to Chrome
pdx.hDevMode = NULL;
pdx.hDevNames = NULL;
pdx.hDC = NULL;
pdx.Flags = PD_RETURNDC | PD_COLLATE;
pdx.Flags2 = 0;
pdx.ExclusionFlags = 0;
pdx.nPageRanges = 0;
pdx.nMaxPageRanges = 10;
pdx.lpPageRanges = pPageRanges;
pdx.nMinPage = 1;
pdx.nMaxPage = 1000;
pdx.nCopies = 1;
pdx.hInstance = 0;
pdx.lpPrintTemplateName = NULL;
pdx.lpCallback = NULL;
pdx.nPropertyPages = 0;
pdx.lphPropertyPages = NULL;
pdx.nStartPage = START_PAGE_GENERAL;
pdx.dwResultAction = 0;


// show the print dialog
hResult = PrintDlgEx(&pdx);

if((hResult == S_OK) && (pdx.dwResultAction != PD_RESULT_CANCEL))
{
	DEVMODE * myDevMode		= (DEVMODE *)GlobalLock(pdx.hDevMode);
	DEVNAMES * myDevNames	= (DEVNAMES *)GlobalLock(pdx.hDevNames);

	if(pdx.hDC)
	{
		DOCINFO di = {0};

		di.cbSize = sizeof(DOCINFO);
		di.lpszDocName = "Print Me";
		di.lpszOutput = (LPTSTR) NULL;
		di.lpszDatatype = (LPTSTR) NULL;
		di.fwType = 0;

		StartDoc(pdx.hDC, &di);
		StartPage(pdx.hDC);

		LRESULT results = SendMessage(
			hWndOwner,
			WM_PRINTCLIENT,
			(WPARAM)pdx.hDC,
			PRF_CHECKVISIBLE | PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_OWNED
		);

		EndPage(pdx.hDC);
		EndDoc(pdx.hDC);

		DeleteDC(pdx.hDC);
	}
}



My intentions are to: display the print dialog, store the resultant printerDC, then tell Chrome to print as necessary by calling StartDoc(), StartPage(), SendMessage(hWndChrome, WM_PRINTCLIENT, ...), EndPage(), EndDoc(). My assumption is that Chrome will draw to the device context during the SendMessage() call. All I get now is a blank page.

Does it not work this way?
Is SendMessage() asynchronous or something?

Any ideas?

I really appreciate your help, I've been banging my head on this one for TOO long :)
I am happy to help, but I need more context. Specifically, what is the execution environment of the above posted code. Is the code in a function exported by dll loaded into Chrome's process? Is the code in a separate executable with its own process (For example: in wWinMain/wmain or a function called by wWinMain/wmain)?
Thank you VERY much for responding.

I don't think I understand enough to answer your question, unfortunately. It's not a DLL. I'm using Node-Webkit, and the above code is exported as a Node module. I'm then able to include it using Javascript and call that function. I know node and webkit have different PID's when it's running, but they probably share the same parent. I only run one application to start it. It probably fits in the category of "it's a separate executable with it's own process", but I can't be sure. Webkit is the rendering engine behind Chrome, so this is like a special build of Chrome, bundled with NodeJS (server-side JavaScript).

I was originally attempting to just send Chrome a WM_PRINTCLIENT message from a separate application. I believe now that was a flawed approach, and I misunderstood the purpose of the WM_PRINTCLIENT message... *I think*.

Taking a screenshot of Chrome doesn't work, since Chrome renders a given page differently for a screen than it does for a printer. What I ultimately need is a way to tell Chrome to run it's print routine, but provide a printerDC instead of it fetching one from the user via a PrintDlg(). This might be a really tall order, but I'm motivated and willing to put the effort into learning. Problem is, msdn isn't that helpful to me and I haven't found any resources that explain the concepts enough to answer my questions... just reference material.

Thanks again.
Topic archived. No new replies allowed.