1 /*
2  * Unit test suite for localspl API functions: local print monitor
3  *
4  * Copyright 2006-2007 Detlef Riekenberg
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21 
22 #include <stdarg.h>
23 
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "wingdi.h"
28 #include "winreg.h"
29 
30 #include "winspool.h"
31 #include "ddk/winsplp.h"
32 
33 #include "wine/test.h"
34 
35 
36 /* ##### */
37 
38 static HMODULE  hdll;
39 static HMODULE  hlocalmon;
40 static LPMONITOREX (WINAPI *pInitializePrintMonitor)(LPWSTR);
41 
42 static LPMONITOREX pm;
43 static BOOL  (WINAPI *pEnumPorts)(LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD);
44 static BOOL  (WINAPI *pOpenPort)(LPWSTR, PHANDLE);
45 static BOOL  (WINAPI *pOpenPortEx)(LPWSTR, LPWSTR, PHANDLE, struct _MONITOR *);
46 static BOOL  (WINAPI *pStartDocPort)(HANDLE, LPWSTR, DWORD, DWORD, LPBYTE);
47 static BOOL  (WINAPI *pWritePort)(HANDLE hPort, LPBYTE, DWORD, LPDWORD);
48 static BOOL  (WINAPI *pReadPort)(HANDLE hPort, LPBYTE, DWORD, LPDWORD);
49 static BOOL  (WINAPI *pEndDocPort)(HANDLE);
50 static BOOL  (WINAPI *pClosePort)(HANDLE);
51 static BOOL  (WINAPI *pAddPort)(LPWSTR, HWND, LPWSTR);
52 static BOOL  (WINAPI *pAddPortEx)(LPWSTR, DWORD, LPBYTE, LPWSTR);
53 static BOOL  (WINAPI *pConfigurePort)(LPWSTR, HWND, LPWSTR);
54 static BOOL  (WINAPI *pDeletePort)(LPWSTR, HWND, LPWSTR);
55 static BOOL  (WINAPI *pGetPrinterDataFromPort)(HANDLE, DWORD, LPWSTR, LPWSTR, DWORD, LPWSTR, DWORD, LPDWORD);
56 static BOOL  (WINAPI *pSetPortTimeOuts)(HANDLE, LPCOMMTIMEOUTS, DWORD);
57 static BOOL  (WINAPI *pXcvOpenPort)(LPCWSTR, ACCESS_MASK, PHANDLE phXcv);
58 static DWORD (WINAPI *pXcvDataPort)(HANDLE, LPCWSTR, PBYTE, DWORD, PBYTE, DWORD, PDWORD);
59 static BOOL  (WINAPI *pXcvClosePort)(HANDLE);
60 
61 static HANDLE hXcv;
62 static HANDLE hXcv_noaccess;
63 
64 /* ########################### */
65 
66 static const WCHAR cmd_AddPortW[] = {'A','d','d','P','o','r','t',0};
67 static const WCHAR cmd_ConfigureLPTPortCommandOKW[] = {'C','o','n','f','i','g','u','r','e',
68                                     'L','P','T','P','o','r','t',
69                                     'C','o','m','m','a','n','d','O','K',0};
70 static const WCHAR cmd_DeletePortW[] = {'D','e','l','e','t','e','P','o','r','t',0};
71 static const WCHAR cmd_GetTransmissionRetryTimeoutW[] = {'G','e','t',
72                                     'T','r','a','n','s','m','i','s','s','i','o','n',
73                                     'R','e','t','r','y','T','i','m','e','o','u','t',0};
74 
75 static const WCHAR cmd_MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
76 static const WCHAR cmd_MonitorUI_lcaseW[] = {'m','o','n','i','t','o','r','u','i',0};
77 static const WCHAR cmd_PortIsValidW[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
78 static WCHAR does_not_existW[] = {'d','o','e','s','_','n','o','t','_','e','x','i','s','t',0};
79 static const CHAR emptyA[] = "";
80 static WCHAR emptyW[] = {0};
81 static WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
82 static WCHAR Monitors_LocalPortW[] = {
83                                 'S','y','s','t','e','m','\\',
84                                 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
85                                 'C','o','n','t','r','o','l','\\',
86                                 'P','r','i','n','t','\\',
87                                 'M','o','n','i','t','o','r','s','\\',
88                                 'L','o','c','a','l',' ','P','o','r','t',0};
89 
90 static const CHAR num_0A[] = "0";
91 static WCHAR num_0W[] = {'0',0};
92 static const CHAR num_1A[] = "1";
93 static WCHAR num_1W[] = {'1',0};
94 static const CHAR num_999999A[] = "999999";
95 static WCHAR num_999999W[] = {'9','9','9','9','9','9',0};
96 static const CHAR num_1000000A[] = "1000000";
97 static WCHAR num_1000000W[] = {'1','0','0','0','0','0','0',0};
98 
99 static const WCHAR portname_comW[]  = {'C','O','M',0};
100 static WCHAR portname_com1W[] = {'C','O','M','1',':',0};
101 static WCHAR portname_com2W[] = {'C','O','M','2',':',0};
102 static WCHAR portname_fileW[] = {'F','I','L','E',':',0};
103 static const WCHAR portname_lptW[]  = {'L','P','T',0};
104 static WCHAR portname_lpt1W[] = {'L','P','T','1',':',0};
105 static WCHAR portname_lpt2W[] = {'L','P','T','2',':',0};
106 static WCHAR server_does_not_existW[] = {'\\','\\','d','o','e','s','_','n','o','t','_','e','x','i','s','t',0};
107 
108 static const CHAR TransmissionRetryTimeoutA[] = {'T','r','a','n','s','m','i','s','s','i','o','n',
109                                     'R','e','t','r','y','T','i','m','e','o','u','t',0};
110 
111 static const CHAR WinNT_CV_WindowsA[] = {'S','o','f','t','w','a','r','e','\\',
112                                          'M','i','c','r','o','s','o','f','t','\\',
113                                          'W','i','n','d','o','w','s',' ','N','T','\\',
114                                          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
115                                          'W','i','n','d','o','w','s',0};
116 static WCHAR wineW[] = {'W','i','n','e',0};
117 
118 static WCHAR tempdirW[MAX_PATH];
119 static WCHAR tempfileW[MAX_PATH];
120 
121 #define PORTNAME_PREFIX  3
122 #define PORTNAME_MINSIZE 5
123 #define PORTNAME_MAXSIZE 10
124 static WCHAR have_com[PORTNAME_MAXSIZE];
125 static WCHAR have_lpt[PORTNAME_MAXSIZE];
126 static WCHAR have_file[PORTNAME_MAXSIZE];
127 
128 /* ########################### */
129 
130 static DWORD delete_port(LPWSTR portname)
131 {
132     DWORD   res;
133 
134     if (pDeletePort) {
135         res = pDeletePort(NULL, 0, portname);
136     }
137     else
138     {
139         res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) portname, (lstrlenW(portname) + 1) * sizeof(WCHAR), NULL, 0, NULL);
140     }
141     return res;
142 }
143 
144 /* ########################### */
145 
146 static void find_installed_ports(void)
147 {
148     PORT_INFO_1W * pi = NULL;
149     WCHAR   nameW[PORTNAME_MAXSIZE];
150     DWORD   needed;
151     DWORD   returned;
152     DWORD   res;
153     DWORD   id;
154 
155     have_com[0] = '\0';
156     have_lpt[0] = '\0';
157     have_file[0] = '\0';
158 
159     if (!pEnumPorts) return;
160 
161     res = pEnumPorts(NULL, 1, NULL, 0, &needed, &returned);
162     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
163         pi = HeapAlloc(GetProcessHeap(), 0, needed);
164     }
165     res = pEnumPorts(NULL, 1, (LPBYTE) pi, needed, &needed, &returned);
166 
167     if (!res) {
168         skip("no ports found\n");
169         HeapFree(GetProcessHeap(), 0, pi);
170         return;
171     }
172 
173     id = 0;
174     while (id < returned) {
175         res = lstrlenW(pi[id].pName);
176         if ((res >= PORTNAME_MINSIZE) && (res < PORTNAME_MAXSIZE) &&
177             (pi[id].pName[res-1] == ':')) {
178             /* copy only the prefix ("LPT" or "COM") */
179             memcpy(&nameW, pi[id].pName, PORTNAME_PREFIX * sizeof(WCHAR));
180             nameW[PORTNAME_PREFIX] = '\0';
181 
182             if (!have_com[0] && (lstrcmpiW(nameW, portname_comW) == 0)) {
183                 memcpy(&have_com, pi[id].pName, (res+1) * sizeof(WCHAR));
184             }
185 
186             if (!have_lpt[0] && (lstrcmpiW(nameW, portname_lptW) == 0)) {
187                 memcpy(&have_lpt, pi[id].pName, (res+1) * sizeof(WCHAR));
188             }
189 
190             if (!have_file[0] && (lstrcmpiW(pi[id].pName, portname_fileW) == 0)) {
191                 memcpy(&have_file, pi[id].pName, (res+1) * sizeof(WCHAR));
192             }
193         }
194         id++;
195     }
196 
197     HeapFree(GetProcessHeap(), 0, pi);
198 }
199 
200 /* ########################### */
201 
202 static void test_AddPort(void)
203 {
204     DWORD   res;
205 
206     /* moved to localui.dll since w2k */
207     if (!pAddPort) return;
208 
209     if (0)
210     {
211         /* NT4 crash on this test */
212         pAddPort(NULL, 0, NULL);
213     }
214 
215     /*  Testing-Results (localmon.dll from NT4.0):
216         - The Servername is ignored
217         - Case of MonitorName is ignored
218     */
219 
220     SetLastError(0xdeadbeef);
221     res = pAddPort(NULL, 0, emptyW);
222     ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
223 
224     SetLastError(0xdeadbeef);
225     res = pAddPort(NULL, 0, does_not_existW);
226     ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
227 
228 }
229 
230 /* ########################### */
231 
232 static void test_AddPortEx(void)
233 {
234     PORT_INFO_2W pi;
235     DWORD   res;
236 
237     if (!pAddPortEx) {
238         skip("AddPortEx\n");
239         return;
240     }
241     if ((!pDeletePort) &&  (!hXcv)) {
242         skip("No API to delete a Port\n");
243         return;
244     }
245 
246     /* start test with clean ports */
247     delete_port(tempfileW);
248 
249     pi.pPortName = tempfileW;
250     if (0) {
251         /* tests crash with native localspl.dll in w2k,
252            but works with native localspl.dll in wine */
253         SetLastError(0xdeadbeef);
254         res = pAddPortEx(NULL, 1, (LPBYTE) &pi, LocalPortW);
255         trace("returned %u with %u\n", res, GetLastError() );
256         ok( res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
257 
258         /* port already exists: */
259         SetLastError(0xdeadbeef);
260         res = pAddPortEx(NULL, 1, (LPBYTE) &pi, LocalPortW);
261         trace("returned %u with %u\n", res, GetLastError() );
262         ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
263             "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
264             res, GetLastError());
265         delete_port(tempfileW);
266 
267 
268         /*  NULL for pMonitorName is documented for Printmonitors, but
269             localspl.dll fails always with ERROR_INVALID_PARAMETER  */
270         SetLastError(0xdeadbeef);
271         res = pAddPortEx(NULL, 1, (LPBYTE) &pi, NULL);
272         trace("returned %u with %u\n", res, GetLastError() );
273         ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
274             "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
275             res, GetLastError());
276         if (res) delete_port(tempfileW);
277 
278 
279         SetLastError(0xdeadbeef);
280         res = pAddPortEx(NULL, 1, (LPBYTE) &pi, emptyW);
281         trace("returned %u with %u\n", res, GetLastError() );
282         ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
283             "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
284             res, GetLastError());
285         if (res) delete_port(tempfileW);
286 
287 
288         SetLastError(0xdeadbeef);
289         res = pAddPortEx(NULL, 1, (LPBYTE) &pi, does_not_existW);
290         trace("returned %u with %u\n", res, GetLastError() );
291         ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
292             "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
293             res, GetLastError());
294         if (res) delete_port(tempfileW);
295     }
296 
297     pi.pPortName = NULL;
298     SetLastError(0xdeadbeef);
299     res = pAddPortEx(NULL, 1, (LPBYTE) &pi, LocalPortW);
300     ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
301         "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
302         res, GetLastError());
303 
304     /*  level 2 is documented as supported for Printmonitors,
305         but localspl.dll fails always with ERROR_INVALID_LEVEL */
306 
307     pi.pPortName = tempfileW;
308     pi.pMonitorName = LocalPortW;
309     pi.pDescription = wineW;
310     pi.fPortType = PORT_TYPE_WRITE;
311 
312     SetLastError(0xdeadbeef);
313     res = pAddPortEx(NULL, 2, (LPBYTE) &pi, LocalPortW);
314     ok( !res && (GetLastError() == ERROR_INVALID_LEVEL),
315         "got %u with %u (expected '0' with ERROR_INVALID_LEVEL)\n",
316         res, GetLastError());
317     if (res) delete_port(tempfileW);
318 
319 
320     /* invalid levels */
321     SetLastError(0xdeadbeef);
322     res = pAddPortEx(NULL, 0, (LPBYTE) &pi, LocalPortW);
323     ok( !res && (GetLastError() == ERROR_INVALID_LEVEL),
324         "got %u with %u (expected '0' with ERROR_INVALID_LEVEL)\n",
325         res, GetLastError());
326     if (res) delete_port(tempfileW);
327 
328 
329     SetLastError(0xdeadbeef);
330     res = pAddPortEx(NULL, 3, (LPBYTE) &pi, LocalPortW);
331     ok( !res && (GetLastError() == ERROR_INVALID_LEVEL),
332         "got %u with %u (expected '0' with ERROR_INVALID_LEVEL)\n",
333         res, GetLastError());
334     if (res) delete_port(tempfileW);
335 
336     /* cleanup */
337     delete_port(tempfileW);
338 }
339 
340 /* ########################### */
341 
342 static void test_ClosePort(void)
343 {
344     HANDLE  hPort;
345     HANDLE  hPort2;
346     LPWSTR  nameW = NULL;
347     DWORD   res;
348     DWORD   res2;
349 
350 
351     if (!pOpenPort || !pClosePort) return;
352 
353     if (have_com[0]) {
354         nameW = have_com;
355 
356         hPort = (HANDLE) 0xdeadbeef;
357         res = pOpenPort(nameW, &hPort);
358         hPort2 = (HANDLE) 0xdeadbeef;
359         res2 = pOpenPort(nameW, &hPort2);
360 
361         if (res2 && (hPort2 != hPort)) {
362             SetLastError(0xdeadbeef);
363             res2 = pClosePort(hPort2);
364             ok(res2, "got %u with %u (expected '!= 0')\n", res2, GetLastError());
365         }
366 
367         if (res) {
368             SetLastError(0xdeadbeef);
369             res = pClosePort(hPort);
370             ok(res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
371         }
372     }
373 
374 
375     if (have_lpt[0]) {
376         nameW = have_lpt;
377 
378         hPort = (HANDLE) 0xdeadbeef;
379         res = pOpenPort(nameW, &hPort);
380         hPort2 = (HANDLE) 0xdeadbeef;
381         res2 = pOpenPort(nameW, &hPort2);
382 
383         if (res2 && (hPort2 != hPort)) {
384             SetLastError(0xdeadbeef);
385             res2 = pClosePort(hPort2);
386             ok(res2, "got %u with %u (expected '!= 0')\n", res2, GetLastError());
387         }
388 
389         if (res) {
390             SetLastError(0xdeadbeef);
391             res = pClosePort(hPort);
392             ok(res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
393         }
394     }
395 
396 
397     if (have_file[0]) {
398         nameW = have_file;
399 
400         hPort = (HANDLE) 0xdeadbeef;
401         res = pOpenPort(nameW, &hPort);
402         hPort2 = (HANDLE) 0xdeadbeef;
403         res2 = pOpenPort(nameW, &hPort2);
404 
405         if (res2 && (hPort2 != hPort)) {
406             SetLastError(0xdeadbeef);
407             res2 = pClosePort(hPort2);
408             ok(res2, "got %u with %u (expected '!= 0')\n", res2, GetLastError());
409         }
410 
411         if (res) {
412             SetLastError(0xdeadbeef);
413             res = pClosePort(hPort);
414             ok(res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
415         }
416 
417     }
418 
419     if (0) {
420         /* an invalid HANDLE crash native localspl.dll */
421 
422         SetLastError(0xdeadbeef);
423         res = pClosePort(NULL);
424         trace("got %u with %u\n", res, GetLastError());
425 
426         SetLastError(0xdeadbeef);
427         res = pClosePort( (HANDLE) 0xdeadbeef);
428         trace("got %u with %u\n", res, GetLastError());
429 
430         SetLastError(0xdeadbeef);
431         res = pClosePort(INVALID_HANDLE_VALUE);
432         trace("got %u with %u\n", res, GetLastError());
433     }
434 
435 }
436 
437 /* ########################### */
438 
439 static void test_ConfigurePort(void)
440 {
441     DWORD   res;
442 
443     /* moved to localui.dll since w2k */
444     if (!pConfigurePort) return;
445 
446     if (0)
447     {
448         /* NT4 crash on this test */
449         pConfigurePort(NULL, 0, NULL);
450     }
451 
452     /*  Testing-Results (localmon.dll from NT4.0):
453         - Case of Portname is ignored
454         - "COM1:" and "COM01:" are the same (Compared by value)
455         - Portname without ":" => Dialog "Nothing to configure" comes up; Success
456         - "LPT1:", "LPT0:" and "LPT:" are the same (Numbers in "LPT:" are ignored)
457         - Empty Servername (LPT1:) => Dialog comes up (Servername is ignored)
458         - "FILE:" => Dialog "Nothing to configure" comes up; Success
459         - Empty Portname =>  => Dialog "Nothing to configure" comes up; Success
460         - Port "does_not_exist" => Dialog "Nothing to configure" comes up; Success
461     */
462     if (winetest_interactive > 0) {
463 
464         SetLastError(0xdeadbeef);
465         res = pConfigurePort(NULL, 0, portname_com1W);
466         trace("returned %d with %u\n", res, GetLastError());
467 
468         SetLastError(0xdeadbeef);
469         res = pConfigurePort(NULL, 0, portname_lpt1W);
470         trace("returned %d with %u\n", res, GetLastError());
471 
472         SetLastError(0xdeadbeef);
473         res = pConfigurePort(NULL, 0, portname_fileW);
474         trace("returned %d with %u\n", res, GetLastError());
475     }
476 }
477 
478 /* ########################### */
479 
480 static void test_DeletePort(void)
481 {
482     DWORD   res;
483 
484     /* moved to localui.dll since w2k */
485     if (!pDeletePort) return;
486 
487     if (0)
488     {
489         /* NT4 crash on this test */
490         pDeletePort(NULL, 0, NULL);
491     }
492 
493     /*  Testing-Results (localmon.dll from NT4.0):
494         - Case of Portname is ignored (returned '1' on Success)
495         - "COM1:" and "COM01:" are different (Compared as string)
496         - server_does_not_exist (LPT1:) => Port deleted, Success (Servername is ignored)
497         - Empty Portname =>  => FALSE (LastError not changed)
498         - Port "does_not_exist" => FALSE (LastError not changed)
499     */
500 
501     SetLastError(0xdeadbeef);
502     res = pDeletePort(NULL, 0, emptyW);
503     ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
504 
505     SetLastError(0xdeadbeef);
506     res = pDeletePort(NULL, 0, does_not_existW);
507     ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
508 
509 }
510 
511 /* ########################### */
512 
513 static void test_EnumPorts(void)
514 {
515     DWORD   res;
516     DWORD   level;
517     LPBYTE  buffer;
518     DWORD   cbBuf;
519     DWORD   pcbNeeded;
520     DWORD   pcReturned;
521 
522     if (!pEnumPorts) return;
523 
524     /* valid levels are 1 and 2 */
525     for(level = 0; level < 4; level++) {
526 
527         cbBuf = 0xdeadbeef;
528         pcReturned = 0xdeadbeef;
529         SetLastError(0xdeadbeef);
530         res = pEnumPorts(NULL, level, NULL, 0, &cbBuf, &pcReturned);
531 
532         /* use only a short test, when we test with an invalid level */
533         if(!level || (level > 2)) {
534             /* NT4 fails with ERROR_INVALID_LEVEL (as expected)
535                XP succeeds with ERROR_SUCCESS () */
536             ok( (cbBuf == 0) && (pcReturned == 0),
537                 "(%d) returned %d with %u and %d, %d (expected 0, 0)\n",
538                 level, res, GetLastError(), cbBuf, pcReturned);
539             continue;
540         }
541 
542         ok( !res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER),
543             "(%d) returned %d with %u and %d, %d (expected '0' with "
544             "ERROR_INSUFFICIENT_BUFFER)\n",
545             level, res, GetLastError(), cbBuf, pcReturned);
546 
547         buffer = HeapAlloc(GetProcessHeap(), 0, cbBuf * 2);
548         if (buffer == NULL) continue;
549 
550         pcbNeeded = 0xdeadbeef;
551         pcReturned = 0xdeadbeef;
552         SetLastError(0xdeadbeef);
553         res = pEnumPorts(NULL, level, buffer, cbBuf, &pcbNeeded, &pcReturned);
554         ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
555             level, res, GetLastError(), pcbNeeded, pcReturned);
556         /* We can compare the returned Data with the Registry / "win.ini",[Ports] here */
557 
558         pcbNeeded = 0xdeadbeef;
559         pcReturned = 0xdeadbeef;
560         SetLastError(0xdeadbeef);
561         res = pEnumPorts(NULL, level, buffer, cbBuf+1, &pcbNeeded, &pcReturned);
562         ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
563             level, res, GetLastError(), pcbNeeded, pcReturned);
564 
565         pcbNeeded = 0xdeadbeef;
566         pcReturned = 0xdeadbeef;
567         SetLastError(0xdeadbeef);
568         res = pEnumPorts(NULL, level, buffer, cbBuf-1, &pcbNeeded, &pcReturned);
569         ok( !res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER),
570             "(%d) returned %d with %u and %d, %d (expected '0' with "
571             "ERROR_INSUFFICIENT_BUFFER)\n",
572             level, res, GetLastError(), pcbNeeded, pcReturned);
573 
574         if (0)
575         {
576             /* The following tests crash this app with native localmon/localspl */
577             pEnumPorts(NULL, level, NULL, cbBuf, &pcbNeeded, &pcReturned);
578             pEnumPorts(NULL, level, buffer, cbBuf, NULL, &pcReturned);
579             pEnumPorts(NULL, level, buffer, cbBuf, &pcbNeeded, NULL);
580         }
581 
582         /* The Servername is ignored */
583         pcbNeeded = 0xdeadbeef;
584         pcReturned = 0xdeadbeef;
585         SetLastError(0xdeadbeef);
586         res = pEnumPorts(emptyW, level, buffer, cbBuf+1, &pcbNeeded, &pcReturned);
587         ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
588             level, res, GetLastError(), pcbNeeded, pcReturned);
589 
590         pcbNeeded = 0xdeadbeef;
591         pcReturned = 0xdeadbeef;
592         SetLastError(0xdeadbeef);
593         res = pEnumPorts(server_does_not_existW, level, buffer, cbBuf+1, &pcbNeeded, &pcReturned);
594         ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
595             level, res, GetLastError(), pcbNeeded, pcReturned);
596 
597         HeapFree(GetProcessHeap(), 0, buffer);
598     }
599 }
600 
601 /* ########################### */
602 
603 
604 static void test_InitializePrintMonitor(void)
605 {
606     LPMONITOREX res;
607 
608     SetLastError(0xdeadbeef);
609     res = pInitializePrintMonitor(NULL);
610     /* The Parameter was unchecked before w2k */
611     ok( res || (GetLastError() == ERROR_INVALID_PARAMETER),
612         "returned %p with %u\n (expected '!= NULL' or: NULL with "
613         "ERROR_INVALID_PARAMETER)\n", res, GetLastError());
614 
615     SetLastError(0xdeadbeef);
616     res = pInitializePrintMonitor(emptyW);
617     ok( res || (GetLastError() == ERROR_INVALID_PARAMETER),
618         "returned %p with %u\n (expected '!= NULL' or: NULL with "
619         "ERROR_INVALID_PARAMETER)\n", res, GetLastError());
620 
621 
622     /* Every call with a non-empty string returns the same Pointer */
623     SetLastError(0xdeadbeef);
624     res = pInitializePrintMonitor(Monitors_LocalPortW);
625     ok( res == pm,
626         "returned %p with %u (expected %p)\n", res, GetLastError(), pm);
627 }
628 
629 
630 /* ########################### */
631 
632 static void test_OpenPort(void)
633 {
634     HANDLE  hPort;
635     HANDLE  hPort2;
636     LPWSTR  nameW = NULL;
637     DWORD   res;
638     DWORD   res2;
639 
640     if (!pOpenPort || !pClosePort) return;
641 
642     if (have_com[0]) {
643         nameW = have_com;
644 
645         hPort = (HANDLE) 0xdeadbeef;
646         SetLastError(0xdeadbeef);
647         res = pOpenPort(nameW, &hPort);
648         ok( res, "got %u with %u and %p (expected '!= 0')\n",
649             res, GetLastError(), hPort);
650 
651         /* the same HANDLE is returned for a second OpenPort in native localspl */
652         hPort2 = (HANDLE) 0xdeadbeef;
653         SetLastError(0xdeadbeef);
654         res2 = pOpenPort(nameW, &hPort2);
655         ok( res2, "got %u with %u and %p (expected '!= 0')\n",
656             res2, GetLastError(), hPort2);
657 
658         if (res) pClosePort(hPort);
659         if (res2 && (hPort2 != hPort)) pClosePort(hPort2);
660     }
661 
662     if (have_lpt[0]) {
663         nameW = have_lpt;
664 
665         hPort = (HANDLE) 0xdeadbeef;
666         SetLastError(0xdeadbeef);
667         res = pOpenPort(nameW, &hPort);
668         ok( res || (GetLastError() == ERROR_ACCESS_DENIED),
669             "got %u with %u and %p (expected '!= 0' or '0' with ERROR_ACCESS_DENIED)\n",
670             res, GetLastError(), hPort);
671 
672         /* the same HANDLE is returned for a second OpenPort in native localspl */
673         hPort2 = (HANDLE) 0xdeadbeef;
674         SetLastError(0xdeadbeef);
675         res2 = pOpenPort(nameW, &hPort2);
676         ok( res2 || (GetLastError() == ERROR_ACCESS_DENIED),
677             "got %u with %u and %p (expected '!= 0' or '0' with ERROR_ACCESS_DENIED)\n",
678             res2, GetLastError(), hPort2);
679 
680         if (res) pClosePort(hPort);
681         if (res2 && (hPort2 != hPort)) pClosePort(hPort2);
682     }
683 
684     if (have_file[0]) {
685         nameW = have_file;
686 
687         hPort = (HANDLE) 0xdeadbeef;
688         SetLastError(0xdeadbeef);
689         res = pOpenPort(nameW, &hPort);
690         ok( res, "got %u with %u and %p (expected '!= 0')\n",
691             res, GetLastError(), hPort);
692 
693         /* a different HANDLE is returned for a second OpenPort */
694         hPort2 = (HANDLE) 0xdeadbeef;
695         SetLastError(0xdeadbeef);
696         res2 = pOpenPort(nameW, &hPort2);
697         ok( res2 && (hPort2 != hPort),
698             "got %u with %u and %p (expected '!= 0' and '!= %p')\n",
699             res2, GetLastError(), hPort2, hPort);
700 
701         if (res) pClosePort(hPort);
702         if (res2 && (hPort2 != hPort)) pClosePort(hPort2);
703     }
704 
705     if (0) {
706         /* this test crash native localspl (w2k+xp) */
707         if (nameW) {
708             hPort = (HANDLE) 0xdeadbeef;
709             SetLastError(0xdeadbeef);
710             res = pOpenPort(nameW, NULL);
711             trace("got %u with %u and %p\n", res, GetLastError(), hPort);
712         }
713     }
714 
715     hPort = (HANDLE) 0xdeadbeef;
716     SetLastError(0xdeadbeef);
717     res = pOpenPort(does_not_existW, &hPort);
718     ok (!res && (hPort == (HANDLE) 0xdeadbeef),
719         "got %u with 0x%x and %p (expected '0' and 0xdeadbeef)\n", res, GetLastError(), hPort);
720     if (res) pClosePort(hPort);
721 
722     hPort = (HANDLE) 0xdeadbeef;
723     SetLastError(0xdeadbeef);
724     res = pOpenPort(emptyW, &hPort);
725     ok (!res && (hPort == (HANDLE) 0xdeadbeef),
726         "got %u with 0x%x and %p (expected '0' and 0xdeadbeef)\n", res, GetLastError(), hPort);
727     if (res) pClosePort(hPort);
728 
729 
730     /* NULL as name crash native localspl (w2k+xp) */
731     if (0) {
732         hPort = (HANDLE) 0xdeadbeef;
733         SetLastError(0xdeadbeef);
734         res = pOpenPort(NULL, &hPort);
735         trace("got %u with %u and %p\n", res, GetLastError(), hPort);
736     }
737 
738 }
739 
740 /* ########################### */
741 
742 static void test_XcvClosePort(void)
743 {
744     DWORD   res;
745     HANDLE hXcv2;
746 
747 
748     if (0)
749     {
750         /* crash with native localspl.dll (w2k+xp) */
751         pXcvClosePort(NULL);
752         pXcvClosePort(INVALID_HANDLE_VALUE);
753     }
754 
755 
756     SetLastError(0xdeadbeef);
757     hXcv2 = (HANDLE) 0xdeadbeef;
758     res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv2);
759     ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
760 
761     if (res) {
762         SetLastError(0xdeadbeef);
763         res = pXcvClosePort(hXcv2);
764         ok(res, "returned %d with %u (expected '!= 0')\n", res, GetLastError());
765 
766         if (0)
767         {
768             /* test for "Double Free": crash with native localspl.dll (w2k+xp) */
769             pXcvClosePort(hXcv2);
770         }
771     }
772 }
773 
774 /* ########################### */
775 
776 static void test_XcvDataPort_AddPort(void)
777 {
778     DWORD   res;
779 
780     /*
781      * The following tests crash with native localspl.dll on w2k and xp,
782      * but it works, when the native dll (w2k and xp) is used in wine.
783      * also tested (same crash): replacing emptyW with portname_lpt1W
784      * and replacing "NULL, 0, NULL" with "buffer, MAX_PATH, &needed"
785      *
786      * We need to use a different API (AddPortEx) instead
787      */
788     if (0)
789     {
790     /* create a Port for a normal, writable file */
791     SetLastError(0xdeadbeef);
792     res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, NULL);
793     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
794 
795     /* add our testport again */
796     SetLastError(0xdeadbeef);
797     res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, NULL);
798     ok( res == ERROR_ALREADY_EXISTS, "returned %d with %u (expected ERROR_ALREADY_EXISTS)\n", res, GetLastError());
799 
800     /* create a well-known Port  */
801     SetLastError(0xdeadbeef);
802     res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) portname_lpt1W, (lstrlenW(portname_lpt1W) + 1) * sizeof(WCHAR), NULL, 0, NULL);
803     ok( res == ERROR_ALREADY_EXISTS, "returned %d with %u (expected ERROR_ALREADY_EXISTS)\n", res, GetLastError());
804 
805     /* ERROR_ALREADY_EXISTS is also returned from native localspl.dll on wine,
806        when "RPT1:" was already installed for redmonnt.dll:
807        res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) portname_rpt1W, ...
808     */
809 
810     /* cleanup */
811     SetLastError(0xdeadbeef);
812     res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, NULL);
813     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
814     }
815 
816 }
817 
818 /* ########################### */
819 
820 static void test_XcvDataPort_ConfigureLPTPortCommandOK(void)
821 {
822     CHAR    org_value[16];
823     CHAR    buffer[16];
824     HKEY    hroot = NULL;
825     DWORD   res;
826     DWORD   needed;
827 
828 
829     /* Read the original value from the registry */
830     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsA, 0, KEY_ALL_ACCESS, &hroot);
831     if (res == ERROR_ACCESS_DENIED) {
832         skip("ACCESS_DENIED\n");
833         return;
834     }
835 
836     if (res != ERROR_SUCCESS) {
837         /* unable to open the registry: skip the test */
838         skip("got %d\n", res);
839         return;
840     }
841     org_value[0] = '\0';
842     needed = sizeof(org_value)-1 ;
843     res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) org_value, &needed);
844     ok( (res == ERROR_SUCCESS) || (res == ERROR_FILE_NOT_FOUND),
845         "returned %u and %u for \"%s\" (expected ERROR_SUCCESS or "
846         "ERROR_FILE_NOT_FOUND)\n", res, needed, org_value);
847 
848     RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
849 
850     /* set to "0" */
851     needed = (DWORD) 0xdeadbeef;
852     SetLastError(0xdeadbeef);
853     res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_0W, sizeof(num_0W), NULL, 0, &needed);
854     if (res == ERROR_INVALID_PARAMETER) {
855         skip("'ConfigureLPTPortCommandOK' not supported\n");
856         return;
857     }
858     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
859     needed = sizeof(buffer)-1 ;
860     res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
861     ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_0A) == 0),
862         "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
863         res, buffer, num_0A);
864 
865 
866     /* set to "1" */
867     needed = (DWORD) 0xdeadbeef;
868     SetLastError(0xdeadbeef);
869     res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_1W, sizeof(num_1W), NULL, 0, &needed);
870     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
871     needed = sizeof(buffer)-1 ;
872     res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
873     ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_1A) == 0),
874         "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
875         res, buffer, num_1A);
876 
877     /* set to "999999" */
878     needed = (DWORD) 0xdeadbeef;
879     SetLastError(0xdeadbeef);
880     res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_999999W, sizeof(num_999999W), NULL, 0, &needed);
881     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
882     needed = sizeof(buffer)-1 ;
883     res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
884     ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_999999A) == 0),
885         "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
886         res, buffer, num_999999A);
887 
888     /* set to "1000000" */
889     needed = (DWORD) 0xdeadbeef;
890     SetLastError(0xdeadbeef);
891     res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_1000000W, sizeof(num_1000000W), NULL, 0, &needed);
892     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
893     needed = sizeof(buffer)-1 ;
894     res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
895     ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_1000000A) == 0),
896         "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
897         res, buffer, num_1000000A);
898 
899     /*  using cmd_ConfigureLPTPortCommandOKW with does_not_existW:
900         the string "does_not_exist" is written to the registry */
901 
902 
903     /* restore the original value */
904     RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
905     if (org_value[0]) {
906         res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)org_value, lstrlenA(org_value)+1);
907         ok(res == ERROR_SUCCESS, "unable to restore original value (got %u): %s\n", res, org_value);
908     }
909 
910     RegCloseKey(hroot);
911 
912 }
913 
914 /* ########################### */
915 
916 static void test_XcvDataPort_DeletePort(void)
917 {
918     DWORD   res;
919     DWORD   needed;
920 
921 
922     /* cleanup: just to make sure */
923     needed = (DWORD) 0xdeadbeef;
924     SetLastError(0xdeadbeef);
925     res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
926     ok( !res  || (res == ERROR_FILE_NOT_FOUND),
927         "returned %d with %u (expected ERROR_SUCCESS or ERROR_FILE_NOT_FOUND)\n",
928         res, GetLastError());
929 
930 
931     /* ToDo: cmd_AddPortW for tempfileW, then cmd_DeletePortW for the existing Port */
932 
933 
934     /* try to delete a nonexistent Port */
935     needed = (DWORD) 0xdeadbeef;
936     SetLastError(0xdeadbeef);
937     res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
938     ok( res == ERROR_FILE_NOT_FOUND,
939         "returned %d with %u (expected ERROR_FILE_NOT_FOUND)\n", res, GetLastError());
940 
941     /* emptyW as Portname: ERROR_FILE_NOT_FOUND is returned */
942     /* NULL as Portname: Native localspl.dll crashed */
943 
944 }
945 
946 /* ########################### */
947 
948 static void test_XcvDataPort_GetTransmissionRetryTimeout(void)
949 {
950     CHAR    org_value[16];
951     HKEY    hroot = NULL;
952     DWORD   buffer[2];
953     DWORD   res;
954     DWORD   needed;
955     DWORD   len;
956 
957 
958     /* ask for needed size */
959     needed = (DWORD) 0xdeadbeef;
960     SetLastError(0xdeadbeef);
961     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, NULL, 0, &needed);
962     if (res == ERROR_INVALID_PARAMETER) {
963         skip("'GetTransmissionRetryTimeout' not supported\n");
964         return;
965     }
966     len = sizeof(DWORD);
967     ok( (res == ERROR_INSUFFICIENT_BUFFER) && (needed == len),
968         "returned %d with %u and %u (expected ERROR_INSUFFICIENT_BUFFER "
969         "and '%u')\n", res, GetLastError(), needed, len);
970     len = needed;
971 
972     /* Read the original value from the registry */
973     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsA, 0, KEY_ALL_ACCESS, &hroot);
974     if (res == ERROR_ACCESS_DENIED) {
975         skip("ACCESS_DENIED\n");
976         return;
977     }
978 
979     if (res != ERROR_SUCCESS) {
980         /* unable to open the registry: skip the test */
981         skip("got %d\n", res);
982         return;
983     }
984 
985     org_value[0] = '\0';
986     needed = sizeof(org_value)-1 ;
987     res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) org_value, &needed);
988     ok( (res == ERROR_SUCCESS) || (res == ERROR_FILE_NOT_FOUND),
989         "returned %u and %u for \"%s\" (expected ERROR_SUCCESS or "
990         "ERROR_FILE_NOT_FOUND)\n", res, needed, org_value);
991 
992     /* Get default value (documented as 90 in the w2k reskit, but that is wrong) */
993     RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
994     needed = (DWORD) 0xdeadbeef;
995     buffer[0] = 0xdeadbeef;
996     SetLastError(0xdeadbeef);
997     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
998     ok( (res == ERROR_SUCCESS) && (buffer[0] == 45),
999         "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
1000         "for '45')\n", res, GetLastError(), needed, buffer[0]);
1001 
1002     /* the default timeout is returned, when the value is empty */
1003     res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)emptyA, 1);
1004     ok(res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", GetLastError());
1005     needed = (DWORD) 0xdeadbeef;
1006     buffer[0] = 0xdeadbeef;
1007     SetLastError(0xdeadbeef);
1008     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
1009     ok( (res == ERROR_SUCCESS) && (buffer[0] == 45),
1010         "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
1011         "for '45')\n", res, GetLastError(), needed, buffer[0]);
1012 
1013     /* the dialog is limited (1 - 999999), but that is done somewhere else */
1014     res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_0A, lstrlenA(num_0A)+1);
1015     ok(res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", GetLastError());
1016     needed = (DWORD) 0xdeadbeef;
1017     buffer[0] = 0xdeadbeef;
1018     SetLastError(0xdeadbeef);
1019     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
1020     ok( (res == ERROR_SUCCESS) && (buffer[0] == 0),
1021         "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
1022         "for '0')\n", res, GetLastError(), needed, buffer[0]);
1023 
1024 
1025     res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_1A, lstrlenA(num_1A)+1);
1026     ok(res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", GetLastError());
1027     needed = (DWORD) 0xdeadbeef;
1028     buffer[0] = 0xdeadbeef;
1029     SetLastError(0xdeadbeef);
1030     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
1031     ok( (res == ERROR_SUCCESS) && (buffer[0] == 1),
1032         "returned %d with %u and %u for %d\n (expected 'ERROR_SUCCESS' "
1033         "for '1')\n", res, GetLastError(), needed, buffer[0]);
1034 
1035     res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_999999A, lstrlenA(num_999999A)+1);
1036     ok(res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", GetLastError());
1037     needed = (DWORD) 0xdeadbeef;
1038     buffer[0] = 0xdeadbeef;
1039     SetLastError(0xdeadbeef);
1040     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
1041     ok( (res == ERROR_SUCCESS) && (buffer[0] == 999999),
1042         "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
1043         "for '999999')\n", res, GetLastError(), needed, buffer[0]);
1044 
1045 
1046     res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_1000000A, lstrlenA(num_1000000A)+1);
1047     ok(res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", GetLastError());
1048     needed = (DWORD) 0xdeadbeef;
1049     buffer[0] = 0xdeadbeef;
1050     SetLastError(0xdeadbeef);
1051     res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
1052     ok( (res == ERROR_SUCCESS) && (buffer[0] == 1000000),
1053         "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
1054         "for '1000000')\n", res, GetLastError(), needed, buffer[0]);
1055 
1056     /* restore the original value */
1057     RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
1058     if (org_value[0]) {
1059         res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)org_value, lstrlenA(org_value)+1);
1060         ok(res == ERROR_SUCCESS, "unable to restore original value (got %u): %s\n", res, org_value);
1061     }
1062 
1063     RegCloseKey(hroot);
1064 }
1065 
1066 /* ########################### */
1067 
1068 static void test_XcvDataPort_MonitorUI(void)
1069 {
1070     DWORD   res;
1071     BYTE    buffer[MAX_PATH + 2];
1072     DWORD   needed;
1073     DWORD   len;
1074 
1075 
1076     /* ask for needed size */
1077     needed = (DWORD) 0xdeadbeef;
1078     SetLastError(0xdeadbeef);
1079     res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, NULL, 0, &needed);
1080     if (res == ERROR_INVALID_PARAMETER) {
1081         skip("'MonitorUI' nor supported\n");
1082         return;
1083     }
1084     ok( (res == ERROR_INSUFFICIENT_BUFFER) && (needed <= MAX_PATH),
1085         "returned %d with %u and 0x%x (expected 'ERROR_INSUFFICIENT_BUFFER' "
1086         " and '<= MAX_PATH')\n", res, GetLastError(), needed);
1087 
1088     if (needed > MAX_PATH) {
1089         skip("buffer overflow (%u)\n", needed);
1090         return;
1091     }
1092     len = needed;
1093 
1094     /* the command is required */
1095     needed = (DWORD) 0xdeadbeef;
1096     SetLastError(0xdeadbeef);
1097     res = pXcvDataPort(hXcv, emptyW, NULL, 0, NULL, 0, &needed);
1098     ok( res == ERROR_INVALID_PARAMETER, "returned %d with %u and 0x%x "
1099         "(expected 'ERROR_INVALID_PARAMETER')\n", res, GetLastError(), needed);
1100 
1101     if (0) {
1102         /* crash with native localspl.dll (w2k+xp) */
1103         pXcvDataPort(hXcv, NULL, NULL, 0, buffer, MAX_PATH, &needed);
1104         pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, NULL, len, &needed);
1105         pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len, NULL);
1106     }
1107 
1108 
1109     /* hXcv is ignored for the command "MonitorUI" */
1110     needed = (DWORD) 0xdeadbeef;
1111     SetLastError(0xdeadbeef);
1112     res = pXcvDataPort(NULL, cmd_MonitorUIW, NULL, 0, buffer, len, &needed);
1113     ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
1114         "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
1115 
1116 
1117     /* pszDataName is case-sensitive */
1118     memset(buffer, 0, len);
1119     needed = (DWORD) 0xdeadbeef;
1120     SetLastError(0xdeadbeef);
1121     res = pXcvDataPort(hXcv, cmd_MonitorUI_lcaseW, NULL, 0, buffer, len, &needed);
1122     ok( res == ERROR_INVALID_PARAMETER, "returned %d with %u and 0x%x "
1123         "(expected 'ERROR_INVALID_PARAMETER')\n", res, GetLastError(), needed);
1124 
1125     /* off by one: larger  */
1126     needed = (DWORD) 0xdeadbeef;
1127     SetLastError(0xdeadbeef);
1128     res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len+1, &needed);
1129     ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
1130         "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
1131 
1132 
1133     /* off by one: smaller */
1134     /* the buffer is not modified for NT4, w2k, XP */
1135     needed = (DWORD) 0xdeadbeef;
1136     SetLastError(0xdeadbeef);
1137     res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len-1, &needed);
1138     ok( res == ERROR_INSUFFICIENT_BUFFER, "returned %d with %u and 0x%x "
1139         "(expected 'ERROR_INSUFFICIENT_BUFFER')\n", res, GetLastError(), needed);
1140 
1141     /* Normal use. The DLL-Name without a Path is returned */
1142     memset(buffer, 0, len);
1143     needed = (DWORD) 0xdeadbeef;
1144     SetLastError(0xdeadbeef);
1145     res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len, &needed);
1146     ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
1147         "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
1148 
1149 
1150     /* small check without access-rights: */
1151     if (!hXcv_noaccess) return;
1152 
1153     /* The ACCESS_MASK is ignored for "MonitorUI" */
1154     memset(buffer, 0, len);
1155     needed = (DWORD) 0xdeadbeef;
1156     SetLastError(0xdeadbeef);
1157     res = pXcvDataPort(hXcv_noaccess, cmd_MonitorUIW, NULL, 0, buffer, sizeof(buffer), &needed);
1158     ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
1159         "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
1160 }
1161 
1162 /* ########################### */
1163 
1164 static void test_XcvDataPort_PortIsValid(void)
1165 {
1166     DWORD   res;
1167     DWORD   needed;
1168 
1169     /* normal use: "LPT1:" */
1170     needed = (DWORD) 0xdeadbeef;
1171     SetLastError(0xdeadbeef);
1172     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, &needed);
1173     if (res == ERROR_INVALID_PARAMETER) {
1174         skip("'PostIsValid' not supported\n");
1175         return;
1176     }
1177     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
1178 
1179 
1180     if (0) {
1181         /* crash with native localspl.dll (w2k+xp) */
1182         pXcvDataPort(hXcv, cmd_PortIsValidW, NULL, 0, NULL, 0, &needed);
1183     }
1184 
1185 
1186     /* hXcv is ignored for the command "PortIsValid" */
1187     needed = (DWORD) 0xdeadbeef;
1188     SetLastError(0xdeadbeef);
1189     res = pXcvDataPort(NULL, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, NULL);
1190     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
1191 
1192     /* needed is ignored */
1193     needed = (DWORD) 0xdeadbeef;
1194     SetLastError(0xdeadbeef);
1195     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, NULL);
1196     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
1197 
1198 
1199     /* cbInputData is ignored */
1200     needed = (DWORD) 0xdeadbeef;
1201     SetLastError(0xdeadbeef);
1202     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, 0, NULL, 0, &needed);
1203     ok( res == ERROR_SUCCESS,
1204         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1205         res, GetLastError(), needed);
1206 
1207     needed = (DWORD) 0xdeadbeef;
1208     SetLastError(0xdeadbeef);
1209     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, 1, NULL, 0, &needed);
1210     ok( res == ERROR_SUCCESS,
1211         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1212         res, GetLastError(), needed);
1213 
1214     needed = (DWORD) 0xdeadbeef;
1215     SetLastError(0xdeadbeef);
1216     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W) -1, NULL, 0, &needed);
1217     ok( res == ERROR_SUCCESS,
1218         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1219         res, GetLastError(), needed);
1220 
1221     needed = (DWORD) 0xdeadbeef;
1222     SetLastError(0xdeadbeef);
1223     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W) -2, NULL, 0, &needed);
1224     ok( res == ERROR_SUCCESS,
1225         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1226         res, GetLastError(), needed);
1227 
1228 
1229     /* an empty name is not allowed */
1230     needed = (DWORD) 0xdeadbeef;
1231     SetLastError(0xdeadbeef);
1232     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) emptyW, sizeof(emptyW), NULL, 0, &needed);
1233     ok( res == ERROR_PATH_NOT_FOUND,
1234         "returned %d with %u and 0x%x (expected ERROR_PATH_NOT_FOUND)\n",
1235         res, GetLastError(), needed);
1236 
1237 
1238     /* a directory is not allowed */
1239     needed = (DWORD) 0xdeadbeef;
1240     SetLastError(0xdeadbeef);
1241     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) tempdirW, (lstrlenW(tempdirW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
1242     /* XP(admin): ERROR_INVALID_NAME, XP(user): ERROR_PATH_NOT_FOUND, w2k ERROR_ACCESS_DENIED */
1243     ok( (res == ERROR_INVALID_NAME) || (res == ERROR_PATH_NOT_FOUND) ||
1244         (res == ERROR_ACCESS_DENIED), "returned %d with %u and 0x%x "
1245         "(expected ERROR_INVALID_NAME, ERROR_PATH_NOT_FOUND or ERROR_ACCESS_DENIED)\n",
1246         res, GetLastError(), needed);
1247 
1248 
1249     /* test more valid well known Ports: */
1250     needed = (DWORD) 0xdeadbeef;
1251     SetLastError(0xdeadbeef);
1252     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt2W, sizeof(portname_lpt2W), NULL, 0, &needed);
1253     ok( res == ERROR_SUCCESS,
1254         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1255         res, GetLastError(), needed);
1256 
1257 
1258     needed = (DWORD) 0xdeadbeef;
1259     SetLastError(0xdeadbeef);
1260     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_com1W, sizeof(portname_com1W), NULL, 0, &needed);
1261     ok( res == ERROR_SUCCESS,
1262         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1263         res, GetLastError(), needed);
1264 
1265 
1266     needed = (DWORD) 0xdeadbeef;
1267     SetLastError(0xdeadbeef);
1268     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_com2W, sizeof(portname_com2W), NULL, 0, &needed);
1269     ok( res == ERROR_SUCCESS,
1270         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1271         res, GetLastError(), needed);
1272 
1273 
1274     needed = (DWORD) 0xdeadbeef;
1275     SetLastError(0xdeadbeef);
1276     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_fileW, sizeof(portname_fileW), NULL, 0, &needed);
1277     ok( res == ERROR_SUCCESS,
1278         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1279         res, GetLastError(), needed);
1280 
1281 
1282     /* a normal, writable file is allowed */
1283     needed = (DWORD) 0xdeadbeef;
1284     SetLastError(0xdeadbeef);
1285     res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
1286     ok( res == ERROR_SUCCESS,
1287         "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
1288         res, GetLastError(), needed);
1289 
1290 
1291     /* small check without access-rights: */
1292     if (!hXcv_noaccess) return;
1293 
1294     /* The ACCESS_MASK from XcvOpenPort is ignored in "PortIsValid" */
1295     needed = (DWORD) 0xdeadbeef;
1296     SetLastError(0xdeadbeef);
1297     res = pXcvDataPort(hXcv_noaccess, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, &needed);
1298     ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
1299 
1300 }
1301 
1302 /* ########################### */
1303 
1304 static void test_XcvOpenPort(void)
1305 {
1306     DWORD   res;
1307     HANDLE  hXcv2;
1308 
1309 
1310     if (0)
1311     {
1312         /* crash with native localspl.dll (w2k+xp) */
1313         pXcvOpenPort(NULL, SERVER_ACCESS_ADMINISTER, &hXcv2);
1314         pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, NULL);
1315     }
1316 
1317 
1318     /* The returned handle is the result from a previous "spoolss.dll,DllAllocSplMem" */
1319     SetLastError(0xdeadbeef);
1320     hXcv2 = (HANDLE) 0xdeadbeef;
1321     res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv2);
1322     ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
1323     if (res) pXcvClosePort(hXcv2);
1324 
1325 
1326     /* The ACCESS_MASK is not checked in XcvOpenPort */
1327     SetLastError(0xdeadbeef);
1328     hXcv2 = (HANDLE) 0xdeadbeef;
1329     res = pXcvOpenPort(emptyW, 0, &hXcv2);
1330     ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
1331     if (res) pXcvClosePort(hXcv2);
1332 
1333 
1334     /* A copy of pszObject is saved in the Memory-Block */
1335     SetLastError(0xdeadbeef);
1336     hXcv2 = (HANDLE) 0xdeadbeef;
1337     res = pXcvOpenPort(portname_lpt1W, SERVER_ALL_ACCESS, &hXcv2);
1338     ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
1339     if (res) pXcvClosePort(hXcv2);
1340 
1341     SetLastError(0xdeadbeef);
1342     hXcv2 = (HANDLE) 0xdeadbeef;
1343     res = pXcvOpenPort(portname_fileW, SERVER_ALL_ACCESS, &hXcv2);
1344     ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
1345     if (res) pXcvClosePort(hXcv2);
1346 
1347 }
1348 
1349 /* ########################### */
1350 
1351 #define GET_MONITOR_FUNC(name) \
1352             if(numentries > 0) { \
1353                 numentries--; \
1354                 p##name = pm->Monitor.pfn##name ;  \
1355             }
1356 
1357 
1358 START_TEST(localmon)
1359 {
1360     DWORD   numentries;
1361     DWORD   res;
1362 
1363     LoadLibraryA("winspool.drv");
1364     /* This DLL does not exist on Win9x */
1365     hdll = LoadLibraryA("localspl.dll");
1366     if (!hdll) {
1367         skip("localspl.dll cannot be loaded, most likely running on Win9x\n");
1368         return;
1369     }
1370 
1371     tempdirW[0] = '\0';
1372     tempfileW[0] = '\0';
1373     res = GetTempPathW(MAX_PATH, tempdirW);
1374     ok(res != 0, "with %u\n", GetLastError());
1375     res = GetTempFileNameW(tempdirW, wineW, 0, tempfileW);
1376     ok(res != 0, "with %u\n", GetLastError());
1377 
1378     pInitializePrintMonitor = (void *) GetProcAddress(hdll, "InitializePrintMonitor");
1379 
1380     if (!pInitializePrintMonitor) {
1381         /* The Monitor for "Local Ports" was in a separate dll before w2k */
1382         hlocalmon = LoadLibraryA("localmon.dll");
1383         if (hlocalmon) {
1384             pInitializePrintMonitor = (void *) GetProcAddress(hlocalmon, "InitializePrintMonitor");
1385         }
1386     }
1387     if (!pInitializePrintMonitor) return;
1388 
1389     /* Native localmon.dll / localspl.dll need a valid Port-Entry in:
1390        a) since xp: HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports
1391        b) up to w2k: Section "Ports" in win.ini
1392        or InitializePrintMonitor fails. */
1393     pm = pInitializePrintMonitor(Monitors_LocalPortW);
1394     if (pm) {
1395         numentries = (pm->dwMonitorSize ) / sizeof(VOID *);
1396         /* NT4: 14, since w2k: 17 */
1397         ok( numentries == 14 || numentries == 17,
1398             "dwMonitorSize (%d) => %d Functions\n", pm->dwMonitorSize, numentries);
1399 
1400         GET_MONITOR_FUNC(EnumPorts);
1401         GET_MONITOR_FUNC(OpenPort);
1402         GET_MONITOR_FUNC(OpenPortEx);
1403         GET_MONITOR_FUNC(StartDocPort);
1404         GET_MONITOR_FUNC(WritePort);
1405         GET_MONITOR_FUNC(ReadPort);
1406         GET_MONITOR_FUNC(EndDocPort);
1407         GET_MONITOR_FUNC(ClosePort);
1408         GET_MONITOR_FUNC(AddPort);
1409         GET_MONITOR_FUNC(AddPortEx);
1410         GET_MONITOR_FUNC(ConfigurePort);
1411         GET_MONITOR_FUNC(DeletePort);
1412         GET_MONITOR_FUNC(GetPrinterDataFromPort);
1413         GET_MONITOR_FUNC(SetPortTimeOuts);
1414         GET_MONITOR_FUNC(XcvOpenPort);
1415         GET_MONITOR_FUNC(XcvDataPort);
1416         GET_MONITOR_FUNC(XcvClosePort);
1417 
1418         if ((pXcvOpenPort) && (pXcvDataPort) && (pXcvClosePort)) {
1419             SetLastError(0xdeadbeef);
1420             res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv);
1421             ok(res, "hXcv: %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv);
1422 
1423             SetLastError(0xdeadbeef);
1424             res = pXcvOpenPort(emptyW, 0, &hXcv_noaccess);
1425             ok(res, "hXcv_noaccess: %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv_noaccess);
1426         }
1427     }
1428 
1429     test_InitializePrintMonitor();
1430 
1431     find_installed_ports();
1432 
1433     test_AddPort();
1434     test_AddPortEx();
1435     test_ClosePort();
1436     test_ConfigurePort();
1437     test_DeletePort();
1438     test_EnumPorts();
1439     test_OpenPort();
1440 
1441     if ( !hXcv ) {
1442         skip("Xcv not supported\n");
1443     }
1444     else
1445     {
1446         test_XcvClosePort();
1447         test_XcvDataPort_AddPort();
1448         test_XcvDataPort_ConfigureLPTPortCommandOK();
1449         test_XcvDataPort_DeletePort();
1450         test_XcvDataPort_GetTransmissionRetryTimeout();
1451         test_XcvDataPort_MonitorUI();
1452         test_XcvDataPort_PortIsValid();
1453         test_XcvOpenPort();
1454 
1455         pXcvClosePort(hXcv);
1456     }
1457     if (hXcv_noaccess) pXcvClosePort(hXcv_noaccess);
1458 
1459     /* Cleanup our temporary file */
1460     DeleteFileW(tempfileW);
1461 }
1462