Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / MFC

Using the Free CutePDF Writer without User Intervention

Rate me:
Please Sign up or sign in to vote.
4.85/5 (14 votes)
7 Dec 2009CPOL4 min read 81.9K   4.4K   47   7
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.

C++
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.

C++
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.

C++
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)


Written By
President
Canada Canada
Father of two, brother of two, child of two.
Spouse to one, uncle to many, friend to lots.
Farmer, carpenter, mechanic, electrician, but definitely not a plumber.
Likes walks with the wife, board games, card games, travel, and camping in the summer.
High school graduate, college drop-out.
Hobby programmer who knows C++ with MFC and the STL.
Has dabbled with BASIC, Pascal, Fortran, COBOL, C#, SQL, ASM, and HTML.
Realized long ago that programming is fun when there is nobody pressuring you with schedules and timelines.

Comments and Discussions

 
Questionhow will i use this in my asp.net c# web application Pin
aassaahh30-Sep-14 1:13
aassaahh30-Sep-14 1:13 
QuestionCutePDF Pin
bpd197715-Feb-13 1:20
bpd197715-Feb-13 1:20 
GeneralExcellent Pin
ColinDavies6-Sep-10 14:13
ColinDavies6-Sep-10 14:13 
GeneralRe: Excellent [modified] Pin
T21021-Jan-11 23:12
T21021-Jan-11 23:12 
GeneralNeed Help Pin
Mehunter4823-Feb-10 11:58
Mehunter4823-Feb-10 11:58 
Your code does just what I need; however, I am not a programmer. I ran the demo and now have my CuteWriter files going to C:\My_PDF_Folder.

How can I change the folder location?

I was hoping I could just a change the location in the demo files and rerun the exe. But that did not work.

Like I said, I am not a programmer.

Appreciate any help you can give.

Michael
GeneralGood work! Pin
xliqz7-Dec-09 23:09
xliqz7-Dec-09 23:09 
Generalgreat ! Pin
Alexandre GRANVAUD7-Dec-09 20:07
Alexandre GRANVAUD7-Dec-09 20:07 

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

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