winspool: better handling of cups printers
Huw D M Davies
h.davies1 at physics.ox.ac.uk
Tue Nov 4 12:29:56 CST 2003
Huw Davies <huw at codeweavers.com>
Make sure that all printer entries have a valid Name and Port key.
Cope with cups printer deletion.
Index: dlls/winspool/info.c
===================================================================
RCS file: /home/wine/wine/dlls/winspool/info.c,v
retrieving revision 1.84
diff -u -r1.84 info.c
--- dlls/winspool/info.c 15 Oct 2003 21:01:05 -0000 1.84
+++ dlls/winspool/info.c 4 Nov 2003 18:25:25 -0000
@@ -101,12 +101,16 @@
'i','l','e',0};
static WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
static WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
+static WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
+
+static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
static HKEY WINSPOOL_OpenDriverReg( LPVOID pEnvironment, BOOL unicode);
static BOOL WINSPOOL_GetPrinterDriver(HANDLE hPrinter, LPWSTR pEnvironment,
DWORD Level, LPBYTE pDriverInfo,
DWORD cbBuf, LPDWORD pcbNeeded,
BOOL unicode);
+static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey);
/* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
if passed a NULL string. This returns NULLs to the result.
@@ -144,8 +148,8 @@
}
#ifdef HAVE_CUPS_CUPS_H
-BOOL
-CUPS_LoadPrinters(void) {
+static BOOL CUPS_LoadPrinters(void)
+{
typeof(cupsGetDests) *pcupsGetDests = NULL;
typeof(cupsGetPPD) *pcupsGetPPD = NULL;
int i, nrofdests;
@@ -153,11 +157,8 @@
cups_dest_t *dests;
PRINTER_INFO_2A pinfo2a;
void *cupshandle = NULL;
- const char *ppd;
char *port,*devline;
- UNICODE_STRING lpszNameW;
- PWSTR pwstrNameW;
- HKEY hkeyPrinters, hkeyPrinter;
+ HKEY hkeyPrinter, hkeyPrinters;
cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, NULL, 0);
if (!cupshandle)
@@ -172,70 +173,55 @@
DYNCUPS(cupsGetDests);
#undef DYNCUPS
+ if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
+ ERROR_SUCCESS) {
+ ERR("Can't create Printers key\n");
+ SetLastError(ERROR_FILE_NOT_FOUND); /* ?? */
+ return FALSE;
+ }
nrofdests = pcupsGetDests(&dests);
TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
for (i=0;i<nrofdests;i++) {
- /* First check that the printer doesn't exist already */
- pwstrNameW = asciitounicode(&lpszNameW, dests[i].name);
- if (RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) ==
- ERROR_SUCCESS) {
- if (RegOpenKeyW(hkeyPrinters, pwstrNameW, &hkeyPrinter) ==
- ERROR_SUCCESS) {
- /* We know this printer already */
- RegCloseKey(hkeyPrinter);
- RegCloseKey(hkeyPrinters);
- RtlFreeUnicodeString(&lpszNameW);
- hadprinter = TRUE;
- if(dests[i].is_default)
- WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
- TRACE("Printer %s already known. Skipping detection\n", dests[i].name);
- continue;
- }
- RegCloseKey(hkeyPrinters);
- }
- RtlFreeUnicodeString(&lpszNameW);
-
- /* OK, we haven't seen this one yet. Request PPD for it */
- ppd = pcupsGetPPD(dests[i].name);
- if (!ppd) {
- WARN("No ppd file for %s.\n",dests[i].name);
- /* If this was going to be the default printer,
- * forget it and use another one.
- */
- continue;
- }
- unlink(ppd);
-
- memset(&pinfo2a,0,sizeof(pinfo2a));
- pinfo2a.pPrinterName = dests[i].name;
- pinfo2a.pDatatype = "RAW";
- pinfo2a.pPrintProcessor = "WinPrint";
- pinfo2a.pDriverName = "PS Driver";
- pinfo2a.pComment = "WINEPS Printer using CUPS";
- pinfo2a.pLocation = "<physical location of printer>";
- port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
- sprintf(port,"LPR:%s",dests[i].name);
- pinfo2a.pPortName = port;
- pinfo2a.pParameters = "<parameters?>";
- pinfo2a.pShareName = "<share name?>";
- pinfo2a.pSepFile = "<sep file?>";
-
+ port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(dests[i].name)+1);
+ sprintf(port,"LPR:%s",dests[i].name);
devline=HeapAlloc(GetProcessHeap(),0,strlen("WINEPS,")+strlen(port)+1);
sprintf(devline,"WINEPS,%s",port);
WriteProfileStringA("devices",dests[i].name,devline);
HeapFree(GetProcessHeap(),0,devline);
- if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
- if (GetLastError()!=ERROR_PRINTER_ALREADY_EXISTS)
- ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
- }
+ TRACE("Printer %d: %s\n", i, dests[i].name);
+ if(RegOpenKeyA(hkeyPrinters, dests[i].name, &hkeyPrinter) == ERROR_SUCCESS) {
+ /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
+ and continue */
+ TRACE("Printer already exists\n");
+ RegDeleteValueW(hkeyPrinter, May_Delete_Value);
+ RegCloseKey(hkeyPrinter);
+ } else {
+ memset(&pinfo2a,0,sizeof(pinfo2a));
+ pinfo2a.pPrinterName = dests[i].name;
+ pinfo2a.pDatatype = "RAW";
+ pinfo2a.pPrintProcessor = "WinPrint";
+ pinfo2a.pDriverName = "PS Driver";
+ pinfo2a.pComment = "WINEPS Printer using CUPS";
+ pinfo2a.pLocation = "<physical location of printer>";
+ pinfo2a.pPortName = port;
+ pinfo2a.pParameters = "<parameters?>";
+ pinfo2a.pShareName = "<share name?>";
+ pinfo2a.pSepFile = "<sep file?>";
+
+ if (!AddPrinterA(NULL,2,(LPBYTE)&pinfo2a)) {
+ if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
+ ERR("printer '%s' not added by AddPrinterA (error %ld)\n",dests[i].name,GetLastError());
+ }
+ }
HeapFree(GetProcessHeap(),0,port);
hadprinter = TRUE;
if (dests[i].is_default)
WINSPOOL_SetDefaultPrinter(dests[i].name, dests[i].name, TRUE);
}
+ RegCloseKey(hkeyPrinters);
wine_dlclose(cupshandle, NULL, 0);
return hadprinter;
}
@@ -382,10 +368,15 @@
return ERROR_FILE_NOT_FOUND;
}
-void
-WINSPOOL_LoadSystemPrinters() {
- HKEY hkPPD;
- DRIVER_INFO_3A di3a;
+void WINSPOOL_LoadSystemPrinters(void)
+{
+ HKEY hkey, hkeyPrinters;
+ DRIVER_INFO_3A di3a;
+ HANDLE hprn;
+ DWORD needed, num, i;
+ WCHAR PrinterName[256];
+ BOOL done = FALSE;
+
di3a.cVersion = 0x400;
di3a.pName = "PS Driver";
di3a.pEnvironment = NULL; /* NULL means auto */
@@ -401,18 +392,93 @@
ERR("Failed adding PS Driver (%ld)\n",GetLastError());
return;
}
+
+ /* This ensures that all printer entries have a valid Name value. If causes
+ problems later if they don't. If one is found to be missed we create one
+ and set it equal to the name of the key */
+ if(RegCreateKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
+ if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
+ for(i = 0; i < num; i++) {
+ if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)) == ERROR_SUCCESS) {
+ if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
+ if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
+ set_reg_szW(hkey, NameW, PrinterName);
+ }
+ RegCloseKey(hkey);
+ }
+ }
+ }
+ }
+ RegCloseKey(hkeyPrinters);
+ }
+
+ /* We want to avoid calling AddPrinter on cups printers as much as
+ possible, because this will (eventually) lead to a call to
+ cupsGetPPD which takes forever. So we'll tag all printers that
+ were automatically added last time around, if they still exist
+ we'll leave them be otherwise we'll delete them. */
+ EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
+ if(needed) {
+ PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
+ if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
+ for(i = 0; i < num; i++) {
+ if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
+ if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
+ if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
+ DWORD dw = 1;
+ RegSetValueExW(hkey, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&dw, sizeof(dw));
+ RegCloseKey(hkey);
+ }
+ ClosePrinter(hprn);
+ }
+ }
+ }
+ }
+ HeapFree(GetProcessHeap(), 0, pi);
+ }
+
+
#ifdef HAVE_CUPS_CUPS_H
/* If we have any CUPS based printers, skip looking for printcap printers */
- if (CUPS_LoadPrinters())
- return;
+ done = CUPS_LoadPrinters();
+
#endif
+ /* Now enumerate the list again and delete any printers that a still tagged */
+ EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num);
+ if(needed) {
+ PRINTER_INFO_5A* pi = HeapAlloc(GetProcessHeap(), 0, needed);
+ if(EnumPrintersA(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num)) {
+ for(i = 0; i < num; i++) {
+ if(pi[i].pPortName == NULL || !strncmp(pi[i].pPortName,"CUPS:", 5) || !strncmp(pi[i].pPortName, "LPR:", 4)) {
+ if(OpenPrinterA(pi[i].pPrinterName, &hprn, NULL)) {
+ if(WINSPOOL_GetOpenedPrinterRegKey(hprn, &hkey) == ERROR_SUCCESS) {
+ DWORD dw, type, size = sizeof(dw);
+ if(RegQueryValueExW(hkey, May_Delete_Value, NULL, &type, (LPBYTE)&dw, &size) == ERROR_SUCCESS) {
+ TRACE("Deleting old printer %s\n", pi[i].pPrinterName);
+ DeletePrinter(hprn);
+ }
+ RegCloseKey(hkey);
+ }
+ ClosePrinter(hprn);
+ }
+ }
+ }
+ }
+ HeapFree(GetProcessHeap(), 0, pi);
+ }
+
+
+ if(done)
+ return;
+
/* Check for [ppd] section in config file before parsing /etc/printcap */
if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppd",
- &hkPPD) == ERROR_SUCCESS)
+ &hkey) == ERROR_SUCCESS)
{
- RegCloseKey(hkPPD);
+ RegCloseKey(hkey);
PRINTCAP_LoadPrinters();
}
}
@@ -1268,6 +1334,57 @@
}
/*****************************************************************************
+ * WINSPOOL_SHRegDeleteKey
+ *
+ * Recursively delete subkeys.
+ * Cut & paste from shlwapi.
+ *
+ */
+static DWORD WINSPOOL_SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey)
+{
+ DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i;
+ WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
+ HKEY hSubKey = 0;
+
+ dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
+ if(!dwRet)
+ {
+ /* Find how many subkeys there are */
+ dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount,
+ &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
+ if(!dwRet)
+ {
+ dwMaxSubkeyLen++;
+ if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
+ /* Name too big: alloc a buffer for it */
+ lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
+
+ if(!lpszName)
+ dwRet = ERROR_NOT_ENOUGH_MEMORY;
+ else
+ {
+ /* Recursively delete all the subkeys */
+ for(i = 0; i < dwKeyCount && !dwRet; i++)
+ {
+ dwSize = dwMaxSubkeyLen;
+ dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL);
+ if(!dwRet)
+ dwRet = WINSPOOL_SHDeleteKeyW(hSubKey, lpszName);
+ }
+
+ if (lpszName != szNameBuf)
+ HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
+ }
+ }
+
+ RegCloseKey(hSubKey);
+ if(!dwRet)
+ dwRet = RegDeleteKeyW(hKey, lpszSubKey);
+ }
+ return dwRet;
+}
+
+/*****************************************************************************
* DeletePrinter [WINSPOOL.@]
*/
BOOL WINAPI DeletePrinter(HANDLE hPrinter)
@@ -1276,20 +1393,11 @@
HKEY hkeyPrinters;
if(!lpNameW) return FALSE;
- if(RegOpenKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) !=
- ERROR_SUCCESS) {
- ERR("Can't open Printers key\n");
- return 0;
- }
-
- /* This should use a recursive delete see Q142491 or SHDeleteKey */
- if(RegDeleteKeyW(hkeyPrinters, lpNameW) == ERROR_SUCCESS) {
- SetLastError(ERROR_PRINTER_NOT_FOUND); /* ?? */
- RegCloseKey(hkeyPrinters);
- return 0;
+ if(RegOpenKeyA(HKEY_LOCAL_MACHINE, Printers, &hkeyPrinters) == ERROR_SUCCESS) {
+ WINSPOOL_SHDeleteKeyW(hkeyPrinters, lpNameW);
+ RegCloseKey(hkeyPrinters);
}
-
- ClosePrinter(hPrinter);
+ WriteProfileStringW(devicesW, lpNameW, NULL);
return TRUE;
}
More information about the wine-patches
mailing list