Attached is a patch to implement EnumPrinterDataEx (which should let everyone be happy about where the PostScript driver's font substitution table goes). Since this is the first time I've implemented a "real" Win32 API, rather than just doing internal tweaking, I may have missed something that's glaringly obvious to the more experienced. Before I send this to wine- patches, I'd appreciate it if some folks would take a quick look and let me know what they think. Thanks! -- ======================================================================== Ian Pilcher pilcher(a)concentric.net ======================================================================== --- ../wine-20010203cvs/include/winspool.h Sun Feb 11 21:46:34 2001 +++ include/winspool.h Sun Feb 11 14:59:53 2001 @@ -763,6 +763,25 @@ DECL_WINELIB_TYPE_AW(PPROVIDOR_INFO_1) DECL_WINELIB_TYPE_AW(LPPROVIDOR_INFO_1) +typedef struct _PRINTER_ENUM_VALUESA { + LPSTR pValueName; + DWORD cbValueName; + DWORD dwType; + LPBYTE pData; + DWORD cbData; +} PRINTER_ENUM_VALUESA, *PPRINTER_ENUM_VALUESA; + +typedef struct _PRINTER_ENUM_VALUESW { + LPWSTR pValueName; + DWORD cbValueName; + DWORD dwType; + LPBYTE pData; + DWORD cbData; +} PRINTER_ENUM_VALUESW, *PPRINTER_ENUM_VALUESW; + +DECL_WINELIB_TYPE_AW(PRINTER_ENUM_VALUES) +DECL_WINELIB_TYPE_AW(PPRINTER_ENUM_VALUES) + /* DECLARATIONS */ INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort,WORD fwCapability, LPSTR pOutput, LPDEVMODEA pDevMode); @@ -1082,6 +1101,14 @@ BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProvidorName); #define DeletePrintProvidor WINELIB_NAME_AW(DeletePrintProvidor) + +DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, + LPBYTE pEnumValues, DWORD cbEnumValues, + LPDWORD pcbEnumValues, LPDWORD pnEnumValues); +DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, + LPBYTE pEnumValues, DWORD cbEnumValues, + LPDWORD pcbEnumValues, LPDWORD pnEnumValues); +#define EnumPrinterDataEx WINELIB_NAME_AW(EnumPrinterDataEx) --- ../wine-20010203cvs/dlls/winspool/winspool.drv.spec Sun Feb 11 21:46:34 2001 +++ dlls/winspool/winspool.drv.spec Sun Feb 11 14:59:53 2001 @@ -80,6 +80,10 @@ @ stub EnumPrintProcessorDatatypesW @ stub EnumPrintProcessorsA @ stub EnumPrintProcessorsW +@ stub EnumPrinterDataA +@ stdcall EnumPrinterDataExA(long str ptr long ptr ptr) EnumPrinterDataExA +@ stdcall EnumPrinterDataExW(long wstr ptr long ptr ptr) EnumPrinterDataExW +@ stub EnumPrinterDataW @ stdcall EnumPrinterDriversA(str str long ptr long ptr ptr) EnumPrinterDriversA @ stdcall EnumPrinterDriversW(wstr wstr long ptr long ptr ptr) EnumPrinterDriversW @ stdcall EnumPrintersA(long ptr long ptr long ptr ptr) EnumPrintersA --- ../wine-20010203cvs/dlls/winspool/info.c Sun Feb 11 21:46:34 2001 +++ dlls/winspool/info.c Sun Feb 11 21:36:19 2001 @@ -2561,3 +2561,322 @@ return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType, pData, nSize, pcbNeeded); } + +/* + * The Win32 doc says that RegCloseKey and HeapFree can fail, but it's hard to + * imagine why either of them would. And what is the calling function supposed + * to do about it anyway? + */ + +#define REGCLOSEKEY(X) \ +{ \ + DWORD ret; \ + \ + ret = RegCloseKey (X); \ + if (ret != ERROR_SUCCESS) \ + WARN ("RegCloseKey returned %li\n", ret); \ +} + +#define HEAPFREE(X,Y,Z) \ +{ \ + if (HeapFree ((X), (Y), (Z)) == 0) \ + WARN ("HeapFree failed with code %li\n", GetLastError ()); \ +} + +/******************************************************************************* + * EnumPrinterDataExW [WINSPOOL.197] + */ +DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, + LPBYTE pEnumValues, DWORD cbEnumValues, + LPDWORD pcbEnumValues, LPDWORD pnEnumValues) +{ + HKEY hkPrinter, hkSubKey; + DWORD ret, dwIndex, cValues, cbMaxValueNameLen, + cbValueNameLen, cbMaxValueLen, cbValueLen, + cbBufSize, dwType; + LPWSTR lpValueName; + HANDLE hHeap; + PBYTE lpValue; + PPRINTER_ENUM_VALUESW ppev; + + TRACE ("%08x %s\n", hPrinter, debugstr_w (pKeyName)); + + if (pKeyName == NULL || *pKeyName == 0) + return ERROR_INVALID_PARAMETER; + + ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter); + if (ret != ERROR_SUCCESS) + { + TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%08x) returned %li\n", + hPrinter, ret); + return ret; + } + + ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey); + if (ret != ERROR_SUCCESS) + { + REGCLOSEKEY (hkPrinter); + TRACE ("RegOpenKeyExW (%08x, %s) returned %li\n", hPrinter, + debugstr_w (pKeyName), ret); + return ret; + } + + REGCLOSEKEY (hkPrinter); + + ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL, + &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL); + if (ret != ERROR_SUCCESS) + { + REGCLOSEKEY (hkSubKey); + TRACE ("RegQueryInfoKeyW (%08x) returned %li\n", hkSubKey, ret); + return ret; + } + + TRACE ("RegQueryInfoKeyW returned cValues = %li, cbMaxValueNameLen = %li, " + "cbMaxValueLen = %li\n", cValues, cbMaxValueNameLen, cbMaxValueLen); + + if (cValues == 0) /* empty key */ + { + REGCLOSEKEY (hkSubKey); + *pcbEnumValues = *pnEnumValues = 0; + return ERROR_SUCCESS; + } + + ++cbMaxValueNameLen; /* allow for trailing '\0' */ + lpValueName = alloca (cbMaxValueNameLen * sizeof (WCHAR)); + if (lpValueName == NULL) + { + ERR ("Failed to allocate %li bytes on stack\n", + cbMaxValueNameLen * sizeof (WCHAR)); + REGCLOSEKEY (hkSubKey); + return ERROR_OUTOFMEMORY; + } + + hHeap = GetProcessHeap (); + if (hHeap == (HANDLE) NULL) + { + ERR ("GetProcessHeap failed\n"); + REGCLOSEKEY (hkSubKey); + return ERROR_OUTOFMEMORY; + } + + lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen); + if (lpValue == NULL) + { + ERR ("Failed to allocate %li bytes from process heap\n", cbMaxValueLen); + REGCLOSEKEY (hkSubKey); + return ERROR_OUTOFMEMORY; + } + + TRACE ("pass 1: calculating buffer required for all names and values\n"); + + cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW); + + TRACE ("%li bytes required for %li headers\n", cbBufSize, cValues); + + for (dwIndex = 0; dwIndex < cValues; ++dwIndex) + { + cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen; + ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen, + NULL, NULL, lpValue, &cbValueLen); + if (ret != ERROR_SUCCESS) + { + HEAPFREE (hHeap, 0, lpValue); + REGCLOSEKEY (hkSubKey); + TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret); + return ret; + } + + TRACE ("%s [%li]: name needs %li bytes, data needs %li bytes\n", + debugstr_w (lpValueName), dwIndex, + (cbValueNameLen + 1) * sizeof (WCHAR), cbValueLen); + + cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR); + cbBufSize += cbValueLen; + } + + TRACE ("%li bytes required for all %li values\n", cbBufSize, cValues); + + *pcbEnumValues = cbBufSize; + *pnEnumValues = cValues; + + if (cbEnumValues < cbBufSize) /* buffer too small */ + { + HEAPFREE (hHeap, 0, lpValue); + REGCLOSEKEY (hkSubKey); + TRACE ("%li byte buffer is not large enough\n", cbEnumValues); + return ERROR_MORE_DATA; + } + + TRACE ("pass 2: copying all names and values to buffer\n"); + + ppev = (PPRINTER_ENUM_VALUESW) pEnumValues; /* array of structs */ + pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW); + + for (dwIndex = 0; dwIndex < cValues; ++dwIndex) + { + cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen; + ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen, + NULL, &dwType, lpValue, &cbValueLen); + if (ret != ERROR_SUCCESS) + { + HEAPFREE (hHeap, 0, lpValue); + REGCLOSEKEY (hkSubKey); + TRACE ("RegEnumValueW (%li) returned %li\n", dwIndex, ret); + return ret; + } + + cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR); + memcpy (pEnumValues, lpValueName, cbValueNameLen); + ppev[dwIndex].pValueName = (LPWSTR) pEnumValues; + pEnumValues += cbValueNameLen; + + /* return # of *bytes* (including trailing \0), not # of chars */ + ppev[dwIndex].cbValueName = cbValueNameLen; + + ppev[dwIndex].dwType = dwType; + + memcpy (pEnumValues, lpValue, cbValueLen); + ppev[dwIndex].pData = pEnumValues; + pEnumValues += cbValueLen; + + ppev[dwIndex].cbData = cbValueLen; + + TRACE ("%s [%li]: copied name (%li bytes) and data (%li bytes)\n", + debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen); + } + + HEAPFREE (hHeap, 0, lpValue); + REGCLOSEKEY (hkSubKey); + return ERROR_SUCCESS; +} + +/******************************************************************************* + * EnumPrinterDataExA [WINSPOOL.196] + * + * The observant will note that this function returns ASCII strings in Unicode- + * sized buffers (for value names and values of type REG_SZ, REG_EXPAND_SZ, and + * REG_MULTI_SZ). This is what Windows 2000 does. + * + */ +DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, + LPBYTE pEnumValues, DWORD cbEnumValues, + LPDWORD pcbEnumValues, LPDWORD pnEnumValues) +{ + INT len; + LPWSTR pKeyNameW; + DWORD ret, dwIndex, dwBufSize; + HANDLE hHeap; + LPSTR pBuffer; + + TRACE ("%08x %s\n", hPrinter, pKeyName); + + if (pKeyName == NULL || *pKeyName == 0) + return ERROR_INVALID_PARAMETER; + + len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0); + + pKeyNameW = alloca (len * sizeof (WCHAR)); + if (pKeyNameW == NULL) + { + ERR ("Failed to allocate %li bytes on stack\n", + (LONG) len * sizeof (WCHAR)); + return ERROR_OUTOFMEMORY; + } + + if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0) + { + TRACE ("MultiByteToWide Char (%s, %i) failed with code %li\n", pKeyName, + len, GetLastError ()); + return GetLastError (); + } + + ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues, + pcbEnumValues, pnEnumValues); + if (ret != ERROR_SUCCESS) + { + TRACE ("EnumPrinterDataExW returned %li\n", ret); + return ret; + } + + if (*pnEnumValues == 0) /* empty key */ + return ERROR_SUCCESS; + + dwBufSize = 0; + for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex) + { + PPRINTER_ENUM_VALUESW ppev = + &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex]; + + if (dwBufSize < ppev->cbValueName) + dwBufSize = ppev->cbValueName; + + if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ || + ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ)) + dwBufSize = ppev->cbData; + } + + TRACE ("Largest Unicode name or value is %li bytes\n", dwBufSize); + + hHeap = GetProcessHeap (); + if (hHeap == (HANDLE) NULL) + { + ERR ("GetProcessHeap failed\n"); + return ERROR_OUTOFMEMORY; + } + + pBuffer = HeapAlloc (hHeap, 0, dwBufSize); + if (pBuffer == NULL) + { + ERR ("Failed to allocate %li bytes from process heap\n", dwBufSize); + return ERROR_OUTOFMEMORY; + } + + for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex) + { + PPRINTER_ENUM_VALUESW ppev = + &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex]; + + len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName, + ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL, + NULL); + if (len == 0) + { + HEAPFREE (hHeap, 0, pBuffer); + TRACE ("WideCharToMultiByte (%s, %li) failed with code %li\n", + debugstr_w (ppev->pValueName), dwBufSize, GetLastError ()); + return GetLastError (); + } + + memcpy (ppev->pValueName, pBuffer, len); + + TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer); + + if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ && + ppev->dwType != REG_MULTI_SZ) + continue; + + len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData, + ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL); + if (len == 0) + { + HEAPFREE (hHeap, 0, pBuffer); + TRACE ("WideCharToMultiByte (%s, %li) failed with code %li\n", + debugstr_w ((LPWSTR) ppev->pData), dwBufSize, + GetLastError ()); + TRACE (" (only first string of REG_MULTI_SZ printed)\n"); + return GetLastError (); + } + + memcpy (ppev->pData, pBuffer, len); + + TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer); + TRACE (" (only first string of REG_MULTI_SZ printed)\n"); + } + + HEAPFREE (hHeap, 0, pBuffer); + return ERROR_SUCCESS; +} + +#undef REGCLOSEKEY +#undef HEAPFREE
Hi, I don't know anything about the Win32 print API, but style wise I tend to stay away from alloca(). It screws up exception handling because it messes with the stack, and is machine dependant. Maybe LocalAlloc() would be a better choice? Cheers, Jon -- "Don't wait for the seas to part, or messiahs to come, Dont you sit around and waste this chance ..." -Live tntjpgriff(a)tsnxt.co.uk , jon_p_griffiths(a)yahoo.com
Jon Griffiths wrote:
I don't know anything about the Win32 print API, but style wise I tend to stay away from alloca(). It screws up exception handling because it messes with the stack, and is machine dependant. Maybe LocalAlloc() would be a better choice?
According to the Microsoft web site, LocalAlloc is deprecated in favor of HeapAlloc and friends. Can anyone comment on whether using alloca will mess up Wine? (Jon's comment seems to imply that he avoids alloca on general principles, rather than for Wine-specific reasons.) Does Wine use exceptions? Thanks! -- ======================================================================== Ian Pilcher pilcher(a)concentric.net ========================================================================
participants (2)
-
Ian Pilcher -
Jon Griffiths