Huw Davies <huw@xxxxxxxxxxxxxxx> 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; }