Click here to Skip to main content
15,900,461 members
Please Sign up or sign in to vote.
1.00/5 (3 votes)
See more:
OK I really need some help here! I am not the best C programmer out there but I can usually find my way through. But this one has me stumped. The company I work for has been using an open source program to print PDF file from our accounting software for about 6 years. The program was originally written in V$C 6.0. Now in Abode's infinate wisdome they decided to change the name of the function that was used in the program when Adobe Reader 10.x was released. (I understand their reason, but...) I found and downloaded the program and I imported the program into a VS 2005 C project. I was able to modify it to work with Adobe 10.x but it would not work for Adobe 9.x or below. I did some research and found that Adobe was adding the application name in the Windows Registry so all I had to do was read the registry key and be done with it. Now after working on this for 2 days, I am frustrated that I can not get this to program to compile. I do not know what I am missing and any help would be MUCH appreciated! In the code below you can search on "adobeversion" and you can see what I am trying to do. I am trying to use the RegistryKey class but I get compile errors. I know this is long but I felt it would be better to see the entire program.

Thanks again in advance!!

C++
#include <windows.h>
#include <ddeml.h>
#include <string.h>
#include <stdio.h>

#include "PrinterUtils.h"

#define TIMEOUT 60

int  multiplier=1;
HWND hWndAdobe=0;
BOOL isRunning=FALSE;
BOOL wasRunning=TRUE;

char* adobeversion=" ";
//char szAdobe[MAX_PATH];
//HKEY hKey=0;
//char regbuf[255]="";
//DWORD dwType=0;
//DWORD dwBufSize=sizeof(regbuf);
const char* subkey="acrobat\\shell\\open\\ddeexec\\application";


// Try to find the version of Adobe installed on the workstation
/*char getPDFVersion()
{
	 if( RegOpenKey(HKEY_CLASSES_ROOT, subkey, &hKey) == ERROR_SUCCESS)
	{
		dwType = REG_SZ;
		if ( RegQueryValueEx(hKey, "(Default)", 0, &dwType, (BYTE*)regbuf, &dwBufSize) == ERROR_SUCCESS)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	else
	{
		return 0;
	}
} */

// Hide Adobe Window 
// Adobe Reader 7 always pops up when printing, ignores SW_HIDE, so hide it offscreen
void HideAdobe() 
{
	ShowWindow (hWndAdobe, SW_RESTORE);
	MoveWindow (hWndAdobe, -100, -100, 0, 0, TRUE);
	ShowWindow (hWndAdobe, SW_RESTORE);
}


// Determine if Acrobat or Reader is running
BOOL isAdobeRunning()
{
	// Search all windows for title starting with szWnd
    // hWndAdobe = FindWindow("AdobeAcrobat", NULL);
    hWndAdobe = FindWindow(NULL, "Adobe Reader");
    isRunning = (hWndAdobe > 0 ? TRUE: FALSE);
	return(isRunning);
}

void usage(char *p) 
{
	printf("usage:\t%s [options] [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\n");
	printf("options:\n");
	printf("\t-p printer : print to specified printer (must be first option)\n");
	printf("\t\tSpecify complete printer UNC name for network printers:\n");
	printf("\t\t\t\"\\\\printserver\\printer\"\n");
	printf("\t\t\tsurround with double-quotes if any spaces in printer name\n\n");
	printf("\t-o orientation : set orientation\n");
	printf("\t\t1=portrait, 2=landscape\n\n");
	printf("\t-d duplex : set duplex mode (if supported by printer)\n");
	printf("\t\t1=none, 2=long side, 3=short side\n\n");
	printf("\t-c copies : set number of copies to print\n");
	printf("\t\tcopies=1 to 10\n");
	exit(0);
}

// Callback function for DDE messages
HDDEDATA CALLBACK DDECallback(	UINT wType, 
				UINT wFmt, 
				HCONV hConv,
				HSZ hsz1, 
				HSZ hsz2, 
				HDDEDATA hDDEData, 
				DWORD dwData1, 
				DWORD dwData2)
{
	switch (wType) {

	default:
		return NULL;
		break;
	}
}


