Click here to Skip to main content
Click here to Skip to main content

Using the Free CutePDF Writer without User Intervention

, 7 Dec 2009
Rate this:
Please Sign up or sign in to vote.
A C++/MFC class that allows you to use the free CutePDF Writer without user intervention

Introduction

This article presents a class that is a hack around a shortcoming in the free CutePDF Writer. The shortcoming is that the "Save As" dialog always pops up so it is impossible to print to a PDF file without user interaction. This class makes it possible to use the free CutePDF Writer without user interaction.

Background

I have a small app that is scheduled to run every morning at 5:00 AM. It's job is to print out a summary report of the previous days activities. I used to have it print out on paper, but I found that to be a waste of paper as I would usually only read it once and then toss it. So I decided to it would be better to simply print to a PDF file that I could make a hard copy of if I needed one. I already had the CutePDF Writer installed on my system, so it was only natural that I use it.

My first attempt, which worked fairly well, was to simply find the "Save As" dialog using the FindWindow API, and simulating a click on the Save button by posting a BN_CLICKED command to the dialog. But this approach had several problems including the unreliability of FindWindow, and what would happen if another application also happened to have a Save As dialog open at the same time.

The main problem I had though was specifing the folder that the CutePDF Writer was going to save the PDF file to. All was well as long as no other app also used the CutePDF Writer to print a PDF file as it always defaulted the folder in the Save As dialog to the last folder used by any app. I then discovered that the last folder used was saved in the Windows registry, and I realized that by changing that registry value to point to the folder where I wanted to save my PDF file the Save As dialog would use the folder I wanted, not the last one used by some other random application.

I also tried to fix the problem I had with actually finding the proper "Save As" dialog. The dialog is not a child of the calling application, but instead is a child of the CPWSave.exe application. So I used the EnumWindows API to list all the top level Windows on the system. In the callback function, I first check if the top level window is a dialog by checking if the class name is "#32770" which is the class name for dialogs. If it is, then I check if the dialog is owned by the CPWSave.exe program. I could probably add a few more checks, but at this point I can be fairly certain that the window found is the proper Save As dialog.

BOOL CALLBACK CCutePDFWriter::GetSaveAsDialogProc(HWND hWnd, LPARAM lp)
{
    BOOL result = TRUE;
    TCHAR Buffer[MAX_PATH + 1] = {0};

    GetClassName(hWnd, Buffer, _countof(Buffer));
    // is this window a dialog?
    if (_tcsicmp(Buffer, DIALOG_CLASS_NAME) == 0)
    {
        DWORD ProcessID = 0;
        // Get the ID of the app that owns this dialog
        GetWindowThreadProcessId(hWnd, &ProcessID);
        HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION
                                       | PROCESS_VM_READ
                                       , FALSE
                                       , ProcessID);
        if (NULL != hProcess)
        {
            // get the name of the exe file
            GetModuleBaseName(hProcess, NULL, Buffer, _countof(Buffer));
            // was this dialog created by the CPWSave program?
            if (_tcsicmp(Buffer, EXE_FILE_NAME) == 0)
            {
                datastruct *dsp = (datastruct*)lp;
                dsp->hWnd = hWnd;
                dsp->ProcessID = ProcessID;
                result = FALSE;
            }

            CloseHandle(hProcess);
        }
    }

    return result;
}

I also wanted to address what would happen if another application was already using the CutePDF Writer and was waiting for a user to click on the Save button of an active dialog. My solution was to look for an active dialog in the class constructor, and if one was found simply wait for it to close. This is not the ideal solution as it will make the application that is using this class appear to hang.

CCutePDFWriter::CCutePDFWriter(void)
: SavedDC(0)
, CPWProcessID(0)
{
    // Check if the CutePDF Writer is already processing a file
    HWND hWnd = GetSaveAsDialog();
    if (NULL != hWnd)
    {
        // if it is then we wait for it to finish before we proceed
        HANDLE ProcessHandle = OpenProcess(SYNCHRONIZE, FALSE, CPWProcessID);
        WaitForSingleObject(ProcessHandle, INFINITE);
        CloseHandle(ProcessHandle);

Using the Code

To use the code, simply declare a CCutePDFWriter class object, call its GetDC(LPCTSTR Folder) method to get the PDF printer device context. You specify the complete path to the folder that the PDF file will be saved in the GetDC call. If the folder does not exist, it will be created via a call to SHCreateDirectoryEx. If GetDC returns NULL, you can find out why by calling GetLastError(). The actual name of the file is specified when you call the StartDoc() function. When you are finished printing the PDF file, you call the ReleaseDC() method or let the CCutePDFWriter object go out of scope.

CCutePDFWriter PDF_Printer;
CDC *pDC = PDF_Printer.GetDC(_T("C:\\My_PDF_Folder\\"));
 
pDC->StartDoc(_T("My_PDF_File"));
pDC->StartPage();
 
pDC->Ellipse(100, 200, 300, 400);
 
pDC->EndPage();
pDC->EndDoc();
 
PDF_Printer.ReleaseDC();

After running the demo code, you will have a PDF file containing a circle drawn on the top right corner of the page. The file will be C:\My_PDF_Folder\My_PDF_File.pdf.

Shortcomings

This class does have a couple of shortcomings. Among them are the fact that the Save As dialog does still pop up for a brief while, but usually too short of a time for it to bother anyone other than the fact that it grabs the input focus for a bit. The other is my use of the Sleep function to try and avoid race conditions as I try to let the CutePDF writer change registry settings before this code does, rather than after.

History

  • December 7, 2009 - Posted to CodeProject

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

PJ Arends
President
Canada Canada
No Biography provided

Comments and Discussions

 
QuestionCutePDF Pinmemberbpd197715-Feb-13 1:20 
Hi,
I found this article while looking for a way to automate CutePDF. I use LISP for many functions in AutoCAD. My skill level is not very high but I am trying to produce a routine that will plot many layouts to pdf's or a pdf. My question is how does this work?
Do I call the command the then run my routine that will invoke the CutePDF printer or how? How do I pass my file to go to the CutePDf to this command?
Do the Class files for download have something to do with it?
 
I would appreciate your help on this?
 
Thanks.
GeneralExcellent PinmemberColinDavies6-Sep-10 14:13 
GeneralRe: Excellent [modified] PinmemberTed21021-Jan-11 23:12 
GeneralNeed Help PinmemberMehunter4823-Feb-10 11:58 
GeneralGood work! Pinmemberxliqz7-Dec-09 23:09 
Generalgreat ! PinmemberAlexandre GRANVAUD7-Dec-09 20:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 7 Dec 2009
Article Copyright 2009 by PJ Arends
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid