1 /*
2  * PROJECT:     ReactOS Local Spooler
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions related to Print Monitors
5  * COPYRIGHT:   Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 // Global Variables
11 LIST_ENTRY PrintMonitorList;
12 
13 // Local Constants
14 static DWORD dwMonitorInfo1Offsets[] = {
15     FIELD_OFFSET(MONITOR_INFO_1W, pName),
16     MAXDWORD
17 };
18 
19 static DWORD dwMonitorInfo2Offsets[] = {
20     FIELD_OFFSET(MONITOR_INFO_2W, pName),
21     FIELD_OFFSET(MONITOR_INFO_2W, pEnvironment),
22     FIELD_OFFSET(MONITOR_INFO_2W, pDLLName),
23     MAXDWORD
24 };
25 
26 
27 PLOCAL_PRINT_MONITOR
28 FindPrintMonitor(PCWSTR pwszName)
29 {
30     PLIST_ENTRY pEntry;
31     PLOCAL_PRINT_MONITOR pPrintMonitor;
32 
33     TRACE("FindPrintMonitor(%S)\n", pwszName);
34 
35     if (!pwszName)
36         return NULL;
37 
38     for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
39     {
40         pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
41 
42         if (_wcsicmp(pPrintMonitor->pwszName, pwszName) == 0)
43             return pPrintMonitor;
44     }
45 
46     return NULL;
47 }
48 
49 static LONG WINAPI CreateKey(HANDLE hcKey, LPCWSTR pszSubKey, DWORD dwOptions, REGSAM samDesired, PSECURITY_ATTRIBUTES pSecurityAttributes, PHANDLE phckResult, PDWORD pdwDisposition, HANDLE hSpooler)
50 {
51     FIXME("stub\n");
52     return ERROR_CALL_NOT_IMPLEMENTED;
53 }
54 
55 static LONG WINAPI OpenKey(HANDLE hcKey, LPCWSTR pszSubKey, REGSAM samDesired, PHANDLE phkResult, HANDLE hSpooler)
56 {
57     FIXME("stub\n");
58     return ERROR_CALL_NOT_IMPLEMENTED;
59 }
60 
61 static LONG WINAPI CloseKey(HANDLE hcKey, HANDLE hSpooler)
62 {
63     FIXME("stub\n");
64     return ERROR_CALL_NOT_IMPLEMENTED;
65 }
66 
67 static LONG WINAPI DeleteKey(HANDLE hcKey, LPCWSTR pszSubKey, HANDLE hSpooler)
68 {
69     FIXME("stub\n");
70     return ERROR_CALL_NOT_IMPLEMENTED;
71 }
72 
73 static LONG WINAPI EnumKey(HANDLE hcKey, DWORD dwIndex, LPWSTR pszName, PDWORD pcchName, PFILETIME pftLastWriteTime, HANDLE hSpooler)
74 {
75     FIXME("stub\n");
76     return ERROR_CALL_NOT_IMPLEMENTED;
77 }
78 
79 static LONG WINAPI QueryInfoKey(HANDLE hcKey, PDWORD pcSubKeys, PDWORD pcbKey, PDWORD pcValues, PDWORD pcbValue, PDWORD pcbData, PDWORD pcbSecurityDescriptor, PFILETIME pftLastWriteTime,
80                 HANDLE hSpooler)
81 {
82     FIXME("stub\n");
83     return ERROR_CALL_NOT_IMPLEMENTED;
84 }
85 
86 static LONG WINAPI SetValue(HANDLE hcKey, LPCWSTR pszValue, DWORD dwType, const BYTE* pData, DWORD cbData, HANDLE hSpooler)
87 {
88     FIXME("stub\n");
89     return ERROR_CALL_NOT_IMPLEMENTED;
90 }
91 
92 static LONG WINAPI DeleteValue(HANDLE hcKey, LPCWSTR pszValue, HANDLE hSpooler)
93 {
94     FIXME("stub\n");
95     return ERROR_CALL_NOT_IMPLEMENTED;
96 }
97 
98 static LONG WINAPI EnumValue(HANDLE hcKey, DWORD dwIndex, LPWSTR pszValue, PDWORD pcbValue, PDWORD pType, PBYTE pData, PDWORD pcbData, HANDLE hSpooler)
99 {
100     FIXME("stub\n");
101     return ERROR_CALL_NOT_IMPLEMENTED;
102 }
103 
104 static LONG WINAPI QueryValue(HANDLE hcKey, LPCWSTR pszValue, PDWORD pType, PBYTE pData, PDWORD pcbData, HANDLE hSpooler)
105 {
106     FIXME("stub\n");
107     return ERROR_CALL_NOT_IMPLEMENTED;
108 }
109 
110 static MONITORREG MonReg =
111 {
112     sizeof(MONITORREG),
113     CreateKey,
114     OpenKey,
115     CloseKey,
116     DeleteKey,
117     EnumKey,
118     QueryInfoKey,
119     SetValue,
120     DeleteValue,
121     EnumValue,
122     QueryValue
123 };
124 
125 BOOL
126 InitializePrintMonitorList(void)
127 {
128     const WCHAR wszMonitorsPath[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors";
129     const DWORD cchMonitorsPath = _countof(wszMonitorsPath) - 1;
130 
131     DWORD cchMaxSubKey;
132     DWORD cchPrintMonitorName;
133     DWORD dwErrorCode;
134     DWORD dwSubKeys;
135     DWORD i;
136     HINSTANCE hinstPrintMonitor = NULL;
137     HKEY hKey = NULL;
138     HKEY hSubKey = NULL;
139     MONITORINIT MonitorInit;
140     PInitializePrintMonitor pfnInitializePrintMonitor;
141     PInitializePrintMonitor2 pfnInitializePrintMonitor2;
142     PLOCAL_PRINT_MONITOR pPrintMonitor = NULL;
143     PWSTR pwszRegistryPath = NULL;
144 
145     TRACE("InitializePrintMonitorList()\n");
146 
147     // Initialize an empty list for our Print Monitors.
148     InitializeListHead(&PrintMonitorList);
149 
150     // Open the key containing Print Monitors.
151     dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszMonitorsPath, 0, KEY_READ, &hKey);
152     if (dwErrorCode != ERROR_SUCCESS)
153     {
154         ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
155         goto Cleanup;
156     }
157 
158     // Get the number of Print Providers and maximum sub key length.
159     dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
160     if (dwErrorCode != ERROR_SUCCESS)
161     {
162         ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
163         goto Cleanup;
164     }
165 
166     // Loop through all available Print Providers.
167     for (i = 0; i < dwSubKeys; i++)
168     {
169         // Cleanup tasks from the previous run
170         if (hSubKey)
171         {
172             RegCloseKey(hSubKey);
173             hSubKey = NULL;
174         }
175 
176         if (pwszRegistryPath)
177         {
178             DllFreeSplMem(pwszRegistryPath);
179             pwszRegistryPath = NULL;
180         }
181 
182         if (pPrintMonitor)
183         {
184             if (pPrintMonitor->pwszFileName)
185                 DllFreeSplMem(pPrintMonitor->pwszFileName);
186 
187             if (pPrintMonitor->pwszName)
188                 DllFreeSplMem(pPrintMonitor->pwszName);
189 
190             DllFreeSplMem(pPrintMonitor);
191             pPrintMonitor = NULL;
192         }
193 
194         // Create a new LOCAL_PRINT_MONITOR structure for it.
195         pPrintMonitor = DllAllocSplMem(sizeof(LOCAL_PRINT_MONITOR));
196         if (!pPrintMonitor)
197         {
198             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
199             ERR("DllAllocSplMem failed!\n");
200             goto Cleanup;
201         }
202 
203         memset( pPrintMonitor, 0, sizeof(LOCAL_PRINT_MONITOR));
204 
205         // Allocate memory for the Print Monitor Name.
206         pPrintMonitor->pwszName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
207         if (!pPrintMonitor->pwszName)
208         {
209             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
210             ERR("DllAllocSplMem failed!\n");
211             goto Cleanup;
212         }
213 
214         // Get the name of this Print Monitor.
215         cchPrintMonitorName = cchMaxSubKey + 1;
216         dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, pPrintMonitor->pwszName, &cchPrintMonitorName, NULL, NULL, NULL, NULL);
217         if (dwErrorCode != ERROR_SUCCESS)
218         {
219             ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
220             continue;
221         }
222 
223         // Open this Print Monitor's registry key.
224         dwErrorCode = (DWORD)RegOpenKeyExW(hKey, pPrintMonitor->pwszName, 0, KEY_READ, &hSubKey);
225         if (dwErrorCode != ERROR_SUCCESS)
226         {
227             ERR("RegOpenKeyExW failed for Print Provider \"%S\" with status %lu!\n", pPrintMonitor->pwszName, dwErrorCode);
228             continue;
229         }
230 
231         // Get the file name of the Print Monitor.
232         pPrintMonitor->pwszFileName = AllocAndRegQueryWSZ(hSubKey, L"Driver");
233         if (!pPrintMonitor->pwszFileName)
234             continue;
235 
236         // Try to load it.
237         hinstPrintMonitor = LoadLibraryW(pPrintMonitor->pwszFileName);
238         if (!hinstPrintMonitor)
239         {
240             ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
241             continue;
242         }
243 
244         pPrintMonitor->hModule = hinstPrintMonitor;
245 
246         // Try to find a Level 2 initialization routine first.
247         pfnInitializePrintMonitor2 = (PInitializePrintMonitor2)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor2");
248         if (pfnInitializePrintMonitor2)
249         {
250             // Prepare a MONITORINIT structure.
251             MonitorInit.cbSize = sizeof(MONITORINIT);
252             MonitorInit.bLocal = TRUE;
253 
254             // TODO: Fill the other fields.
255             MonitorInit.hckRegistryRoot = hKey;
256             MonitorInit.pMonitorReg = &MonReg;
257 
258             // Call the Level 2 initialization routine.
259             pPrintMonitor->pMonitor = (PMONITOR2)pfnInitializePrintMonitor2(&MonitorInit, &pPrintMonitor->hMonitor);
260             if (!pPrintMonitor->pMonitor)
261             {
262                 ERR("InitializePrintMonitor2 failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
263                 continue;
264             }
265             FIXME("InitializePrintMonitor2 loaded.\n");
266             pPrintMonitor->bIsLevel2 = TRUE;
267         }
268         else
269         {
270             // Try to find a Level 1 initialization routine then.
271             pfnInitializePrintMonitor = (PInitializePrintMonitor)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor");
272             if (pfnInitializePrintMonitor)
273             {
274                 // Construct the registry path.
275                 pwszRegistryPath = DllAllocSplMem((cchMonitorsPath + 1 + cchPrintMonitorName + 1) * sizeof(WCHAR));
276                 if (!pwszRegistryPath)
277                 {
278                     dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
279                     ERR("DllAllocSplMem failed!\n");
280                     goto Cleanup;
281                 }
282 
283                 CopyMemory(pwszRegistryPath, wszMonitorsPath, cchMonitorsPath * sizeof(WCHAR));
284                 pwszRegistryPath[cchMonitorsPath] = L'\\';
285                 CopyMemory(&pwszRegistryPath[cchMonitorsPath + 1], pPrintMonitor->pwszName, (cchPrintMonitorName + 1) * sizeof(WCHAR));
286 
287                 // Call the Level 1 initialization routine.
288                 pPrintMonitor->pMonitor = (LPMONITOREX)pfnInitializePrintMonitor(pwszRegistryPath);
289                 if (!pPrintMonitor->pMonitor)
290                 {
291                     ERR("InitializePrintMonitor failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
292                     continue;
293                 }
294             }
295             else
296             {
297                 ERR("No initialization routine found for \"%S\"!\n", pPrintMonitor->pwszFileName);
298                 continue;
299             }
300         }
301 
302         // Add this Print Monitor to the list.
303         InsertTailList(&PrintMonitorList, &pPrintMonitor->Entry);
304         FIXME("InitializePrintMonitorList Handle %p\n",pPrintMonitor->hMonitor);
305         pPrintMonitor->refcount++;
306 
307         // Don't let the cleanup routine free this.
308         pPrintMonitor = NULL;
309     }
310 
311     dwErrorCode = ERROR_SUCCESS;
312 
313 Cleanup:
314     // Inside the loop
315     if (hSubKey)
316         RegCloseKey(hSubKey);
317 
318     if (pwszRegistryPath)
319         DllFreeSplMem(pwszRegistryPath);
320 
321     if (pPrintMonitor)
322     {
323         if (pPrintMonitor->pwszFileName)
324             DllFreeSplMem(pPrintMonitor->pwszFileName);
325 
326         if (pPrintMonitor->pwszName)
327             DllFreeSplMem(pPrintMonitor->pwszName);
328 
329         DllFreeSplMem(pPrintMonitor);
330     }
331 
332     // Outside the loop
333     if (hKey)
334         RegCloseKey(hKey);
335 
336     SetLastError(dwErrorCode);
337     return (dwErrorCode == ERROR_SUCCESS);
338 }
339 
340 
341 static void
342 _LocalGetMonitorLevel1(PLOCAL_PRINT_MONITOR pPrintMonitor, PMONITOR_INFO_1W* ppMonitorInfo, PBYTE* ppMonitorInfoEnd, PDWORD pcbNeeded)
343 {
344     DWORD cbMonitorName;
345     PCWSTR pwszStrings[1];
346 
347     // Calculate the string lengths.
348     if (!ppMonitorInfo)
349     {
350         cbMonitorName = (wcslen(pPrintMonitor->pwszName) + 1) * sizeof(WCHAR);
351 
352         *pcbNeeded += sizeof(MONITOR_INFO_1W) + cbMonitorName;
353         return;
354     }
355 
356     // Set the pName field.
357     pwszStrings[0] = pPrintMonitor->pwszName;
358 
359     // Copy the structure and advance to the next one in the output buffer.
360     *ppMonitorInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppMonitorInfo), dwMonitorInfo1Offsets, *ppMonitorInfoEnd);
361     (*ppMonitorInfo)++;
362 }
363 
364 static void
365 _LocalGetMonitorLevel2(PLOCAL_PRINT_MONITOR pPrintMonitor, PMONITOR_INFO_2W* ppMonitorInfo, PBYTE* ppMonitorInfoEnd, PDWORD pcbNeeded)
366 {
367     DWORD cbFileName;
368     DWORD cbMonitorName;
369     PCWSTR pwszStrings[3];
370 
371     // Calculate the string lengths.
372     if (!ppMonitorInfo)
373     {
374         cbMonitorName = (wcslen(pPrintMonitor->pwszName) + 1) * sizeof(WCHAR);
375         cbFileName = (wcslen(pPrintMonitor->pwszFileName) + 1) * sizeof(WCHAR);
376 
377         *pcbNeeded += sizeof(MONITOR_INFO_2W) + cbMonitorName + cbCurrentEnvironment + cbFileName;
378         return;
379     }
380 
381     // Set the pName field.
382     pwszStrings[0] = pPrintMonitor->pwszName;
383 
384     // Set the pEnvironment field.
385     pwszStrings[1] = (PWSTR)wszCurrentEnvironment;
386 
387     // Set the pDLLName field.
388     pwszStrings[2] = pPrintMonitor->pwszFileName;
389 
390     // Copy the structure and advance to the next one in the output buffer.
391     *ppMonitorInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppMonitorInfo), dwMonitorInfo2Offsets, *ppMonitorInfoEnd);
392     (*ppMonitorInfo)++;
393 }
394 
395 BOOL WINAPI
396 LocalEnumMonitors(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
397 {
398     DWORD dwErrorCode;
399     PBYTE pMonitorInfoEnd;
400     PLIST_ENTRY pEntry;
401     PLOCAL_PRINT_MONITOR pPrintMonitor;
402 
403     TRACE("LocalEnumMonitors(%S, %lu, %p, %lu, %p, %p)\n", pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned);
404 
405     // Sanity checks.
406     if (Level > 2)
407     {
408         dwErrorCode = ERROR_INVALID_LEVEL;
409         goto Cleanup;
410     }
411 
412     // Begin counting.
413     *pcbNeeded = 0;
414     *pcReturned = 0;
415 
416     // Count the required buffer size and the number of monitors.
417     for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
418     {
419         pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
420 
421         if (Level == 1)
422             _LocalGetMonitorLevel1(pPrintMonitor, NULL, NULL, pcbNeeded);
423         else if (Level == 2)
424             _LocalGetMonitorLevel2(pPrintMonitor, NULL, NULL, pcbNeeded);
425     }
426 
427     // Check if the supplied buffer is large enough.
428     if (cbBuf < *pcbNeeded)
429     {
430         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
431         goto Cleanup;
432     }
433 
434     // Copy over the Monitor information.
435     pMonitorInfoEnd = &pMonitors[*pcbNeeded];
436 
437     for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
438     {
439         pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
440 
441         if (Level == 1)
442             _LocalGetMonitorLevel1(pPrintMonitor, (PMONITOR_INFO_1W*)&pMonitors, &pMonitorInfoEnd, NULL);
443         else if (Level == 2)
444             _LocalGetMonitorLevel2(pPrintMonitor, (PMONITOR_INFO_2W*)&pMonitors, &pMonitorInfoEnd, NULL);
445 
446         (*pcReturned)++;
447     }
448 
449     dwErrorCode = ERROR_SUCCESS;
450 
451 Cleanup:
452     SetLastError(dwErrorCode);
453     return (dwErrorCode == ERROR_SUCCESS);
454 }
455 
456 BOOL
457 AddPrintMonitorList( LPCWSTR pName, LPWSTR DllName )
458 {
459     const WCHAR wszMonitorsPath[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors\\";
460     const DWORD cchMonitorsPath = _countof(wszMonitorsPath) - 1;
461 
462     WCHAR wszRegRoot[MAX_PATH] = {0};
463 
464     DWORD cchPrintMonitorName;
465     DWORD dwErrorCode;
466     HINSTANCE hinstPrintMonitor = NULL;
467     HKEY hKey = NULL;
468     MONITORINIT MonitorInit;
469     PInitializePrintMonitor pfnInitializePrintMonitor;
470     PInitializePrintMonitor2 pfnInitializePrintMonitor2;
471     PLOCAL_PRINT_MONITOR pPrintMonitor = NULL;
472     PWSTR pwszRegistryPath = NULL;
473 
474     FIXME("AddPrintMonitorList( %S, %S)\n",pName, DllName);
475 
476     StringCbCopyW(wszRegRoot, sizeof(wszRegRoot), wszMonitorsPath);
477     StringCbCatW(wszRegRoot, sizeof(wszRegRoot), pName);
478 
479     // Open the key containing Print Monitors.
480     dwErrorCode = (DWORD)RegOpenKeyW( HKEY_LOCAL_MACHINE, wszRegRoot, &hKey );
481     if (dwErrorCode != ERROR_SUCCESS)
482     {
483         ERR("RegOpenKeyExW %S failed with status %lu!\n", wszRegRoot, dwErrorCode);
484         goto Cleanup;
485     }
486 
487     // Create a new LOCAL_PRINT_MONITOR structure for it.
488     pPrintMonitor = DllAllocSplMem(sizeof(LOCAL_PRINT_MONITOR));
489     if (!pPrintMonitor)
490     {
491         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
492         ERR("DllAllocSplMem failed!\n");
493         goto Cleanup;
494     }
495 
496     memset( pPrintMonitor, 0, sizeof(LOCAL_PRINT_MONITOR));
497 
498     // Allocate memory for the Print Monitor Name.
499     pPrintMonitor->pwszName = AllocSplStr( pName );
500     if (!pPrintMonitor->pwszName)
501     {
502         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
503         ERR("DllAllocSplMem failed!\n");
504         goto Cleanup;
505     }
506 
507     cchPrintMonitorName = wcslen(pPrintMonitor->pwszName);
508 
509     if ( DllName == NULL )
510     {
511         DWORD namesize;
512 
513         dwErrorCode = RegQueryValueExW( hKey, L"Driver", NULL, NULL, NULL, &namesize );
514 
515         if ( dwErrorCode == ERROR_SUCCESS )
516         {
517             DllName = DllAllocSplMem(namesize);
518 
519             RegQueryValueExW( hKey, L"Driver", NULL, NULL, (LPBYTE)DllName, &namesize );
520 
521             pPrintMonitor->pwszFileName = DllName;
522         }
523         else
524         {
525             ERR("DllName not found\n");
526             goto Cleanup;
527         }
528     }
529     else
530     {
531         pPrintMonitor->pwszFileName = AllocSplStr( DllName );
532     }
533 
534     // Try to load it.
535     hinstPrintMonitor = LoadLibraryW(pPrintMonitor->pwszFileName);
536     if (!hinstPrintMonitor)
537     {
538         ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
539         dwErrorCode = GetLastError();
540         goto Cleanup;
541     }
542 
543     pPrintMonitor->hModule = hinstPrintMonitor;
544 
545     // Try to find a Level 2 initialization routine first.
546     pfnInitializePrintMonitor2 = (PInitializePrintMonitor2)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor2");
547     if (pfnInitializePrintMonitor2)
548     {
549         // Prepare a MONITORINIT structure.
550         MonitorInit.cbSize = sizeof(MONITORINIT);
551         MonitorInit.bLocal = TRUE;
552 
553         // TODO: Fill the other fields.
554         MonitorInit.hckRegistryRoot = hKey;
555         MonitorInit.pMonitorReg = &MonReg;
556 
557         // Call the Level 2 initialization routine.
558         pPrintMonitor->pMonitor = (PMONITOR2)pfnInitializePrintMonitor2(&MonitorInit, &pPrintMonitor->hMonitor);
559         if (!pPrintMonitor->pMonitor)
560         {
561             ERR("InitializePrintMonitor2 failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
562             goto Cleanup;
563         }
564 
565         pPrintMonitor->bIsLevel2 = TRUE;
566     }
567     else
568     {
569         // Try to find a Level 1 initialization routine then.
570         pfnInitializePrintMonitor = (PInitializePrintMonitor)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor");
571         if (pfnInitializePrintMonitor)
572         {
573             // Construct the registry path.
574             pwszRegistryPath = DllAllocSplMem((cchMonitorsPath + 1 + cchPrintMonitorName + 1) * sizeof(WCHAR));
575             if (!pwszRegistryPath)
576             {
577                 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
578                 ERR("DllAllocSplMem failed!\n");
579                 goto Cleanup;
580             }
581 
582             CopyMemory(pwszRegistryPath, wszMonitorsPath, cchMonitorsPath * sizeof(WCHAR));
583             pwszRegistryPath[cchMonitorsPath] = L'\\';
584             CopyMemory(&pwszRegistryPath[cchMonitorsPath + 1], pPrintMonitor->pwszName, (cchPrintMonitorName + 1) * sizeof(WCHAR));
585 
586             // Call the Level 1 initialization routine.
587             pPrintMonitor->pMonitor = (LPMONITOREX)pfnInitializePrintMonitor(pwszRegistryPath);
588             if (!pPrintMonitor->pMonitor)
589             {
590                 ERR("InitializePrintMonitor failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
591                 goto Cleanup;
592             }
593         }
594         else
595         {
596             ERR("No initialization routine found for \"%S\"!\n", pPrintMonitor->pwszFileName);
597             dwErrorCode = ERROR_PROC_NOT_FOUND;
598             goto Cleanup;
599         }
600     }
601     // Add this Print Monitor to the list.
602     InsertTailList(&PrintMonitorList, &pPrintMonitor->Entry);
603     FIXME("AddPrintMonitorList Handle %p\n",pPrintMonitor->hMonitor);
604 
605     pPrintMonitor->refcount++;
606 
607     // Don't let the cleanup routine free this.
608     pPrintMonitor = NULL;
609 
610     dwErrorCode = ERROR_SUCCESS;
611 
612 Cleanup:
613     if (pwszRegistryPath)
614         DllFreeSplMem(pwszRegistryPath);
615 
616     if (pPrintMonitor)
617     {
618         if (pPrintMonitor->pwszFileName)
619             DllFreeSplMem(pPrintMonitor->pwszFileName);
620 
621         if (pPrintMonitor->pwszName)
622             DllFreeSplMem(pPrintMonitor->pwszName);
623 
624         DllFreeSplMem(pPrintMonitor);
625     }
626 
627     // Outside the loop
628     if (hKey)
629         RegCloseKey(hKey);
630 
631     SetLastError(dwErrorCode);
632     return (dwErrorCode == ERROR_SUCCESS);
633 }
634 
635 BOOL WINAPI
636 LocalAddMonitor(PWSTR pName, DWORD Level, PBYTE pMonitors)
637 {
638     PPRINTENV_T env;
639     LPMONITOR_INFO_2W mi2w;
640     HKEY hroot = NULL;
641     HKEY hentry = NULL;
642     DWORD disposition;
643     BOOL res = FALSE;
644     const WCHAR wszMonitorsPath[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors\\";
645 
646     mi2w = (LPMONITOR_INFO_2W) pMonitors;
647 
648     FIXME("LocalAddMonitor(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
649         debugstr_w(mi2w->pName), debugstr_w(mi2w->pEnvironment), debugstr_w(mi2w->pDLLName));
650 
651     if (copy_servername_from_name(pName, NULL))
652     {
653         FIXME("server %s not supported\n", debugstr_w(pName));
654         SetLastError(ERROR_ACCESS_DENIED);
655         return FALSE;
656     }
657 
658     if (!mi2w->pName || (!mi2w->pName[0]) )
659     {
660         FIXME("pName not valid : %s\n", debugstr_w(mi2w->pName));
661         SetLastError(ERROR_INVALID_PARAMETER);
662         return FALSE;
663     }
664 
665     env = validate_envW(mi2w->pEnvironment);
666     if (!env)
667         return FALSE;   /* ERROR_INVALID_ENVIRONMENT */
668 
669     if (!mi2w->pDLLName || (!mi2w->pDLLName[0]) )
670     {
671         FIXME("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
672         SetLastError(ERROR_INVALID_PARAMETER);
673         return FALSE;
674     }
675 
676     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, wszMonitorsPath, &hroot) != ERROR_SUCCESS) {
677         ERR("unable to create key %s\n", debugstr_w(wszMonitorsPath));
678         return FALSE;
679     }
680 
681     if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry, &disposition) == ERROR_SUCCESS)
682     {
683         /* Some installers set options for the port before calling AddMonitor.
684            We query the "Driver" entry to verify that the monitor is installed,
685            before we return an error.
686            When a user installs two print monitors at the same time with the
687            same name, a race condition is possible but silently ignored. */
688 
689         DWORD   namesize = 0;
690 
691         if ((disposition == REG_OPENED_EXISTING_KEY) &&
692             (RegQueryValueExW(hentry, L"Driver", NULL, NULL, NULL, &namesize) == ERROR_SUCCESS))
693         {
694             FIXME("monitor %s already exists\n", debugstr_w(mi2w->pName));
695             /* 9x use ERROR_ALREADY_EXISTS */
696             SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
697         }
698         else
699         {
700             INT len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
701 
702             res = (RegSetValueExW(hentry, L"Driver", 0, REG_SZ, (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
703 
704             /* Load and initialize the monitor. SetLastError() is called on failure */
705 
706             res = AddPrintMonitorList( mi2w->pName, mi2w->pDLLName );
707 
708             if ( !res )
709             {
710                 RegDeleteKeyW(hroot, mi2w->pName);
711             }
712             else
713                 SetLastError(ERROR_SUCCESS); /* Monitor installer depends on this */
714         }
715 
716         RegCloseKey(hentry);
717     }
718 
719     RegCloseKey(hroot);
720     return res;
721 }
722 
723 BOOL WINAPI
724 LocalDeleteMonitor(PWSTR pName, PWSTR pEnvironment, PWSTR pMonitorName)
725 {
726     HKEY hroot = NULL;
727     LONG lres;
728     PLOCAL_PRINT_MONITOR pPrintMonitor;
729     const WCHAR wszMonitorsPath[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors\\";
730 
731     FIXME("LocalDeleteMonitor(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
732            debugstr_w(pMonitorName));
733 
734     lres = copy_servername_from_name(pName, NULL);
735     if (lres)
736     {
737         FIXME("server %s not supported\n", debugstr_w(pName));
738         SetLastError(ERROR_INVALID_NAME);
739         return FALSE;
740     }
741 
742     /*  pEnvironment is ignored in Windows for the local Computer */
743     if (!pMonitorName || !pMonitorName[0])
744     {
745         ERR("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
746         SetLastError(ERROR_INVALID_PARAMETER);
747         return FALSE;
748     }
749 
750     pPrintMonitor = FindPrintMonitor( pMonitorName );
751     if ( pPrintMonitor )
752     {
753        if ( pPrintMonitor->refcount ) pPrintMonitor->refcount--;
754 
755        if ( pPrintMonitor->refcount == 0 )
756        {       /* Unload the monitor if it's loaded */
757        RemoveEntryList(&pPrintMonitor->Entry);
758 
759        if ( pPrintMonitor->bIsLevel2 )
760        {
761            PMONITOR2 pm2 = pPrintMonitor->pMonitor;
762            if ( pm2 && pm2->pfnShutdown )
763            {
764                pm2->pfnShutdown(pPrintMonitor->hMonitor);
765            }
766        }
767 
768        if ( pPrintMonitor->hModule )
769            FreeLibrary(pPrintMonitor->hModule);
770 
771        if (pPrintMonitor->pwszFileName)
772            DllFreeSplStr(pPrintMonitor->pwszFileName);
773 
774        if (pPrintMonitor->pwszName)
775            DllFreeSplStr(pPrintMonitor->pwszName);
776 
777        DllFreeSplMem(pPrintMonitor);
778        pPrintMonitor = NULL;
779        }
780     }
781     else
782     {
783        FIXME("Could not find %s\n", debugstr_w(pMonitorName));
784     }
785 
786     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, wszMonitorsPath, &hroot) != ERROR_SUCCESS)
787     {
788         ERR("unable to create key %s\n", debugstr_w(wszMonitorsPath));
789         return FALSE;
790     }
791 
792     if (RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS)
793     {
794         FIXME("%s deleted\n", debugstr_w(pMonitorName));
795         RegCloseKey(hroot);
796         return TRUE;
797     }
798 
799     FIXME("%s does not exist\n", debugstr_w(pMonitorName));
800     RegCloseKey(hroot);
801 
802     /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
803     SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
804     return FALSE;
805 }
806