// Send a DDE execute request to Acrobat/Reader 
static BOOL SendExecCmd(LPSTR lpszCmd)
{
	int test=0;
	DWORD dwDDEInst = 0;
	UINT ui;
	HSZ hszApplication;
	HSZ hszTopic;
	HCONV hConv;
	HDDEDATA hExecData;
	
	// Initialize DDEML
	ui = DdeInitialize(&dwDDEInst,
			DDECallback,
			CBF_FAIL_ALLSVRXACTIONS,
			0l);

	if (ui != DMLERR_NO_ERROR) {
		return FALSE;
	}

	// Initiate a conversation with the acroview application
	// on the control topic.
	
/* Jim Whalen - 11-10-2011 The release of Reader 10.X does not work with this application
                           Found we need to change the commad acroview to acroviewR10
*/
/*	hszApplication = DdeCreateStringHandle( dwDDEInst,
						"acroview",
						CP_WINANSI);
*/
	//test=getPDFVersion();
	//if (test != 1)
	//    return FALSE;

	RegistryKey reg = (Registry.ClassesRoot).OpenSubKey(subkey);
	adobeversion = reg.GetValue("(Default)");

	hszApplication = DdeCreateStringHandle(	dwDDEInst,
						adobeversion,
						CP_WINANSI);

	hszTopic = DdeCreateStringHandle(dwDDEInst,
					"control",
					CP_WINANSI);

	hConv = DdeConnect(dwDDEInst,
			hszApplication,
			hszTopic,
			NULL);

	// Free the HSZ now
	DdeFreeStringHandle(dwDDEInst, hszApplication);
	DdeFreeStringHandle(dwDDEInst, hszTopic);

	if (!hConv) {
		return FALSE;
	}

	// Create a data handle for the exec string
	hExecData = DdeCreateDataHandle(dwDDEInst,
					lpszCmd,
					lstrlen(lpszCmd)+1,
					0,
					NULL,
					0,
					0);

	// Send the execute request
	DdeClientTransaction(	(void FAR *)hExecData,
				(DWORD)-1,
				hConv,
				NULL,
				0,
				XTYP_EXECUTE,
				TIMEOUT * 1000 * multiplier, // ms timeout
				NULL);

	// Done with the conversation now.
	DdeDisconnect(hConv);

	// Done with DDEML
	DdeUninitialize(dwDDEInst);

	return TRUE;
}

