Printing with Adobe Reader and Waiting for it to Finish (using DDE)

If you program in Visual Studio, there is a nice ActiveX plugin available for Adobe Reader that provides basic capabilities such as loading, displaying, and printing a PDF. After quickly integrating this plugin into one of my projects and happily prepping to begin silently printing PDFs on the user’s command, I realized a nearly show-stopping issue with the API – there is no blocking and no return! I needed to have the application wait until printing was completed before continuing processing. Most of the available API calls through the ActiveX are essentially fire-and-forget in nature, meaning you can easily launch printing, but you’ll have no idea when it completes. After some extensive searching, I found that Adobe is still supporting the older Dynamic Data Exchange (DDE) interface, which does enable blocking on a process – specifically printing. Apparently this has become the de facto approach to printing with blocking.

An older version of the Interapplication Communication documentation is available here. In summary, the following commands are available for Adobe Reader:

● AppExit
● CloseAllDocs
● DocClose
● DocGoTo
● DocGoToNameDest
● DocOpen
● FileOpen
● FileOpenEx
● FilePrint
● FilePrintEx
● FilePrintSilent
● FilePrintSilentEx
● FilePrintTo
● FilePrintToEx

Below is a quick example of how to communicate with Adobe Reader X from a Visual C++ program using DDE. Of particular note is the [FilePrint("test.pdf")][AppExit()] string and the si.nShow = SW_HIDE | SW_MINIMIZE command, which enable silent printing of test.pdf. This will be visible to the user as Adobe Reader launches in a minimized state, prints the specified file using the default settings, and exits. Additionally, DdeClientTransaction( (LPBYTE)hData, -1, hConv, 0L, 0, XTYP_EXECUTE, 3600000, 0 ) is the key call with DDE that enables blocking when waiting for Adobe Reader to finish. The 3600000 is measured in milliseconds, meaning the program will wait 30 seconds before timing out.

#include "ddeml.h"


CString acroReadExe = "C:\\Program Files (x86)\\Adobe\\Reader 10.0\\Reader\\AcroRd32.exe";
CString stQuery = "[FilePrint(\"test.pdf\")][AppExit()]";


HSZ hszApp( 0 );
HSZ hszTopic( 0 );
DWORD dwIdInst = 0;
HCONV hConv = 0;
SHELLEXECUTEINFO si = { sizeof( SHELLEXECUTEINFO ) };
HDDEDATA hData;


//////////////////////////////////////////
// You'll need a couple support functions
//////////////////////////////////////////

inline ProcessDdeMessages()
{
  MSG msg;
 
  while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
  {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }
}


HDDEDATA CALLBACK DdeCallback(
  UINT uType,
  UINT uFmt,
  HCONV hconv,
  HSZ hsz1,
  HSZ hsz2,
  HDDEDATA hdata,
  DWORD dwData1,
  DWORD dwData2 )
{
  return 0;
}




////////////////////////////////////
// Here is the core of the code
////////////////////////////////////

// Initialize DDE
if( DMLERR_NO_ERROR != DdeInitialize( &dwIdInst, (PFNCALLBACK)DdeCallback, APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0 ) )
{
  AfxMessageBox( "Unable to initialize DDE" );
}

hszApp = DdeCreateStringHandle( dwIdInst, ddeServerName, 0 );
hszTopic = DdeCreateStringHandle( dwIdInst, "control", 0 );


// Connect to DDE Server
if( !( hConv = DdeConnect( dwIdInst, hszApp, hszTopic, 0 ) ) )
{
  // Start the DDE server and try connect again
  si.lpVerb = "open";
  si.lpFile = acroReadExe;
  si.nShow = SW_HIDE | SW_MINIMIZE;
 
  if( ShellExecuteEx( &si ) )
  {
    do
    {
      ProcessDdeMessages();
      Sleep( 300 );
    }
    while( !( hConv = DdeConnect( dwIdInst, hszApp, hszTopic, 0 ) ) );
  }
}
 
DdeFreeStringHandle( dwIdInst, hszApp );
DdeFreeStringHandle( dwIdInst, hszTopic );
 

// If we successfully connected, submit our query
if( !dwIdInst || !hConv )
{
  AfxMessageBox( "DDE Connection Failed to " + acroReadExe );
}
  
hData = DdeCreateDataHandle( dwIdInst, (LPBYTE)( stQuery.GetBuffer( stQuery.GetLength() + 1 ) ), stQuery.GetLength(), 0, 0, CF_TEXT, 0 );

if( !hData )
{
  AfxMessageBox( "ADOBE READER: Command failed: \"" + stQuery + "\"" );
}
 
// Execute the query and wait
DdeClientTransaction( (LPBYTE)hData, -1, hConv, 0L, 0, XTYP_EXECUTE, 3600000, 0 );


//  Disconnect DDE
if( hConv )
{
  DdeDisconnect( hConv );
  hConv = 0;
}
      
if( dwIdInst )
{
  DdeUninitialize( dwIdInst );
  dwIdInst = 0;
}
Advertisements

One thought on “Printing with Adobe Reader and Waiting for it to Finish (using DDE)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s