winspool: (EnumPorts #2) Implement EnumPortsW

Detlef Riekenberg wine.dev at web.de
Fri Nov 3 17:27:16 CST 2006


Changelog:
- winspool: (EnumPorts #2) Implement EnumPortsW

The common way to call a winspool-API is:
1: call winspool with buffer=NULL and cbSize=0 to 
   receive the needed size
2: alloc the buffer
3: call winspool with the allocated buffer to 
   receive the data

Since we must return the needed size in (1),
every Monitor is called twice for EnumPortsW
(and even three times for EnumPortsA).
To avoid this, i added caching.
I have implemented flushing for the cache,
but that is not needed before AddPort / DeletePort.


-- 
 
By by ... Detlef

-------------- next part --------------
>From aa9b4cb602fe58b071b5687ca9d92f5cb1e3e954 Mon Sep 17 00:00:00 2001
From: Detlef Riekenberg <wine.dev at web.de>
Date: Fri, 3 Nov 2006 23:44:14 +0100
Subject: [PATCH] winspool: (EnumPorts #2) Implement EnumPortsW
---
 dlls/winspool.drv/info.c |  222 ++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 214 insertions(+), 8 deletions(-)

diff --git a/dlls/winspool.drv/info.c b/dlls/winspool.drv/info.c
index 1c2ac1b..b1fafd6 100644
--- a/dlls/winspool.drv/info.c
+++ b/dlls/winspool.drv/info.c
@@ -98,6 +98,10 @@ typedef struct {
     HMODULE         hdll;
     DWORD           refcount;
     DWORD           dwMonitorSize;
+    LPPORT_INFO_2W  cache;          /* cached PORT_INFO_2W data */
+    DWORD           pi1_needed;     /* size for PORT_INFO_1W */
+    DWORD           pi2_needed;     /* size for PORT_INFO_2W */
+    DWORD           returned;       /* number of cached PORT_INFO_2W - entries */
 } monitor_t;
 
 typedef struct {
@@ -863,6 +867,26 @@ static void monitor_unload(monitor_t * p
 }
 
 /******************************************************************
+ * monitor_unloadall [internal]
+ *
+ * release all printmonitors and unload them from memory, when needed 
+ */
+
+void monitor_unloadall(void)
+{
+    monitor_t * pm;
+    monitor_t * next;
+
+    EnterCriticalSection(&monitor_handles_cs);
+    /* iterate through the list, with safety against removal */
+    LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
+    {
+        monitor_unload(pm);
+    }
+    LeaveCriticalSection(&monitor_handles_cs);
+}
+
+/******************************************************************
  * monitor_load [internal]
  *
  * load a printmonitor, get the dllname from the registry, when needed 
@@ -1014,6 +1038,135 @@ cleanup:
 }
 
 /******************************************************************
+ * monitor_loadall [internal]
+ *
+ * Load all registered monitors 
+ *
+ */
+
+DWORD monitor_loadall(void)
+{
+    monitor_t * pm;
+    DWORD   registered = 0;
+    DWORD   loaded = 0;
+    HKEY    hmonitors;
+    WCHAR   buffer[MAX_PATH];
+    DWORD   id = 0;
+
+    if (RegOpenKeyW(HKEY_LOCAL_MACHINE, MonitorsW, &hmonitors) == ERROR_SUCCESS) {
+        RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
+                        NULL, NULL, NULL, NULL, NULL);
+
+        TRACE("%d monitors registered\n", registered);
+
+        EnterCriticalSection(&monitor_handles_cs);
+        while (id < registered) {
+            buffer[0] = '\0';
+            RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
+            pm = monitor_load(buffer, NULL);
+            if (pm) loaded++;
+            id++;
+        }
+        LeaveCriticalSection(&monitor_handles_cs);
+        RegCloseKey(hmonitors);
+    }
+    TRACE("%d monitors loaded\n", loaded);
+    return loaded;
+}
+
+/******************************************************************
+ * enumerate the local Ports from all loaded monitors (internal)  
+ *
+ * returns the needed size (in bytes) for pPorts
+ * and  *lpreturned is set to number of entries returned in pPorts
+ *
+ */
+static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
+{
+    monitor_t * pm;
+    LPWSTR      ptr;
+    LPPORT_INFO_2W cache;
+    LPPORT_INFO_2W out;
+    DWORD   res;
+    DWORD   cacheindex;
+    DWORD   outindex = 0;
+    DWORD   needed = 0;
+    DWORD   numentries;
+    DWORD   entrysize;
+
+
+    TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
+    entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
+
+    numentries = *lpreturned;       /* this is 0, when we scan the registry */
+    needed = entrysize * numentries;
+    ptr = (LPWSTR) &pPorts[needed];
+
+    numentries = 0;
+    needed = 0;
+
+    LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
+    {
+        if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
+            if (pm->cache == NULL) {
+                res = pm->monitor->pfnEnumPorts(NULL, 2, NULL, 0, &(pm->pi2_needed), &(pm->returned));
+                if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
+                    pm->cache = HeapAlloc(GetProcessHeap(), 0, (pm->pi2_needed));
+                    res = pm->monitor->pfnEnumPorts(NULL, 2, (LPBYTE) pm->cache, pm->pi2_needed, &(pm->pi2_needed), &(pm->returned));
+                }
+                TRACE("(%s) got %d with %d (cache need %d byte for %d entries)\n", 
+                        debugstr_w(pm->name), res, GetLastError(), pm->pi2_needed, pm->returned);
+                res = FALSE;
+            }     
+            if (pm->cache && (level == 1) && (pm->pi1_needed == 0) && (pm->returned > 0)) {
+                cacheindex = 0;
+                cache = pm->cache;
+                while (cacheindex < (pm->returned)) {
+                    pm->pi1_needed += sizeof(PORT_INFO_1W);
+                    pm->pi1_needed += (lstrlenW(cache->pPortName) + 1) * sizeof(WCHAR);
+                    cache++;
+                    cacheindex++;
+                }
+                TRACE("%d byte for %d cached PORT_INFO_1W entries (%s)\n",
+                        pm->pi1_needed, cacheindex, debugstr_w(pm->name));
+            }
+            numentries += pm->returned;
+            needed += (level == 1) ? pm->pi1_needed : pm->pi2_needed;
+
+            /* fill the buffer, if we have one */
+            if (pPorts && (cbBuf >= needed )) {
+                cacheindex = 0;
+                cache = pm->cache;
+                while (cacheindex < pm->returned) {
+                    out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
+                    out->pPortName = ptr;
+                    lstrcpyW(ptr, cache->pPortName);
+                    ptr += (lstrlenW(ptr)+1);
+                    if (level > 1) {
+                        out->pMonitorName = ptr;
+                        lstrcpyW(ptr,  cache->pMonitorName);
+                        ptr += (lstrlenW(ptr)+1);
+
+                        out->pDescription = ptr;
+                        lstrcpyW(ptr,  cache->pDescription);
+                        ptr += (lstrlenW(ptr)+1);
+                        out->fPortType = cache->fPortType;
+                        out->Reserved = cache->Reserved;
+                    }
+                    cache++;
+                    cacheindex++;
+                    outindex++;
+                }
+            }
+        }
+    }
+
+    *lpreturned = numentries;
+    TRACE("need %d byte for %d entries\n", needed, numentries);
+    return needed;
+}
+
+/******************************************************************
  *  get_opened_printer_entry
  *  Get the first place empty in the opened printer table
  *
@@ -4685,16 +4838,69 @@ BOOL WINAPI EnumPortsA(LPSTR name,DWORD 
  *  Success: TRUE
  *  Failure: FALSE and in bufneeded the Bytes required for buffer, if bufsize is too small
  *
- * BUGS
- *  UNICODE-Version is a stub
- *
  */
-BOOL WINAPI EnumPortsW(LPWSTR name,DWORD level,LPBYTE buffer,DWORD bufsize,
-                       LPDWORD bufneeded,LPDWORD bufreturned)
+
+BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
 {
-    FIXME("(%s,%d,%p,%d,%p,%p) - stub\n",
-          debugstr_w(name),level,buffer,bufsize,bufneeded,bufreturned);
-    return FALSE;
+    DWORD   needed = 0;
+    DWORD   numentries = 0;
+    BOOL    res = FALSE;
+
+    TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
+          cbBuf, pcbNeeded, pcReturned);
+
+    if (pName && (pName[0])) {
+        FIXME("not implemented for Server %s\n", debugstr_w(pName));
+        SetLastError(ERROR_ACCESS_DENIED);
+        goto emP_cleanup;
+    }
+
+    /* Level is not checked in win9x */
+    if (!Level || (Level > 2)) {
+        WARN("level (%d) is ignored in win9x\n", Level);
+        SetLastError(ERROR_INVALID_LEVEL);
+        goto emP_cleanup;
+    }
+    if (!pcbNeeded) {
+        SetLastError(RPC_X_NULL_REF_POINTER);
+        goto emP_cleanup;
+    }
+
+    EnterCriticalSection(&monitor_handles_cs);
+    monitor_loadall();
+
+    /* Scan all local Ports */
+    numentries = 0;
+    needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
+
+    /* we calculated the needed buffersize. now do the error-checks */
+    if (cbBuf < needed) {
+        monitor_unloadall();
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        goto emP_cleanup_cs;
+    }
+    else if (!pPorts || !pcReturned) {
+        monitor_unloadall();
+        SetLastError(RPC_X_NULL_REF_POINTER);
+        goto emP_cleanup_cs;
+    }
+
+    /* Fill the Buffer */
+    needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
+    res = TRUE;
+    monitor_unloadall();
+
+emP_cleanup_cs:
+    LeaveCriticalSection(&monitor_handles_cs);
+
+emP_cleanup:
+    if (pcbNeeded)  *pcbNeeded = needed;
+    if (pcReturned) *pcReturned = (res) ? numentries : 0;
+
+    TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
+            (res), GetLastError(), needed, (res)? numentries : 0, numentries);
+
+    return (res);
 }
 
 /******************************************************************************
-- 
1.4.1



More information about the wine-patches mailing list