BOOL pdfprint(LPSTR lpszPath, LPSTR lpszPrinter, LPSTR lpszPort, LPSTR lpszDriver)
{
	char buf[2048];
	int i;
	BOOL ret;
	FILE *f;
	long lSize;

	// Find the file's length
	f = fopen(lpszPath, "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;

	if(lpszPath && lstrlen(lpszPath)) {
		if(lpszPrinter && lstrlen(lpszPrinter))
        {
			sprintf(buf,
				"[FilePrintTo(\"%s\",%s,\"%s\",\"%s\")]",
				lpszPath, lpszPrinter, lpszDriver, lpszPort);
        }
		else
        {
            sprintf(buf,
                    "[FilePrintSilent(\"%s\")]",
                    lpszPath);
        }
	}

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

	for(i=0;i<3;i++)
	{
		HideAdobe();
		ret = SendExecCmd(buf);
		if(ret)
			break;

		Sleep(1000);
	}

	//HideAdobe();
	return(ret);
}


int main(int argc, char* argv[])
{
	char szPrinter[MAX_PATH];
    char szPrinterName[MAX_PATH];
	char szPort[MAX_PATH];
	char szDriver[MAX_PATH];
	char szFile[MAX_PATH];
	char szPath[MAX_PATH];
	char *p;
	int i;
	int s=1;
	int nPrinted=0;	
	int val=0;
	HANDLE hPrinter=0;
	WIN32_FIND_DATA find_data;
	HANDLE hFind;
    PRINTER_INFO_2 *ppi2 = NULL;
    LPDEVMODE pDevMode1 = NULL;
	LPDEVMODE pDevMode2 = NULL;
	BOOL bSetProperties = FALSE;
	DWORD dwNeeded = 0;
	BOOL bFlag;
	char *pattern;
	BOOL bFirst = TRUE;
	LPTSTR lpFilePart;
	DWORD dwStatus;

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

	*szPrinter='\0';
    *szPrinterName='\0';

	// Get default printer name
	GetDefaultPrinterName(szPrinterName, sizeof(szPrinterName));

	// Get default printer settings
	pDevMode1 = GetPrinterDevMode(szPrinterName);
	pDevMode2 = GetPrinterDevMode(szPrinterName);

	
	for(i=1;i<argc;i++)>
	{
		p = argv[i];

		if(*p == '-')
		{
			if(++i >= argc)
				usage(argv[0]);

			switch (*(++p)) {
				case 'p' :

					if(i != 2) {
						// -p must be first option, if used
						printf("-p MUST be first option, if used\n");
						usage(argv[0]);
					}

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

					// Find out how much space is needed
					GetPrinter(hPrinter, 2, 0, 0, &dwNeeded);
					if (dwNeeded == 0)
					{
						printf("Printer error\n");
						ClosePrinter(hPrinter);
						return 2;
					}

					// Allocate enough space for PRINTER_INFO_2...
					ppi2 = (PRINTER_INFO_2 *)GlobalAlloc(GPTR, dwNeeded);
					if (!ppi2)
					{
						printf("Malloc error\n");
						ClosePrinter(hPrinter);
						return 3;
					}

					// The second GetPrinter() will fill in all the current information
					// so that all you need to do is modify what you're interested in...
					bFlag = GetPrinter(hPrinter, 2, (LPBYTE)ppi2, dwNeeded, &dwNeeded);
					if (!bFlag)
					{
						printf("Printer error\n");
						ClosePrinter(hPrinter);
						GlobalFree(ppi2);
						return 4;
					}

					strcpy(szPort, ppi2->pPortName );
					strcpy(szDriver, ppi2->pDriverName);
					strcpy(szPrinterName, argv[i]);

					ClosePrinter(hPrinter);

					if(pDevMode1)
						free(pDevMode1);

					if(pDevMode2)
						free(pDevMode2);

					// Get default printer settings
					pDevMode1 = GetPrinterDevMode(szPrinterName);
					pDevMode2 = GetPrinterDevMode(szPrinterName);

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

					continue;

				case 'd' :
					// Set duplex mode
					p = argv[i];
					if(!isdigit(*p)) {
						printf("Invalid Duplex Mode: %s\n", p);
					}
					else {
						val = atoi(p);
						if(val < 1 || val > 3)
							usage(argv[0]);

						pDevMode2->dmDuplex = val;
						pDevMode2->dmFields |= DM_DUPLEX;
						pDevMode1->dmFields |= DM_DUPLEX;
						bSetProperties = TRUE;
					}

					continue;
				case 'o' :
					// Set Orientation
					p = argv[i];
					if(!isdigit(*p)) {
						printf("Invalid Orientation Mode: %s\n", p);
					}
					else {
						val = atoi(p);
						if(val < 1 || val > 2)
							usage(argv[0]);

						pDevMode2->dmOrientation = val;
						pDevMode2->dmFields |= DM_ORIENTATION;
						pDevMode1->dmFields |= DM_ORIENTATION;
						bSetProperties = TRUE;
					}

					continue;
				case 'c' :
					// Set Copies
					p = argv[i];
					if(!isdigit(*p)) {
						printf("Invalid Number of Copies: %s\n", p);
					}
					else {
						val = atoi(p);
						if(val < 1 || val > 10)
							usage(argv[0]);

						pDevMode2->dmCopies = val;
						pDevMode2->dmFields |= DM_COPIES;
						pDevMode1->dmFields |= DM_COPIES;
						bSetProperties = TRUE;
					}

					continue;

/*  This doesn't appear to be possible...
				case 'n' :
					// Set N-Up
					p = argv[i];
					if(!isdigit(*p)) {
						printf("Invalid N-Up setting: %s\n", p);
					}
					else {
						val = atoi(p);
						if(val != 1 && val != 2 && val != 4 && val != 6 && val != 9)
							usage(argv[0]);

						pDevMode2->dmScale = (100 / val);
						printf("Scale:%d\n", pDevMode2->dmScale);
						pDevMode2->dmFields |= DM_SCALE;
						pDevMode1->dmFields |= DM_SCALE;
						bSetProperties = TRUE;
					}

					continue;
*/

				default	 :
					continue;
			}

		}

		if(bSetProperties)
			SetPrinterProperties(szPrinterName, pDevMode2);

		// 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);
				dwStatus = GetFullPathName(szPath,
							MAX_PATH,
							szPath,
							&lpFilePart);

				// Get the path to the executable 
				ZeroMemory (szFile, MAX_PATH);
				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;
				}

				bFirst = FALSE;
				// Set flags if Acrobat/Reader already running
				//isAdobeRunning();
				if(!isAdobeRunning())
				{
					wasRunning=FALSE;
                    ShellExecute(NULL,"open",szFile,NULL,NULL,SW_HIDE);

					Sleep(3000);

					HideAdobe();
					
					if(!isAdobeRunning())
						Sleep(3000);

					HideAdobe();
				}

				// Tell Acrobat/Reader to print the file
				if(pdfprint(szPath, szPrinter, szPort, szDriver))
					nPrinted++;

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

		FindClose(hFind);
	}


	// If Acrobat/Reader wasn't running when we started, terminate it
	if(!wasRunning)
	{
		if(isAdobeRunning()) {
			Sleep(1000);
			// Ask Acrobat/Reader to close
			PostMessage (hWndAdobe, WM_CLOSE, 0, 0); 
			//PostQuitMessage(hWndAdobe, WM_QUIT);
		
			//SendExecCmd("[AppExit()]");
			//Sleep(3000);
		}

	}	

	// Reset printer defaults
	if(bSetProperties)
		SetPrinterProperties(szPrinterName, pDevMode1);

	// Cleanup
	if(pDevMode1)
		free(pDevMode1);

	if(pDevMode2)
		free(pDevMode2);

	printf("%d File(s) Printed\n", nPrinted);	
	return(nPrinted);
}
</filename>
Posted
Updated 24-Nov-11 21:52pm
v3
Comments
Richard MacCutchan 23-Nov-11 16:34pm    
You could at least point us to the failing line and show the compiler error message.
Albert Holguin 23-Nov-11 16:46pm    
I second Richard, can't help without errors from compiler.
tazenman 24-Nov-11 11:39am    
Richard and Albert you are right! Sorry for the lack of information! The first error happens on this line;
RegistryKey reg = (Registry.ClassesRoot).OpenSubKey(subkey);
error C2065: 'RegistryKey':undeclared identifier

