Hi,
I attach some code that can open PDFs using DDE, which is the API
interface. It also allows you to go to a specific page, and so on. It has
been tested with a range of different Acrobat versions on all windows
versions. It would be simple to extend this with other methods, such as
close, print and so on.
See
http://partners.adobe.com/public/developer/en/acrobat/sdk/pdf/iac/IACOverview.pdf
for the full API Adobe provides.
Regards,
Asger
#if
!defined(AFX_ACROBATREADER_H__A7867A8B_6E2C_42AF_90E6_4A9E81C44473__INCLUDED_)
#define AFX_ACROBATREADER_H__A7867A8B_6E2C_42AF_90E6_4A9E81C44473__INCLUDED_
#include <Ddeml.h>
#include <string>
class AcrobatReader {
public:
AcrobatReader( std::string const & appName );
~AcrobatReader();
void openDoc( std::string const & filename );
void gotoPage( int page );
void gotoName( std::string const & name );
private:
// Fires up a viewer and returns true if successful
bool startReader();
bool acrobatReaderIsRunning();
// Return true when successful
bool sendDdeMsg( std::string const & msg );
// DDE transaction
bool setUpDde();
void shutDownDde();
void reportDdeError( std::string const & msg );
// The process info is used to shut the viewer down again
PROCESS_INFORMATION processInfo;
// The file being viewed
std::string file;
// The application name, used for reporting errors
std::string appName;
std::string acrobatReaderExecutable;
bool firstTime;
// The DDE communication handles
DWORD ddeId;
HSZ serverName;
HSZ topicName;
HCONV conversation;
};
#endif //
!defined(AFX_ACROBATREADER_H__A7867A8B_6E2C_42AF_90E6_4A9E81C44473__INCLUDED_)
#include <windows.h>
#include "AcrobatReader.h"
// Ignore DDE messages from pdf viewer
// This also seems to interact with COM. For example, DDEMessageProc also
receives
// messages when an event sink is attached to IE.
HDDEDATA CALLBACK
DDEMessageProc(
UINT uType,
UINT uFmt,
HCONV hconv,
HSZ hsz1,
HSZ hsz2,
HDDEDATA hdata,
DWORD dwData1,
DWORD dwData2 )
{
# if defined( _DEBUG )
UTIL_TRACE( _T("DDE callback type %d: \n"), uType );
# endif
return NULL;
}
void
AcrobatReader::reportDdeError( std::string const & msg )
{
UINT errCode = DdeGetLastError( ddeId );
std::cout << msg << " (DDE error code " << errCode << ")" << std::endl;
}
bool
AcrobatReader::startReader()
{
CWaitCursor wc;
// Close Acrobat Reader process and thread handles, if any
if ( processInfo.hProcess != 0) {
CloseHandle( processInfo.hProcess );
}
if ( processInfo.hThread != 0) {
CloseHandle( processInfo.hThread );
}
ZeroMemory( & processInfo, sizeof( PROCESS_INFORMATION ) );
// Tell Acrobat Reader how to start up
STARTUPINFO startupInfo;
ZeroMemory( & startupInfo, sizeof( STARTUPINFO ) );
startupInfo.cb = sizeof( STARTUPINFO );
startupInfo.wShowWindow = SW_SHOW;
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
// Launch Acrobat.
if ( ! CreateProcess( acrobatReaderExecutable.c_str(), NULL, NULL,
NULL,
false, NORMAL_PRIORITY_CLASS,
NULL, NULL, & startupInfo, & processInfo ) ) {
std::string message =
"Problem starting Acrobat Reader '" +
acrobatReaderExecutable + "': "
+ getFormatErrorMessage( GetLastError() );
MessageBox(
::AfxGetMainWnd()->GetSafeHwnd(),
message.c_str(),
appName.c_str(),
MB_OK | MB_ICONINFORMATION
);
return false;
} else {
return true;
}
}
bool
AcrobatReader::setUpDde()
{
/// Set up DDE communication
// Allow some time in ms for DDE connection to be established
int const MAX_TIMEOUT = 5000;
// Sleep one-fifth of a second when the DDE connection cannot be
obtained
int const STEP_SIZE = 200;
/// Initialize DDE conversation with server
serverName = DdeCreateStringHandle( ddeId, _T("acroview"), 0 );
topicName = DdeCreateStringHandle( ddeId, _T("control"), 0 );
// Acrobat can take a while to launch. We repeatedly attempt to connect
to the server until MAX_TIMEOUT expires
DWORD sleep = 0;
while ( true ) {
conversation = DdeConnect( ddeId, serverName, topicName, NULL );
if ( conversation || ( sleep > MAX_TIMEOUT ) ) break;
// Give Acrobat some more time to launch
::Sleep( sleep += STEP_SIZE );
}
if ( ! conversation ) {
reportDdeError( "Could not connect to server" );
return false;
} else {
return true;
}
}
void
AcrobatReader::shutDownDde()
{
// Terminate the DDE conversation
DdeDisconnect( conversation );
// Release the resources we acquired
DdeFreeStringHandle( ddeId, serverName );
DdeFreeStringHandle( ddeId, topicName );
}
bool
AcrobatReader::sendDdeMsg( std::string const & msg )
{
bool success = false;
// Check whether a viewer is running.
// First try Acrobat Reader
HWND viewer = FindWindow( NULL, _T("Adobe Reader") );
if ( viewer == NULL ) {
// Then try Acrobat Reader
viewer = FindWindow( NULL, _T("Acrobat Reader") );
}
if ( viewer == NULL ) {
// Then try Acrobat
viewer = FindWindow( NULL, _T("Acrobat") );
}
if ( viewer == NULL ) {
// Make sure it is started
if ( startReader() ) {
// Nothing
} else {
return false;
}
} else {
// Make sure it is shown
::ShowWindow( viewer, SW_SHOW );
}
// Set up the DDE connection
if ( setUpDde() ) {
// Send the message
if (
DdeClientTransaction(
(unsigned char *) msg.c_str(),
(DWORD) msg.length(),
conversation,
NULL,
0,
(UINT) XTYP_EXECUTE,
TIMEOUT_ASYNC,
NULL ) )
{
success = true;
}
shutDownDde();
}
return success;
}
AcrobatReader::AcrobatReader( gstring const & an ) :
appName( an ),
ddeId( 0 )
{
// Make sure the process info is null
ZeroMemory( & processInfo, sizeof( PROCESS_INFORMATION ) );
/// Determine through the Registry whether Acrobat Reader is installed.
// TODO: Is Acrobat (Reader) always named like this?
TCHAR const * REG_INSTALL_KEY =
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\AcroRd32.exe");
TCHAR const * REG_INSTALL_KEY_ALT =
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Acrobat.exe");
LONG retCode = ERROR_SUCCESS;
HKEY hkey;
// Try to find Acrobat Reader
retCode = ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_INSTALL_KEY, 0,
KEY_READ, & hkey );
// If no Acrobat Reader, try to find First Acrobat
if ( retCode != ERROR_SUCCESS ) {
retCode = ::RegOpenKeyEx( HKEY_LOCAL_MACHINE,
REG_INSTALL_KEY_ALT, 0, KEY_READ, & hkey );
}
if ( retCode == ERROR_SUCCESS ) {
// Get the path to the Acrobat Reader executable.
TCHAR buffer[ MAX_PATH ];
DWORD size = MAX_PATH;
retCode = RegQueryValueEx( hkey, _T(""), 0, 0, (LPBYTE) buffer,
& size );
if ( retCode == ERROR_SUCCESS ) {
RegCloseKey( hkey );
acrobatReaderExecutable =
GString::multiByteFrom_t_str(buffer);
}
}
// Catch all errors
if ( retCode != ERROR_SUCCESS ) {
std::cout << "Could not find Acrobat using registry key '" <<
REG_INSTALL_KEY << "', or '" << REG_INSTALL_KEY_ALT <<
"' : " <<
retCode << std::endl;
}
// Prepare for DDE communication
if ( DdeInitialize( & ddeId, (PFNCALLBACK) & DDEMessageProc,
APPCMD_CLIENTONLY, 0 ) != DMLERR_NO_ERROR ) {
reportDdeError( "Failed to initialize DDE" );
}
}
AcrobatReader::~AcrobatReader()
{
// Close Acrobat Reader process and thread handles, if any
if ( processInfo.hProcess != 0) {
CloseHandle( processInfo.hProcess );
}
if ( processInfo.hThread != 0) {
CloseHandle( processInfo.hThread );
}
// Release DDE handle
if ( ddeId != 0) {
DdeUninitialize( ddeId );
}
}
namespace {
bool
getIsFile( std::string const & fileName )
{
DWORD const attr = ::GetFileAttributes( fileName.c_str() );
return
attr != INVALID_FILE_ATTRIBUTES &&
( attr & FILE_ATTRIBUTE_DIRECTORY ) != FILE_ATTRIBUTE_DIRECTORY;
}
}
void
AcrobatReader::openDoc( std::string const & filename )
{
// Remember the file name
file = filename;
// Check whether the file exists
if( !getIsFile( filename ) ) {
std::string message = "File '" + filename + "' not found";
MessageBox(
::AfxGetMainWnd()->GetSafeHwnd(),
message.c_str(),
appName.c_str(),
MB_OK | MB_ICONINFORMATION
);
return;
}
// Make sure a file is not open already
std::string cmd = "[DocClose(\"" + file + "\")]";
sendDdeMsg( cmd );
::Sleep( 500 );
// We have to Open a document before we can do anything with it
cmd = "[DocOpen(\"" + file + "\")]";
if ( ! sendDdeMsg( cmd ) ) {
reportDdeError( "Cannot open " + file );
} else {
// If the pdf file is password protected then Adobe Reader might crash
without this!
// At least when the file is opened the second time
// guess it resets some state or something....
cmd = ( "[dummy(\"" + file + "\")]" );
sendDdeMsg( cmd );
}
}
namespace {
std::string
fromInt(int d, char const * spec)
{
enum { buffer_size = 50 };
char buffer[ buffer_size ];
_snprintf(buffer, buffer_size, spec, d );
return buffer;
}
}
void
AcrobatReader::gotoPage( int page )
{
// Construct the command to open the document
std::string cmd ( "[DocGoTo(\"" + file + "\"," + fromInt( page, "%d" )
+ ")]" );
if ( ! sendDdeMsg( cmd ) ) {
reportDdeError( "Cannot goto page " + (fromInt( page, "%d" )) );
}
}
void
AcrobatReader::gotoName( std::string const & name )
{
// Construct the command to open the document
std::string cmd ( "[DocGoToNameDest(\"" + file + "\"," + name + ")]" );
if ( ! sendDdeMsg( cmd ) ) {
reportDdeError( "Cannot goto destination " + name );
}
}