Page 1 of 1

Help with some C++ printer code...

Posted: Sun May 02, 2004 9:34 pm
by Karbon
I apologize for posting all of this but I am trying to understand exactly how it works and use the same method in PB..

I'm trying to batch print PDF files, something that this code will do if compiled but I'm failing to understand how it works..

It looks like it opens Acrobat Reader and uses a print "verb". I thought that was just something you could do to print text file on the command line (issue "print myfile.txt" - but if I do that on the command line here it printes the contents of the PDF, not the rendered PDF file. This code does seem to work, so if anyone can give me a hand porting it to PB and understanding how that ShellExecute is working I'd really really appreciate it!!!

Code: Select all

#include <stdio.h>
#include <windows.h>
#include <winspool.h>

#define TIMEOUT 60

int  multiplier=1;
HANDLE hProcessAdobe=0;
HWND hWndAdobe=0;
BOOL isRunning = FALSE;
BOOL wasRunning = TRUE;
char szAdobe[MAX_PATH];

// Callback for isAdobeRunning
BOOL CALLBACK EnumCallBack(HWND hwnd, LPARAM lParam)
{
	char s[MAX_PATH];
  
	// If window title contains match string
	GetWindowText (hwnd, s, MAX_PATH);
	if (strstr(s, (char *)lParam)) {
		// Set the hWnd
		hWndAdobe=hwnd;
		// Set our flag
		isRunning = TRUE;
		return FALSE;
	}
	else
	{
		isRunning = FALSE;
		return TRUE;
	}

}

// Determine if Acrobat or Reader is running
BOOL isAdobeRunning()
{
	// Search all windows for title starting with szWnd
	EnumWindows (EnumCallBack, (LPARAM)szAdobe);
	return(isRunning);
}

void usage(char *p) 
{
	printf("usage:\t%s [-p printer] [drive:][path]<filename>\n", p);
	printf("Specifies drive, directory, and/or file(s) to print.\n");
	printf("Prints to default printer unless [printer] specified.\n");
}

int pdfprint(char* fname, char* szAdobe, char* szPrinter)
{
	DWORD size=1024;
	SHELLEXECUTEINFO ei;
	DWORD lpExitCode;
	FILE *f;
	int i;
	int printed=0;
	long lSize;

	// Find the file's length
	f = fopen(fname, "r");
	if(f == NULL)
		return 0;

	fseek(f,0,SEEK_END);
	lSize=ftell(f);
	fclose(f);

	// Extend the timeout for each MB of filesize
	multiplier = (int)(lSize / 1000000)+1;

	// Set up the ShellExecute parameters
	ZeroMemory( &ei, sizeof(ei) );
	ei.cbSize = sizeof(ei);
	ei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS;
	ei.nShow = SW_HIDE;

	// Open Acrobat/Reader if it's not running
	if(!isRunning)
	{
		wasRunning=FALSE;

		ei.lpVerb = "open";
		ei.lpFile = fname;
		ei.lpParameters = NULL;
		
		ShellExecuteEx(&ei);
		hProcessAdobe = ei.hProcess;

		for(i=0;i<TIMEOUT;i++)
		{
			Sleep(1000);
			if(isAdobeRunning())
				break;
		}
	}
	
	// If Printer was specified on command line
	if(strlen(szPrinter)>0) 
	{
		// Set verb and printer name
		ei.lpVerb = "printto";
		ei.lpParameters = szPrinter;
	}
	else
		// Otherwise, use default printer
		ei.lpVerb = "print";

	printf("Printing: '%s'\n", fname);

	// Start the print job
	ei.lpFile = fname;
	ShellExecuteEx (&ei);

	// Wait for print job to complete
	for(i=0;i<(TIMEOUT*multiplier);i++)
	{
		GetExitCodeProcess(ei.hProcess, &lpExitCode);
		if(lpExitCode!=STILL_ACTIVE)
		{
			printed = 1;
			break;
		}

		Sleep(1000);
	}

	// If timeout
	if(printed==0)
		printf("Timeout\n");

	return printed;
}

int main(int argc, char* argv[])
{
	char szPrinter[MAX_PATH];
	char szFile[MAX_PATH];
	char szPath[MAX_PATH];
	char *p;
	int i;
	int s=1;
	int nPrinted=0;	
	HANDLE hPrinter=0;
    WIN32_FIND_DATA find_data;
    HANDLE hFind;
	char *pattern;

	if (argc < 2)
	{
		usage(argv[0]);
		return 0;
	}

	*szPrinter='\0';

	// Check for printer 
	if( (stricmp(argv[1], "-p")==0) || (stricmp(argv[1],"/p")==0) )
	{
		s=3;
		strcpy(szPrinter, argv[2]);
		
		// Make sure printer name is valid
		if(OpenPrinter(szPrinter, &hPrinter, NULL)==0)
		{
			// If not valid, return
			printf("Printer not found: '%s'\n", szPrinter);
			return 0;
		}

		ClosePrinter(hPrinter);

		// Wrap printer name in double quotes to pass as arg
		// in case there are spaces in the name
		sprintf(szPrinter, "\"%s\"", argv[2]);
		printf("Printer: %s\n", szPrinter);

	}

	for(i=s;i<argc;i++)
	{

		// Expand any wildcards
		pattern = argv[i];
		hFind = FindFirstFile(pattern, &find_data);
		if (hFind != INVALID_HANDLE_VALUE)
		{
			do {

				// Construct full path to file
				strcpy(szPath, argv[i]);
				p = strrchr(szPath, '\\');
				if(p == NULL)
					*szPath = '\0';
				else
					*(p+1) = '\0';

				strcat(szPath, find_data.cFileName);

				// Get the path to the executable 
				ZeroMemory (szFile, MAX_PATH);

				// Make sure Adobe Acrobat/Reader is installed	
				FindExecutable(szPath, NULL, szFile);
				if (!szFile[0])
				{
					printf("'%s' not found or Adobe Acrobat or Reader not installed.\n", szPath);
					continue;
				}

				// Make sure it's a PDF file
				p = strrchr(szPath,'.');
				if(p==NULL)
				{
					printf("Not a PDF file: '%s'\n", szPath);
					continue;
				}

				if( (stricmp(p,".pdf")!=0) && (stricmp(p,".fdf")!=0) && (stricmp(p,".xfdf")!=0) )
				{
					printf("Not a PDF file: '%s'\n", szPath);	
					continue;
				}


				// Determine whether system uses Reader or Acrobat for PDF
				if (strstr(szFile, "AcroRd32"))
					strcpy (szAdobe, "Adobe Reader");
				else
					strcpy (szAdobe, "Adobe Acrobat");

				// Set flags if Acrobat/Reader already running
				isAdobeRunning();

				// Tell Acrobat/Reader to print the file
				nPrinted += pdfprint(szPath, szAdobe, szPrinter);

			} while (FindNextFile(hFind, &find_data));
		}

		FindClose(hFind);
	}

	// If Acrobat/Reader wasn't running when we started, terminate it
	if(!wasRunning)
	{
		if(hProcessAdobe)
		{
			// Politely ask Acrobat/Reader to quit
			for(i=0;i<10;i++)
			{
				if(isAdobeRunning())
					SendMessage (hWndAdobe, WM_CLOSE, 0, 0);
				else
					break;

				Sleep(1000);
			}

			// If it's still running, time to get rude
			if(isAdobeRunning())
				TerminateProcess(hProcessAdobe, 0);
		}
	}	

	printf("%d File(s) Printed\n", nPrinted);	
	return(nPrinted);
}

Posted: Sun May 02, 2004 9:42 pm
by Karbon
Actually, the confusion is clearing.. I think I might have it - just have to polish my winapi skills a bit..

Posted: Mon May 03, 2004 9:06 am
by fweil
Karbon,

Here is wheere I am now ...

Just it seems that I can't catch the exit code back from Acrobat, and it does not work well as it should ATM.

In the following code you have the possibility to retrieve if Acrobat is running or not, to launch it if it was not running and close it at the end, or just open the document if it was running and let it alive.

Also you have the translation of all parameters and structured information if I did not forget any.

What your posted code does is also to enum a set of files, as far as I understand this, which is not listed in my reply but it should not be hard to add.

Let me know if this helps ...

Code: Select all

#STILL_ACTIVE = $103
Timeout = 60

Global hWndAdobe.l

Procedure SeekForAdobe_Acrobat_Reader_Running(hwnd.l, lParam.l)
    Ret.l = GetWindowTextLength_(hwnd)
    sSave.s = Space(Ret)
    GetWindowText_(hwnd, sSave, Ret + 1)
    If FindString(sSave, "Adobe Reader", 1)
        hWndAdobe = hWnd
    EndIf
    ProcedureReturn #True
EndProcedure

  EnumWindows_(@SeekForAdobe_Acrobat_Reader_Running(), 0)
  
  
  Debug hWndAdobe
  File.s = "REGISTER.PDF"
  FileSize = FileSize(File)
  multiplier = (lSize / 1000000)+1
  Printer.s = "Primo PDF" ; Here a given printer name declared in your station / server

  If OpenPrinter_(@Printer, @hPrinter, #SW_HIDE)
      Debug "Printer found"
    Else
      Debug "Printer not found"
      Printer = "Canon S200" ; Here your default printer
  EndIf
  ClosePrinter_(hPrinter)
  
  If Printer = ""
      Printer = "print"
    Else
      Printer = "printto " + Printer
  EndIf

   ei.SHELLEXECUTEINFO
   ei\cbSize = SizeOf(SHELLEXECUTEINFO)
   ei\fMask = #SEE_MASK_FLAG_DDEWAIT | #SEE_MASK_NOCLOSEPROCESS
   ei\nShow = #SW_HIDE

  ShellExecute_(0, "open", File, Printer, "", 0)

  printed = #FALSE

  For i = 0 To Timeout * multiplier
    Delay(1000)
    If GetExitCodeProcess_(hWndAdobe, @ExitCode)
        If ExitCode <> #STILL_ACTIVE
            Debug "Printed"
            printed = #TRUE
            Break
        EndIf
    EndIf
  Next
  
  If printed = #FALSE
      Debug "Timeout"
  EndIf
  
  If hWndAdobe = 0 ; Adobe was not running at program start
      Debug "Acrobat was not running first : try to close it"
      EnumWindows_(@SeekForAdobe_Acrobat_Reader_Running(), 0)
      SendMessage_(hWndAdobe, #WM_CLOSE, 0, 0)
      Delay(1000)
      EnumWindows_(@SeekForAdobe_Acrobat_Reader_Running(), 0)
      If hWndAdobe <> 0
          Debug "Acrobat was not running first but still runs : try to kill it"
          TerminateProcess_(hWndAdobe, 0)
      EndIf
    Else
      Debug "Adobe was running at start : let it alive"
  EndIf
End
Rgrds

Posted: Mon May 03, 2004 1:03 pm
by Karbon
Wow. Thank you very much!

Posted: Mon May 03, 2004 1:52 pm
by dell_jockey
Mitch,

back in the old days, we used to call the code you posted 'C', not C++ :D

Posted: Mon May 03, 2004 1:58 pm
by Karbon
Yeah, you're right.. This is the C code, I was looking at some C++ code too and got all confused..

I was more ignorant than usual yesterday - got this all straightened out now :-)