There are other errors but it starts with this one.

Thanks again for your help!
Richard MacCutchan 25-Nov-11 4:24am    
RegistryKey is a .NET class so you cannot use it in a C program. You need to use the Win32 registry functions as described here.

Just trying to read between the lines here, so please bear with me...

I note your reference to C and your programming abilities, I then notice a word that gets my attention - 'class'. Hmm, is this an attempt to use a class from a C program I wonder?

I checked MSDN here[^], finding that the RegistryKey class appears to be for .NET

Also, the reference to "Registry.ClassesRoot" made me suspicious that code was being mixed from both C and .NET C++.

Given that you appear to be using plain win32 api functions, I'd suggest a look at the Registry functions for win32 at msdn. They're over here[^].
 
Share this answer
 
This is the sample code at :
MSDN RegistryKey.GetValue Method[^]

Just by looking at this code You will notice few things missing from Your code + some of the Methods are obsolete , depending what versions of NET/compiler . .. you are using ..

C++
using namespace System;
using namespace Microsoft::Win32;

public ref class RegGetDef
{
public:
    static void Main()
    {
        // Create a reference to a valid key.  In order for this code to
        // work, the indicated key must have been created previously.
        // The key name is not case-sensitive.
        RegistryKey^ rk = Registry::LocalMachine->OpenSubKey("Software\\myTestKey", false);
        // Get the value from the specified name/value pair in the key.
        String^ valueName = "myTestValue";

        Console::WriteLine("Retrieving registry value ...");
        Console::WriteLine();
        Object^ o = rk->GetValue(valueName);
        Console::WriteLine("Object Type = " + o->GetType()->FullName);
        Console::WriteLine();
        switch (rk->GetValueKind(valueName))
        {
            case RegistryValueKind::String:
            case RegistryValueKind::ExpandString:
                Console::WriteLine("Value = " + o);
                break;
            case RegistryValueKind::Binary:
                for each (Byte^ b in (array<byte^>^)o)
                {
                    Console::Write("{0:x2} ", b);
                }
                Console::WriteLine();
                break;
            case RegistryValueKind::DWord:
                Console::WriteLine("Value = " + Convert::ToString((Int32^)o));
                break;
            case RegistryValueKind::QWord:
                Console::WriteLine("Value = " + Convert::ToString((Int64^)o));
                break;
            case RegistryValueKind::MultiString:
                for each (String^ s in (array<string^>^)o)
                {
                    Console::Write("[{0:s}], ", s);
                }
                Console::WriteLine();
                break;
            default:
                Console::WriteLine("Value = (Unknown)");
                break;
        }

        // Attempt to retrieve a value that does not exist; the specified
        // default value is returned.
        String^ def = (String^)rk->GetValue("notavalue", "The default to return");
        Console::WriteLine();
        Console::WriteLine(def);

        rk->Close();
    }
};

int main()
{
    RegGetDef::Main();
}
/*
Output:
Retrieving registry value ...

Object Type = System.String

Value = testData

The default to return
*/
 
Share this answer
 
See here basic samples how to read/write Registry in Windows APIs: Read/Write in Registry[^]
 
Share this answer
 
Thanks to all that applied and for your patience with me! All of you ROCK! Sergey thanks for the link to example, that is what helped me get through this to get this application working! Also I did not realize importing this C++ program into VS2005 would not convert this from Win32API to .Net, that is one thing that was throwing me off as well.

Thanks again!!!!!
 
Share this answer
 

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900