Thread: "Virtual Printer" or "Moving Printjobs"

  1. #1
    Registered User
    Join Date
    Oct 2008
    Posts
    4

    "Virtual Printer" or "Moving Printjobs"

    Hi,

    my company is using an application that runs on a Windows 20003 Server. It is written in Java. There is a configuration file where a printer name can specified - no printer dialog is shown at all, only this printer is used.

    And here is my problem: The program is used by three different persons - one is using the softare directly logged in on the server and the other two are connected using the MS Terminal Server Client.
    They are all located on different locations and have their own printer. Additionally they are working (and printing) with that software at the same time.

    This is really annyoing for them, so I want to develop a solution for them. I would really prefer a quick solution and also prefer to use a .NET language. If it can only be realize using C++, this will be possible as well.

    These are the two ways I think it can be realized (maybe you know another one?)
    • Creating a virtual printer or a printer port just like this one
    • Creating a disabled default printer and then moving the printjobs programatically from the disabled printer to the right one - dependent on the username who created the job


    Maybe the second solution is the easier and safer one? What would you propose?

    Thank you in advance!

  2. #2
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Why didn't you just post this on the C# forum then? I prefer using C# or VB for .NET crap, not C++. My brutally honest opinion that will get me flamed by many is just use VB.

  3. #3
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Creating a disabled default printer and then moving the printjobs programatically from the disabled printer to the right one - dependent on the username who created the job
    A few years ago, I worked on a project very similiar to your second option. It was written in C++ and was a simple project. The project consisted of a networked computer receiving print jobs from a AS400, now the iSeries. The local computer would not immediately print the received jobs but would archive the print jobs to a CD for printing at a later time. The only problem we encountered was when the local computer was rebooted, it mysteriously lost some print jobs. Now we're talking about 2k to 4k of print jobs in a queue at any given time. So, as I said before, we've lost up to 200 print jobs every time the computer was rebooted. We've tried holding the queue prior to rebooting and other possible solutions which did not resolve the problem. We were using Win 2k at the time. So, I'm not sure if this would be a problem under XP and/or Vista. You should verify that this is still not a problem under your selected OS.

    Because of this issue, we opted for a total iSeries solution.

  4. #4
    Registered User
    Join Date
    Oct 2008
    Posts
    4
    Quote Originally Posted by master5001 View Post
    Why didn't you just post this on the C# forum then? I prefer using C# or VB for .NET crap, not C++. My brutally honest opinion that will get me flamed by many is just use VB.
    Oh, I did post on few C# boards, but in fact nobody could help me there. I'm really not afraid of developing unmanaged C++ but having two febrile little childs at home complicates building stable system apps. I hope you gonna understand this..

    Quote Originally Posted by BobS0327 View Post
    A few years ago, I worked on a project very similiar to your second option [...]
    Thank you for your answer. The application isn't that critical. If printjobs might get lost the clerks will just restart the job. The problem is just that one job could contain 300 - 400 pages and if there are singles different pages that are printed by the others inside that large stack they get mad.

    Would it be possible to show me some lines of the code you've used then?

  5. #5
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Would it be possible to show me some lines of the code you've used then?
    Unfortunately, I'm a contractor and I can't remember what company requested this app. I doubt the source would still be available since it wasn't a viable solution from a networked computer perspective due to the missing print jobs.

    But anyway, I will post some fully functional example C# code to help get you started on this task. I'll have to start form scratch. So be patient.

  6. #6
    Registered User
    Join Date
    Oct 2008
    Posts
    4
    thank you so much!

  7. #7
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    But anyway, I will post some fully functional example C# code to help get you started on this task. I'll have to start form scratch. So be patient.
    Well, I lied. My original intention was to write this app in C#. But after realizing that there is a lot of Interop (pinvoking) involved, I chose to write it in plain C since I just don't have a lot of time to devote to this task.

    The following code is about 80% of the project. It is a printer monitor utility. It will first pause the printer and then monitor the printer for any incoming print jobs. The monitor will pause all the individual print jobs received. Note that you can also change the priority of the print job based on the user submitting the print job. The final 20% will consist of adding a few more functions to enumerate the paused print jobs and to submit those print jobs to the appropriate printer. This will be done by first redirecting the print from this queue to another printer. After the redirection is done, the selected print job status will be changed from paused to active and the print queue status will be changed from pause to active. This sequence of events will redirect the print job to the selected printer. The printer will again be paused after the selected print job has completely printed.

    Prerequisites for the above scenario are:
    1. All printers involved are networked and shared
    2. All printers are the same basic type and use the same driver. For example, Lexmark T634 printers using a PCL5 driver.

    I'm posting this code to give you some idea of my direction.

    You are totally responsible for thoroughly testing any posted code.

    The final 20% ( few more functions) will be posted within the next few days as time permits.

    Finally, you can post on the C# forum for assistance in porting this C code to C# once the code is tweaked to meet your requirements.

    Code:
    #pragma comment(lib, "winspool.lib") 
    #include <windows.h>
    #include <stdio.h>
    
    VOID DisplayError(CHAR* pMessage)
    {
    	DWORD dwErrorNum;
    	CHAR szSysMsg[256] = {0};
    	CHAR* pPos;
    	dwErrorNum = GetLastError( );
    	FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    		NULL, dwErrorNum,
    		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    		szSysMsg, 256, NULL );
    	// Trim the end of the line and terminate it with a null
    	pPos = szSysMsg;
    	while((*pPos > 31) || (*pPos == 9))
    		++pPos;
    	do { *pPos-- = 0; } while((pPos >= szSysMsg) &&
    		((*pPos == '.') || (*pPos < 33) )); 
    	printf( "\n%s, error %d (%s)", pMessage, dwErrorNum, szSysMsg );
    }
    
    INT MonitorPrinter(CHAR *pPrinter)
    {
    	int iIndex = 0;
    	DWORD dwRet, dwNeeded, dwChanged = 0;
    	HANDLE hPrinter; 
    	HANDLE hWait;
    	JOB_INFO_2 *pd_JobInfo = NULL;
    
    	PRINTER_DEFAULTS pd;
    	pd.pDatatype = NULL;
    	pd.pDevMode = NULL;
    	pd.DesiredAccess = PRINTER_ALL_ACCESS;
    
    	if (OpenPrinter(pPrinter, &hPrinter, &pd) == 0)
    	{
    		char szTempError[512] = {0};
    		sprintf(szTempError,"Failed to open printer: %s", pPrinter);
    		DisplayError(szTempError);
    		hPrinter = NULL;
    		hWait = NULL;
    	}
    	else
    	{
    		WORD wJobFields[] =
    		{
    			JOB_NOTIFY_FIELD_PRINTER_NAME,
    			JOB_NOTIFY_FIELD_MACHINE_NAME,
    			JOB_NOTIFY_FIELD_PORT_NAME,
    			JOB_NOTIFY_FIELD_USER_NAME,
    			JOB_NOTIFY_FIELD_PAGES_PRINTED,
    			JOB_NOTIFY_FIELD_TOTAL_BYTES,
    			JOB_NOTIFY_FIELD_STATUS,  // the important one
    			JOB_NOTIFY_FIELD_STATUS_STRING
    		};
    		PRINTER_NOTIFY_INFO_DATA PrinterNotifyInfoData;
    		PrinterNotifyInfoData.Type = JOB_NOTIFY_TYPE;
    		PrinterNotifyInfoData.Field = JOB_NOTIFY_FIELD_DOCUMENT;
    		PrinterNotifyInfoData.Id = JOB_NOTIFY_TYPE;
    
    		PRINTER_NOTIFY_INFO_DATA PrinterNotifyInfoDataArray[1];
    		PrinterNotifyInfoDataArray[0] = PrinterNotifyInfoData;
    
    		PRINTER_NOTIFY_OPTIONS_TYPE PrinterNotifyOptionsType;
    		PrinterNotifyOptionsType.Type = JOB_NOTIFY_TYPE;
    		PrinterNotifyOptionsType.Count = sizeof wJobFields/sizeof wJobFields[0];;
    		PrinterNotifyOptionsType.pFields = wJobFields;
    
    		PRINTER_NOTIFY_OPTIONS PrinterNotifyOptions;
    		PrinterNotifyOptions.Version = 2;
    		PrinterNotifyOptions.Flags = NULL;
    		PrinterNotifyOptions.Count = 1;
    		PrinterNotifyOptions.pTypes = &PrinterNotifyOptionsType;
    
    		LPVOID pPrinterNotifyOptions = &PrinterNotifyOptions;
    		hWait =  (HANDLE*) FindFirstPrinterChangeNotification( hPrinter, PRINTER_CHANGE_ALL, 0, pPrinterNotifyOptions);
    
    		if (hWait == INVALID_HANDLE_VALUE)
    			DisplayError("Failed to create printer notification");
    	}
    	printf("Waiting...\n");
    	do
    	{
    		dwRet = WaitForSingleObject(hWait, INFINITE);
    		if(dwRet != WAIT_FAILED)
    		{
    			PRINTER_NOTIFY_INFO* ptr = NULL;
    			PRINTER_NOTIFY_OPTIONS options;
    			PRINTER_NOTIFY_OPTIONS_TYPE  types;
    			WORD wPrinterFields[] =
    			{
    				PRINTER_NOTIFY_FIELD_PRINTER_NAME,
    				PRINTER_NOTIFY_FIELD_PORT_NAME,
    				PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY
    			};
    			options.Version = 2;
    			options.Flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
    			options.Count = 1;
    			options.pTypes = &types;
    			types.Type = PRINTER_NOTIFY_TYPE;
    			types.Count = sizeof wPrinterFields/sizeof wPrinterFields[0];
    			types.pFields = wPrinterFields;
    			if (FindNextPrinterChangeNotification(hWait, &dwChanged, NULL, (void**)&ptr))
    			{
    				if (dwChanged & PRINTER_CHANGE_ADD_JOB)
    				{
    					printf("PRINTER_CHANGE_JOB_ADD triggered\n");
    					PRINTER_NOTIFY_INFO* pNotifyInfo = (PRINTER_NOTIFY_INFO*) ptr;
    					if(ptr != NULL)                          
    					{
    						for(iIndex = 0; iIndex < pNotifyInfo->Count; iIndex++)          
    						{
    							GetJob( hPrinter,pNotifyInfo->aData[iIndex].Id , 2, (LPBYTE)pd_JobInfo , NULL, &dwNeeded);
    							pd_JobInfo = (JOB_INFO_2 *)malloc(dwNeeded);
    							GetJob( hPrinter, pNotifyInfo->aData[iIndex].Id, 2, (LPBYTE)pd_JobInfo , dwNeeded , &dwRet);
    							pd_JobInfo->Status = JOB_STATUS_PAUSED;
    							if(!SetJob(hPrinter, pNotifyInfo->aData[iIndex].Id, 2, (LPBYTE)pd_JobInfo,0))
    								DisplayError("SetJob");
    							char * pBuf =(char*) pNotifyInfo->aData[iIndex].NotifyData.Data.pBuf;
    							switch(pNotifyInfo->aData[iIndex].Field)             
    							{
    								case JOB_NOTIFY_FIELD_STATUS:
    									break;
    								case JOB_NOTIFY_FIELD_PRINTER_NAME:
    									printf("Printer name %s\n", pBuf);
    									break;                                  
    								case JOB_NOTIFY_FIELD_MACHINE_NAME:
    									printf("Machine name %s\n", pBuf);
    									break;
    								case JOB_NOTIFY_FIELD_USER_NAME: 
    									printf("user name %s\n", pBuf);
    									break;
    								case JOB_NOTIFY_FIELD_DOCUMENT:
    									printf("Document %s\n", pBuf);
    									break;
    								case JOB_NOTIFY_FIELD_TOTAL_PAGES:
    									break;
    								case JOB_NOTIFY_FIELD_TOTAL_BYTES:
    									break;
    							}
    						}       
    					}
    				}
    				if (ptr != NULL)
    				{
    					for (iIndex = 0; iIndex < ptr->Count; iIndex++)
    					{
    						PRINTER_NOTIFY_INFO_DATA* pniData = &(ptr->aData)[iIndex];
    						switch (pniData->Field)
    						{
    							case JOB_NOTIFY_FIELD_STATUS:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_STATUS_STRING:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_USER_NAME:
    								{
    									printf("\nJOB_NOTIFY_FIELD_USER_NAME %s\n",pniData->NotifyData.Data.pBuf);
    									break;
    								}
    							case JOB_NOTIFY_FIELD_DOCUMENT:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_PAGES_PRINTED:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_TOTAL_PAGES:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_TOTAL_BYTES:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_BYTES_PRINTED:
    								{
    									break;
    								}
    							case JOB_NOTIFY_FIELD_SUBMITTED:
    								{
    									break;
    								}
    						}
    					}
    				}
    				FreePrinterNotifyInfo(ptr);
    			}
    			else DisplayError("FindNextPrinterChangeNotification Failed");
    		}
    	}
    	while (TRUE);
    	if (hPrinter != NULL)
    		ClosePrinter(hPrinter);
    	hPrinter = NULL;
    
    	if (hWait != NULL)
    		FindClosePrinterChangeNotification(hWait);
    	hWait = NULL;
    	return 0;
    }
    
    BOOL PauseOrResumePrinter(CHAR *pPrinter, DWORD dwOperation)
    {
    	HANDLE hPrinter;
    	PRINTER_DEFAULTS pd;
    
    	pd.pDatatype = NULL;
    	pd.pDevMode = NULL;
    	pd.DesiredAccess = PRINTER_ALL_ACCESS;
    
    	if(!OpenPrinter(pPrinter,&hPrinter,&pd)) 
    	{
    		DisplayError("OpenPrinter Failed");
    		return FALSE;
    	}
    	else if(!SetPrinter(hPrinter, 0, NULL, dwOperation))
    	{
    		DisplayError("SetPrinter failed");
    		return FALSE;
    	}
    	else if(!ClosePrinter(hPrinter))
    	{
    		DisplayError("ClosePrinter failed");
    		return FALSE;
    	}
    	else return TRUE; 
    }
    
    INT main(VOID)
    {
    	char szPrinter[] = {"\\\\MyServer\\MyPrinter"};
    	PauseOrResumePrinter(szPrinter, PRINTER_CONTROL_PAUSE);
    	MonitorPrinter(szPrinter);
    	// PauseOrResumePrinter(szPrinter, PRINTER_CONTROL_RESUME);    
    	return 0;
    }

  8. #8
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    The final piece of the task, a print manager that checks the print queue and redirects jobs to another printer based on userid of the print job. Please note the comments since you'll have to make some modifications to meet your requirements.

    YOU ARE TOTALLY RESPONSIBLE FOR THOROUGHLY TESTING ANY POSTED CODE


    Code:
    #pragma comment(lib, "winspool.lib") 
    #pragma comment(lib, "user32.lib") 
    
    #include <windows.h>
    #include <stdio.h>
    
    struct userdata{
    	char szUserid[32];
    	char szPort[32];
    }users[3] = {"BSmith", "Port1", "DJones", "Port2", "FPorter", "Port3"};
    
    VOID DisplayError(CHAR* pMessage)
    {
    	DWORD dwErrorNum;
    	CHAR szSysMsg[256] = {0};
    	CHAR* pPos;
    	dwErrorNum = GetLastError( );
    	FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    		NULL, dwErrorNum,
    		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    		szSysMsg, 256, NULL );
    	// Trim the end of the line and terminate it with a null
    	pPos = szSysMsg;
    	while((*pPos > 31) || (*pPos == 9))
    		++pPos;
    	do { *pPos-- = 0; } while((pPos >= szSysMsg) &&
    		((*pPos == '.') || (*pPos < 33) )); 
    	printf( "\n%s, error %d (%s)", pMessage, dwErrorNum, szSysMsg );
    }
    
    BOOL PauseOrResumePrinter(CHAR *pPrinter, DWORD dwOperation)
    {
    	HANDLE hPrinter;
    	PRINTER_DEFAULTS pd;
    
    	pd.pDatatype = NULL;
    	pd.pDevMode = NULL;
    	pd.DesiredAccess = PRINTER_ALL_ACCESS;
    
    	if(!OpenPrinter(pPrinter,&hPrinter,&pd)) 
    	{
    		DisplayError("OpenPrinter Failed");
    		return FALSE;
    	}
    	else if(!SetPrinter(hPrinter, 0, NULL, dwOperation))
    	{
    		DisplayError("SetPrinter failed");
    		return FALSE;
    	}
    	else if(!ClosePrinter(hPrinter))
    	{
    		DisplayError("ClosePrinter failed");
    		return FALSE;
    	}
    	else return TRUE; 
    }
    
    BOOL ChangePrinterPort(LPTSTR pPrinterName, LPTSTR pPortName)   
    {   
    	HANDLE hPrinter = NULL;   
    	DWORD dwSizeNeeded = 0;   
    	DEVMODE *pDevMode = NULL;   
    	PRINTER_DEFAULTS pd;   
    	BOOL bFlag;   
    	LONG lFlag;   
    	PPRINTER_INFO_2 pi2 = NULL;   
    
    	ZeroMemory(&pd, sizeof(pd));   
    	pd.DesiredAccess = PRINTER_ALL_ACCESS;   
    	if(!OpenPrinter(pPrinterName, &hPrinter, &pd))   
    	{   
    		DisplayError("OpenPrinter failed");     
    		return FALSE;   
    	}
    	SetLastError(0);   
    	bFlag = GetPrinter(hPrinter, 2, 0, 0, &dwSizeNeeded);   
    	if ((!bFlag) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER) || (dwSizeNeeded == 0))   
    	{   
    		DisplayError("First GetPrinter failed");        
    		ClosePrinter(hPrinter);   
    		return FALSE;   
    	}   
    	if((pi2 = (PPRINTER_INFO_2) malloc(dwSizeNeeded)) == NULL)   
    	{   
    		DisplayError("pi2 malloc failed");
    		ClosePrinter(hPrinter);   
    		return FALSE;   
    	}   
    	if(!GetPrinter(hPrinter, 2, (LPBYTE)pi2, dwSizeNeeded, &dwSizeNeeded))
    	{   
    		DisplayError("Second GetPrinter failed");       
    		free(pi2);   
    		pi2 = NULL;   
    		ClosePrinter(hPrinter);   
    		return FALSE;   
    	}   
    	// If GetPrinter didn't fill in the DEVMODE, try to get it by calling   
    	// DocumentProperties...   
    	if (pi2->pDevMode == NULL)   
    	{   
    		dwSizeNeeded = DocumentProperties(NULL, hPrinter, pPrinterName, NULL, NULL, 0);   
    		if (dwSizeNeeded <= 0)   
    		{   
    			DisplayError("First DocumentProperties failed");
    			free(pi2);   
    			pi2 = NULL;   
    			ClosePrinter(hPrinter);   
    			return FALSE;   
    		}   
    		if((pDevMode = (DEVMODE *) malloc(dwSizeNeeded)) == NULL)
    		{   
    			DisplayError("pDeviceMode malloc failed");
    			free(pi2);   
    			pi2 = NULL;   
    			ClosePrinter(hPrinter);   
    			return FALSE;   
    		}   
    		lFlag = DocumentProperties(NULL, hPrinter, pPrinterName, pDevMode, NULL, DM_OUT_BUFFER | DM_IN_PROMPT);   
    		if (lFlag != IDOK || pDevMode == NULL)   
    		{   
    			DisplayError("Second DocumentProperties failed");
    			free(pDevMode);   
    			pDevMode = NULL;   
    			free(pi2);   
    			pi2 = NULL;   
    			ClosePrinter(hPrinter);   
    			return FALSE;   
    		}   
    		pi2->pDevMode = pDevMode;   
    	}   
    	// Do not attempt to set security descriptor...   
    	pi2->pSecurityDescriptor = NULL;   
    
    	printf("Printer name: %s\n", pi2->pPrinterName );
    	printf("Port name: %s\n", pi2->pPortName);
    	printf("Server name: %s\n", pi2->pServerName);
    	printf("Share name: %s\n", pi2->pShareName );
    	printf("Driver name: %s\n", pi2->pDriverName);
    
    	strcpy(pi2->pPortName, pPortName);
    
    	bFlag = SetPrinter(hPrinter, 2, (LPBYTE)pi2, 0);   
    	if (!bFlag) 
    	{   
    		DisplayError("SetPrinter failed");
    		free(pi2);   
    		pi2 = NULL;   
    		ClosePrinter(hPrinter);   
    		if (pDevMode != NULL)   
    		{   
    			free(pDevMode);   
    			pDevMode = NULL;   
    		}   
    		return FALSE;   
    	}   
    	// Tell other apps that there was a change...   
    	SendMessageTimeout(HWND_BROADCAST, WM_DEVMODECHANGE, 0L, (LPARAM)(LPCSTR)pPrinterName, SMTO_NORMAL, 1000, NULL);   
    	if (pi2)   
    	{   
    		free(pi2);   
    		pi2 = NULL;   
    	}   
    	if (hPrinter)   
    		ClosePrinter(hPrinter);   
    	if (pDevMode)   
    	{   
    		free(pDevMode);   
    		pDevMode = NULL;   
    	}   
    	return TRUE;   
    }  
    
    BOOL GetJobs(HANDLE hPrinter,       
    	JOB_INFO_2 **ppJobInfo, 
    	int *pcJobs,            
    	DWORD *pStatus)         
    {
    	DWORD dwBytesNeeded, dwReturned, dwBytesUsed;
    	JOB_INFO_2          *pJobStorage = NULL;
    	PRINTER_INFO_2       *pPrinterInfo = NULL;
    	if (!GetPrinter(hPrinter, 2, NULL, 0, &dwBytesNeeded))
    	{
    		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    		{
    			DisplayError("First GetPrinter error");
    			return FALSE;
    		}   
    	}
    	if((pPrinterInfo = (PRINTER_INFO_2 *)malloc(dwBytesNeeded)) == NULL)
    	{
    		DisplayError("pPrinterInfo malloc failed");
    		return FALSE;
    	}
    	if (!GetPrinter(hPrinter,
    		2,
    		(unsigned char *) pPrinterInfo,
    		dwBytesNeeded,
    		&dwBytesUsed))
    	{
    		DisplayError("Second GetPrinter error");
    		free(pPrinterInfo);
    		pPrinterInfo = NULL;
    		return FALSE;
    	}
    	if (!EnumJobs(hPrinter,
    		0,
    		pPrinterInfo->cJobs,
    		2,
    		NULL,
    		0,
    		(LPDWORD)&dwBytesNeeded,
    		(LPDWORD)&dwReturned))
    	{
    		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    		{
    			DisplayError("First EnumJobs failed");
    			free(pPrinterInfo);
    			pPrinterInfo = NULL;
    			return FALSE;
    		}
    	}
    	if((pJobStorage = (JOB_INFO_2 *)malloc(dwBytesNeeded)) == NULL)
    	{
    		DisplayError("pJobStorage malloc failed");
    		free(pPrinterInfo);
    		pPrinterInfo = NULL;
    		return FALSE;
    	}
    	ZeroMemory(pJobStorage, dwBytesNeeded);
    	if (!EnumJobs(hPrinter,
    		0,
    		pPrinterInfo->cJobs,
    		2,
    		(LPBYTE)pJobStorage,
    		dwBytesNeeded,
    		(LPDWORD)&dwBytesUsed,
    		(LPDWORD)&dwReturned))
    	{
    		DisplayError("Second EnumJobs failed");
    		free(pPrinterInfo);
    		free(pJobStorage);
    		pJobStorage = NULL;
    		pPrinterInfo = NULL;
    		return FALSE;
    	}
    	*pcJobs = dwReturned;
    	*pStatus = pPrinterInfo->Status;
    	*ppJobInfo = pJobStorage;
    	free(pPrinterInfo);
    	return TRUE;
    }
    
    
    BOOL PrintTheJobs(char *pPrinter, char *pOriginalPort)
    {
    	char szTempError[128] = {0};    
    	JOB_INFO_2 *pJobInfo = NULL;
    	DWORD dwNeeded, dwRet, dwPrinterStatus;
    	HANDLE hPrinter;
    	PRINTER_DEFAULTS pd;
    	JOB_INFO_2  *pJobs;
    	int iJobs, iIndex, iTempIndex;
    
    	pd.pDatatype = NULL;
    	pd.pDevMode = NULL;
    	pd.DesiredAccess = PRINTER_ALL_ACCESS;
    
    	if(!OpenPrinter(pPrinter,&hPrinter,&pd)) 
    	{
    		DisplayError("OpenPrinter Failed");
    		return FALSE;
    	}
    	else
    		GetJobs(hPrinter,       
    		&pJobs, /* Pointer to be filled.  */ 
    		&iJobs,            /* Count of jobs filled.  */ 
    		&dwPrinterStatus);         /* Print Queue status.    */ 
    
    	for (iIndex = 0; iIndex < iJobs; iIndex++)
    	{
    		printf("%s\n", pJobs[iIndex].pUserName);
    		printf("%s\n", pJobs[iIndex].pMachineName);
    		printf("%s\n", pJobs[iIndex].pPrinterName);
    		if (pJobs[iIndex].Status & JOB_STATUS_PAUSED)
    		{
    			printf("Job %d is paused\n", pJobs[iIndex].JobId);
    			GetJob( hPrinter,pJobs[iIndex].JobId , 2, (LPBYTE)pJobInfo , NULL, &dwNeeded);
    			if((pJobInfo = (JOB_INFO_2 *)malloc(dwNeeded)) == NULL)
    			{
    				DisplayError("pJobInfo malloc failed");
    				return FALSE;
    			}
    			GetJob( hPrinter, pJobs[iIndex].JobId, 2, (LPBYTE)pJobInfo , dwNeeded , &dwRet);
    			pJobInfo->Status = JOB_STATUS_RESTART;
    			if(!SetJob(hPrinter, pJobs[iIndex].JobId, 2, (LPBYTE)pJobInfo,0))
    				DisplayError("SetJob failed");
    			else
    			{
    				// Change this port per your requirements.
    				// Possibly based on userid of job   
    				char szNewPrinterPort[128] = {0};
    				for(iTempIndex = 0; iTempIndex < sizeof(users) / sizeof(userdata); iTempIndex++)
    				{
    					printf("%s  %s\n", users[iTempIndex].szUserid,users[iTempIndex].szPort);
    					if(stricmp(users[iTempIndex].szUserid, pJobs[iIndex].pUserName) == 0)
    					{
    						printf("Processing job for: %s\n",users[iTempIndex].szUserid);
    						strcpy(szNewPrinterPort,users[iTempIndex].szPort );
    						break;
    					}
    				}
    				if(ChangePrinterPort(pPrinter, szNewPrinterPort) == TRUE)
    				{
    					PauseOrResumePrinter(pPrinter, PRINTER_CONTROL_RESUME);  
    					GetJob( hPrinter,pJobs[iIndex].JobId , 2, (LPBYTE)pJobInfo , NULL, &dwNeeded);
    					// This is where you'll have to refine the code to continuously 
    					// query the printer and print the jobs
    					// The following while loop is very primitive.  You'll need something a
    					// lot more elegant.
    					while(pJobs[iIndex].Status & JOB_STATUS_PRINTING)
    					{
    						GetJob( hPrinter,pJobs[iIndex].JobId , 2, (LPBYTE)pJobInfo , NULL, &dwNeeded);
    						printf("Job printing\n");
    						Sleep(2000);
    					}
    					if(ChangePrinterPort(pPrinter, pOriginalPort) == FALSE)
    					{
    						sprintf(szTempError, "Cannot change back to original printer port %s from %s\n",pOriginalPort, szNewPrinterPort);
    						DisplayError(szTempError);
    					}
    				}
    				else
    				{
    					sprintf(szTempError, "Could not print job id: %d  User Name: %s Machine name: %s",
    						pJobs[iIndex].JobId,pJobs[iIndex].pUserName,pJobs[iIndex].pMachineName);
    					DisplayError(szTempError);
    				}
    			}
    			free(pJobInfo);
    		}
    	}
    	return TRUE;
    }
    
    INT main(VOID)
    {
    	char szOriginalPrinterPort[] = {"Port4"};
    	char szPrinter[] = {"\\\\MyServer\\MyPrinter"};
    	PrintTheJobs(szPrinter, szOriginalPrinterPort);
    	return 0;
    }

  9. #9
    Registered User
    Join Date
    Oct 2008
    Posts
    4
    thank you so much for your help!

    I still have one question: Your solution changes the printer port what should work and is a great idea, but therefore I have to pause all new incoming printjobs by default, don't I? Is there maybe a Windows setting?

  10. #10
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    Quote Originally Posted by extasic View Post
    thank you so much for your help!

    I still have one question: Your solution changes the printer port what should work and is a great idea, but therefore I have to pause all new incoming printjobs by default, don't I? Is there maybe a Windows setting?
    My first code posting is the "Printer Monitor" app which constantly monitors the print queue. The first task it does is pause the printer. It then continually monitors the queue for any newly added print jobs. The monitor will pause these jobs. This pausing of the queue and pausing of the added print jobs is done so that the second app can process and print the jobs.

    Bottomline, you do not have to manually pause any new incoming jobs. The "Printer Monitor" will do that for you. .

    The second code posting is the "Print manager" which will check the queue for any paused jobs. It will restart a selected job and change the printer port to the desired printer port based on the userid associated with the job and then restart the printer to allow the job to be printed to the correct printer.

    There is a Windows setting that you can watch to see how the port changes. If you select properties of the the printer and click on the port tab, it will show you the currently selected port. You can watch the ports change if you keep this window open when you're using "Print Manager".

    You'll have to tweak and thoroughly test these two apps to meet your requirements.

    Finally, you can post on the C# forum for assistance with porting the C code to C# once you are satisfied that it meets your requirements.

  11. #11
    Registered User
    Join Date
    Aug 2010
    Posts
    1

    C# code of this

    I'm creating a similar one like this but I do not have a good idea about pInvoke etc. Can I please have the c# code of this, so I can integrate this with my half completed c# solution. Thanks

    And thank you very much this code is very useful and well explained.

  12. #12
    Registered User
    Join Date
    Jun 2011
    Posts
    1

    access violation

    thanks for both codes, they are very usefull. But I tried to test print monitor and I got access violation due to read adress 0x000....
    at line:if(!SetJob(hPrinter, pNotifyInfo->aData[iIndex].Id, 2, (LPBYTE)pd_JobInfo,0))
    could you help me how can I get rid of it...
    thanks in advence
    ayfer

  13. #13
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Please dont bump old threads.

    CLOSED

Popular pages Recent additions subscribe to a feed