要实现一个小功能:监视所有打印任务,使得每次只能打印一份
思路:使用EnumJobs 函数枚举所有的打印任务,得到打印的份数,如果发现份数不是1就将任务删除
其中要用SetJob把任务删除的话需要有一定的权限。如果打印机的句柄是自己打开的,由于OpenPrinter 函数第三个参数不好设置,
因此决定用API hook的办法。截获 EndDocPrinter函数,在里面实现主要功能
PS:蛋疼的WORD真是恶心,用EnumJobs获取的打印份数始终是1,不知道是怎么回事
主要代码如下:
EndDocPrinter.h
1 #pragma once 2 3 #include "stdafx.h" 4 5 typedef 6 BOOL (WINAPI * __pfnEndDocPrinter)( 7 _In_ HANDLE hPrinter 8 ); 9 10 BOOL WINAPI my_EndDocPrinter( 11 _In_ HANDLE hPrinter 12 ); 13 14 extern __pfnEndDocPrinter pfnEndDocPrinter;
EndDocPrinter.cpp:
1 #include "stdafx.h" 2 #include <Winspool.h> 3 4 BOOL WINAPI my_EndDocPrinter( 5 _In_ HANDLE hPrinter 6 ) 7 { 8 BOOL ret = FALSE; 9 DWORD cbNeeded = 0, cReturned = 0; 10 DWORD actNeeded = 0; 11 TCHAR szCopys[MAX_PATH]; 12 ret = EnumJobs(hPrinter, 0, 127, 2, NULL, 0, &cbNeeded, &cReturned); 13 actNeeded = cbNeeded; 14 JOB_INFO_2* print_job = new JOB_INFO_2[actNeeded]; 15 ret = EnumJobs(hPrinter, 0, 127, 2, (LPBYTE)print_job, actNeeded, &cbNeeded, &cReturned); 16 for (int i = 0; i < cReturned; i++) 17 { 18 short num=print_job[i].pDevMode->dmCopies; 19 if(num!=1) 20 { 21 SetJob(hPrinter, print_job[i].JobId,2,(LPBYTE)&(print_job[i]),JOB_CONTROL_DELETE); 22 } 23 } 24 25 return pfnEndDocPrinter(hPrinter); 26 }
DllMain中还是用之前的inline HOOK:
1 // dllmain.cpp : 定义 DLL 应用程序的入口点。 2 #include "stdafx.h" 3 4 5 __pfnEndDocPrinter pfnEndDocPrinter = (__pfnEndDocPrinter)GetProcAddress(GetModuleHandle(TEXT("Winspool.drv")), "EndDocPrinter"); 6 7 BOOL APIENTRY DllMain( HMODULE hModule, 8 DWORD ul_reason_for_call, 9 LPVOID lpReserved 10 ) 11 { 12 switch (ul_reason_for_call) 13 { 14 case DLL_PROCESS_ATTACH: 15 InlineHook( 16 (__pfnEndDocPrinter)GetProcAddress(GetModuleHandle(TEXT("Winspool.drv")), "EndDocPrinter"), 17 my_EndDocPrinter, 18 (void **)&pfnEndDocPrinter 19 ); 20 case DLL_THREAD_ATTACH: 21 case DLL_THREAD_DETACH: 22 case DLL_PROCESS_DETACH: 23 break; 24 } 25 return TRUE; 26 }