xref: /reactos/win32ss/printing/base/winspool/ports.c (revision 94a413ae)
1 /*
2  * PROJECT:     ReactOS Spooler API
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions related to Ports
5  * COPYRIGHT:   Copyright 2015-2018 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 #include <marshalling/ports.h>
10 
11 typedef struct _MONITORUIDATA
12 {
13     HMODULE hLibrary;
14     HANDLE hActCtx;
15     ULONG_PTR ulpCookie;
16     PWSTR pModuleName;
17     BOOL Activeated;
18 } MONITORUIDATA, *PMONITORUIDATA;
19 
20 typedef DWORD (*PPfpFunction)(LPWSTR, ULONG_PTR, LPWSTR);
21 
22 typedef struct _PORTTHREADINFO
23 {
24     LPWSTR pName;
25     ULONG_PTR hWnd;
26     LPWSTR pPortName;
27     PPfpFunction fpFunction;
28     DWORD dwErrorCode;
29     HANDLE hEvent;
30 } PORTTHREADINFO, *PPORTTHREADINFO;
31 
32 VOID WINAPI
33 IntPortThread( PPORTTHREADINFO pPortThreadInfo )
34 {
35     FIXME("IPT : %s\n",debugstr_w( pPortThreadInfo->pPortName ));
36     // Do the RPC call
37     RpcTryExcept
38     {
39         pPortThreadInfo->dwErrorCode = pPortThreadInfo->fpFunction( pPortThreadInfo->pName, pPortThreadInfo->hWnd, pPortThreadInfo->pPortName );
40     }
41     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
42     {
43         pPortThreadInfo->dwErrorCode = RpcExceptionCode();
44         ERR("IPT : _RpcXyzPort failed with exception code %lu!\n", pPortThreadInfo->dwErrorCode);
45     }
46     RpcEndExcept;
47 
48     SetEvent( pPortThreadInfo->hEvent );
49 }
50 
51 //
52 // Start a thread to wait on a printer port.
53 //
54 BOOL WINAPI
55 StartPortThread( LPWSTR pName, HWND hWnd, LPWSTR pPortName, PPfpFunction fpFunction )
56 {
57     PORTTHREADINFO PortThreadInfo;
58     HANDLE htHandle;
59     MSG Msg;
60     DWORD tid;
61 
62     if ( hWnd ) EnableWindow( hWnd, FALSE );
63 
64     PortThreadInfo.pName = pName;
65     PortThreadInfo.hWnd = (ULONG_PTR)hWnd;
66     PortThreadInfo.pPortName = pPortName;
67     PortThreadInfo.fpFunction = fpFunction;
68     PortThreadInfo.dwErrorCode = ERROR_SUCCESS;
69     PortThreadInfo.hEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
70 
71     htHandle = CreateThread( NULL,
72                              32*1024,
73                             (LPTHREAD_START_ROUTINE)IntPortThread,
74                             &PortThreadInfo,
75                              0,
76                             &tid );
77 
78     CloseHandle( htHandle );
79 
80     while ( MsgWaitForMultipleObjects( 1, &PortThreadInfo.hEvent, FALSE, INFINITE, QS_SENDMESSAGE|QS_ALLEVENTS ) == 1 )
81     {
82         while ( PeekMessageW( &Msg, NULL, 0, 0, PM_REMOVE ) )
83         {
84             TranslateMessage( &Msg );
85             DispatchMessageW( &Msg );
86         }
87     }
88 
89     CloseHandle( PortThreadInfo.hEvent );
90 
91     if ( hWnd )
92     {
93         EnableWindow(hWnd, TRUE);
94         SetForegroundWindow(hWnd);
95         SetFocus(hWnd);
96     }
97 
98     SetLastError(PortThreadInfo.dwErrorCode);
99     return (PortThreadInfo.dwErrorCode == ERROR_SUCCESS);
100 }
101 
102 BOOL WINAPI
103 GetMonitorUIFullName( PWSTR pDeviceName, PWSTR *pModuleName )
104 {
105     STRSAFE_LPWSTR SysDir;
106     UINT length;
107     HRESULT hr;
108 
109    *pModuleName = NULL;
110 
111     SysDir = HeapAlloc(hProcessHeap, 0, MAX_PATH*sizeof(WCHAR));
112 
113     if ( SysDir )
114     {
115         memset( SysDir, 0, MAX_PATH*sizeof(WCHAR) );
116 
117         length = GetSystemDirectoryW( SysDir, MAX_PATH*sizeof(WCHAR) );
118 
119         if ( length > 0 )
120         {
121             StringCbCatW(SysDir, MAX_PATH*sizeof(WCHAR), L"\\");
122 
123             hr = StringCchCatW( SysDir, MAX_PATH*sizeof(WCHAR), pDeviceName );
124             if ( !FAILED(hr) )
125             {
126                 *pModuleName = SysDir;
127                 return TRUE;
128             }
129             SetLastError(HRESULT_CODE(hr));
130         }
131 
132         HeapFree(hProcessHeap, 0, SysDir);
133     }
134     return FALSE;
135 }
136 
137 BOOL WINAPI
138 GetMonitorUIActivationContext( PWSTR pDeviceName, PMONITORUIDATA pmuid )
139 {
140    // ACTCTXW actctx;
141    // HANDLE handle;
142     BOOL Ret = FALSE;
143 
144     FIXME("GMUIAC : Module pDeviceName %S\n",pDeviceName);
145 
146     if ( !GetMonitorUIFullName( pDeviceName, &pmuid->pModuleName ) )
147     {
148         ERR("GetMonitorUIFullName Failed\n");
149         return Ret;
150     }
151 /*    OMG! SxS again?
152     memset(&actctx, 0, sizeof(ACTCTXW));
153     actctx.cbSize = sizeof(ACTCTXW);
154     actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
155     actctx.lpResourceName = MAKEINTRESOURCEW(123); This might be the reason....
156     actctx.lpSource = pmuid->pModuleName;
157 
158     handle = CreateActCtxW(&actctx);
159 
160     if ( handle != INVALID_HANDLE_VALUE )
161     {
162         pmuid->hActCtx = handle;
163         if ( ActivateActCtx( handle, &pmuid->ulpCookie ) )
164         {
165             pmuid->Activeated = TRUE;
166             Ret = TRUE;
167         }
168         else
169         {
170             pmuid->Activeated = FALSE;
171         }
172     }
173     else
174     {
175         ERR("GetMonitorUIActivationContext Failed %S\n",pmuid->pModuleName);
176     }*/
177     pmuid->hActCtx = INVALID_HANDLE_VALUE;
178     Ret = TRUE;
179     return Ret;
180 }
181 
182 VOID FASTCALL
183 FreeMonitorUI( PMONITORUIDATA pmuid )
184 {
185     if ( pmuid )
186     {
187         if ( pmuid->hLibrary )
188         {
189             FreeLibrary( pmuid->hLibrary );
190         }
191         if ( pmuid->Activeated )
192         {
193             DeactivateActCtx( 0, pmuid->ulpCookie );
194         }
195         if ( pmuid->hActCtx != INVALID_HANDLE_VALUE )
196         {
197             ReleaseActCtx( pmuid->hActCtx );
198         }
199         if ( pmuid->pModuleName )
200         {
201             DllFreeSplMem( pmuid->pModuleName );
202         }
203         DllFreeSplMem( pmuid );
204     }
205 }
206 
207 BOOL FASTCALL
208 StrNCatBuff( PWSTR ptr, size_t Size, PWSTR args, ...)
209 {
210     va_list Args;
211     PWSTR pwstr;
212     HRESULT hr;
213     BOOL Ret = TRUE;
214 
215     va_start(Args, args );
216 
217    for ( pwstr = args ; pwstr ; pwstr = va_arg( Args, PWSTR ) )
218    {
219        hr = StringCchCatNW( ptr, Size, pwstr, wcslen(pwstr) );
220        if ( FAILED(hr) )
221        {
222            SetLastError(HRESULT_CODE(hr));
223            Ret = FALSE;
224            break;
225         }
226     }
227 
228     va_end(Args);
229 
230     return Ret;
231 }
232 
233 PWSTR WINAPI
234 ConstructXcvName( PWSTR pName, PWSTR pMonitorPortName, PWSTR pXcvName )
235 {
236     BOOL Ret = FALSE;
237     PWSTR pwstr = NULL;
238     size_t sXcv, smpn = 0, Size = 0;
239 
240     if ( pName )
241     {
242         Size = wcslen( pName ) + 1;
243     }
244 
245     sXcv = wcslen( pXcvName ) + Size;
246 
247     if ( pMonitorPortName )
248     {
249         smpn = wcslen( pMonitorPortName );
250     }
251 
252     Size = sXcv + smpn + 3;
253 
254     pwstr = DllAllocSplMem( Size * sizeof(WCHAR) );
255 
256     memset( pwstr, 0, Size );
257 
258     if ( pwstr )
259     {
260         // The caller wants an Xcv handle and provided a string like:
261         //    ", XcvMonitor Local Port"
262         //    "\\COMPUTERNAME\, XcvMonitor Local Port"
263         //    ", XcvPort LPT1:"
264         //    "\\COMPUTERNAME\, XcvPort LPT1:"
265         //
266         //    This produces; !pName ",XcvMonitor " or pName "\\COMPUTERNAME\XcvMonitor "
267         //
268         Ret = StrNCatBuff( pwstr,
269                            Size,
270                            pName ? pName : L"",
271                            pName ? L"\\" : L",",
272                            pXcvName,
273                            L" ",
274                            pMonitorPortName ? pMonitorPortName : L"",
275                            NULL );
276     }
277 
278     if ( !Ret )
279     {
280         DllFreeSplMem( pwstr );
281         pwstr = NULL;
282     }
283 
284     return pwstr;
285 }
286 
287 DWORD WINAPI
288 GetMonitorUI( PWSTR pName, PWSTR pMonitorPortName, PWSTR pXcvName, PMONITORUI *pmui, PMONITORUIDATA *ppmuid )
289 {
290     DWORD dwErrorCode = ERROR_SUCCESS, cbOutputNeeded, dwStatus;
291     HANDLE hPrinter = NULL;
292     HMODULE hModule;
293     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
294     PWSTR pDevice = NULL, pOutputString = NULL;
295     PMONITORUIDATA pmuid = NULL;
296     PRINTER_DEFAULTSW wDefault = { 0, 0, PRINTER_ATTRIBUTE_QUEUED };
297     BYTE OutputData[1024], InputData[4];
298 
299     *pmui = NULL;
300     *ppmuid = NULL;
301 
302     pDevice = ConstructXcvName( pName, pMonitorPortName, pXcvName );
303 
304     if ( !pDevice )
305     {
306         return GetLastError();
307     }
308 
309     FIXME("GMUI : XcvName : %S\n",pDevice);
310 
311     if ( OpenPrinterW( (LPWSTR)pDevice, &hPrinter, &wDefault ) )
312     {
313         pHandle = (PSPOOLER_HANDLE)hPrinter;
314 
315         // Do the RPC call
316         RpcTryExcept
317         {
318             dwErrorCode = _RpcXcvData( pHandle->hPrinter,
319                                        L"MonitorUI",
320                                        (PBYTE)&InputData,
321                                        0,
322                                        (PBYTE)&OutputData,
323                                        1024,
324                                        &cbOutputNeeded,
325                                        &dwStatus );
326         }
327         RpcExcept(EXCEPTION_EXECUTE_HANDLER)
328         {
329             dwErrorCode = RpcExceptionCode();
330             ERR("GMUI : _RpcXcvData failed with exception code %lu!\n", dwErrorCode);
331         }
332         RpcEndExcept;
333 
334         if ( dwErrorCode == ERROR_INSUFFICIENT_BUFFER )
335         {
336             pOutputString = DllAllocSplMem( cbOutputNeeded );
337 
338             // Do the RPC call
339             RpcTryExcept
340             {
341                 dwErrorCode = _RpcXcvData( pHandle->hPrinter,
342                                            L"MonitorUI",
343                                            (PBYTE)&InputData,
344                                            0,
345                                            (PBYTE)pOutputString,
346                                            cbOutputNeeded,
347                                            &cbOutputNeeded,
348                                            &dwStatus );
349             }
350             RpcExcept(EXCEPTION_EXECUTE_HANDLER)
351             {
352                 dwErrorCode = RpcExceptionCode();
353                 ERR("GMUI : _RpcXcvData failed with exception code %lu!\n", dwErrorCode);
354             }
355             RpcEndExcept;
356         }
357 
358         if ( dwErrorCode != ERROR_SUCCESS || dwStatus != ERROR_SUCCESS )
359         {
360             goto Cleanup;
361         }
362 
363         pmuid = DllAllocSplMem( sizeof(MONITORUIDATA) );
364         if ( pmuid )
365         {
366             memset( pmuid, 0, sizeof(MONITORUIDATA) );
367             pmuid->hActCtx = INVALID_HANDLE_VALUE;
368         }
369         else
370         {
371             ERR("GMUI : Memory error\n");
372             dwErrorCode = GetLastError();
373             goto Cleanup;
374         }
375 
376         if ( GetMonitorUIActivationContext( pOutputString ? pOutputString : (PWSTR)&OutputData, pmuid ) )
377         {
378             FIXME("GMUI : MonitorUI Path : %S\n",pmuid->pModuleName);
379 
380             hModule = LoadLibraryW( pmuid->pModuleName );
381             if ( hModule )
382             {
383                 FARPROC fpInitializePrintMonitorUI = (PVOID) GetProcAddress( hModule, "InitializePrintMonitorUI" );
384                 if ( fpInitializePrintMonitorUI )
385                 {
386                     pmuid->hLibrary = hModule;
387                     *pmui = (PMONITORUI)(*fpInitializePrintMonitorUI)();
388                     *ppmuid = pmuid;
389                 }
390                 else
391                 {
392                    ERR("GMUI : Failed to get MUI %S\n",pmuid->pModuleName);
393                    FreeMonitorUI( pmuid );
394                 }
395             }
396             else
397             {
398                 ERR("GMUI : Failed to load library %S\n",pmuid->pModuleName);
399             }
400         }
401     }
402     else
403     {
404         ERR("GMUI : Failed to open printer handle\n");
405     }
406 
407     dwErrorCode = GetLastError();
408 
409 Cleanup:
410     if ( hPrinter ) ClosePrinter( hPrinter );
411     if ( pOutputString ) DllFreeSplMem( pOutputString );
412     if ( pDevice ) DllFreeSplMem( pDevice );
413 
414     FIXME("GMUI : Error Code Exit %d\n",dwErrorCode);
415 
416     return dwErrorCode;
417 }
418 
419 BOOL WINAPI
420 AddPortA(PSTR pName, HWND hWnd, PSTR pMonitorName)
421 {
422     LPWSTR  nameW = NULL;
423     LPWSTR  monitorW = NULL;
424     DWORD   len;
425     BOOL    res;
426 
427     TRACE("AddPortA(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
428 
429     if (pName)
430     {
431         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
432         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
433         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
434     }
435 
436     if (pMonitorName)
437     {
438         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
439         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
440         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
441     }
442 
443     res = AddPortW(nameW, hWnd, monitorW);
444 
445     if (nameW) HeapFree(GetProcessHeap(), 0, nameW);
446     if (monitorW) HeapFree(GetProcessHeap(), 0, monitorW);
447 
448     return res;
449 }
450 
451 BOOL WINAPI
452 AddPortExW(PWSTR pName, DWORD Level, PBYTE lpBuffer, PWSTR lpMonitorName)
453 {
454     DWORD dwErrorCode;
455     WINSPOOL_PORT_CONTAINER PortInfoContainer;
456     WINSPOOL_PORT_VAR_CONTAINER PortVarContainer;
457     WINSPOOL_PORT_INFO_FF *pPortInfoFF;
458 
459     FIXME("AddPortExW(%S, %lu, %p, %S)\n", pName, Level, lpBuffer, lpMonitorName);
460 
461     switch (Level)
462     {
463         case 1:
464            // FIXME!!!! Only Level 1 is supported? See note in wine winspool test info.c : line 575. It's just not supported here.
465            PortInfoContainer.PortInfo.pPortInfo1 = (WINSPOOL_PORT_INFO_1*)lpBuffer;
466            PortInfoContainer.Level = Level;
467            PortVarContainer.cbMonitorData = 0;
468            PortVarContainer.pMonitorData = NULL;
469            break;
470 
471         case 0xFFFFFFFF:
472            pPortInfoFF = (WINSPOOL_PORT_INFO_FF*)lpBuffer;
473            PortInfoContainer.PortInfo.pPortInfoFF = pPortInfoFF;
474            PortInfoContainer.Level = Level;
475            PortVarContainer.cbMonitorData = pPortInfoFF->cbMonitorData;
476            PortVarContainer.pMonitorData = pPortInfoFF->pMonitorData;
477            break;
478 
479         default:
480            ERR("Level = %d, unsupported!\n", Level);
481            SetLastError(ERROR_INVALID_LEVEL);
482            return FALSE;
483     }
484 
485     // Do the RPC call
486     RpcTryExcept
487     {
488         dwErrorCode = _RpcAddPortEx(pName, &PortInfoContainer, &PortVarContainer, lpMonitorName);
489     }
490     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
491     {
492         dwErrorCode = RpcExceptionCode();
493     }
494     RpcEndExcept;
495 
496     SetLastError(dwErrorCode);
497     return (dwErrorCode == ERROR_SUCCESS);
498 }
499 
500 BOOL WINAPI
501 AddPortExA(PSTR pName, DWORD Level, PBYTE lpBuffer, PSTR lpMonitorName)
502 {
503     PORT_INFO_1W   pi1W;
504     PORT_INFO_1A * pi1A;
505     LPWSTR  nameW = NULL;
506     LPWSTR  monitorW = NULL;
507     DWORD   len;
508     BOOL    res = FALSE;
509     WINSPOOL_PORT_INFO_FF *pPortInfoFF, PortInfoFF;
510 
511     pi1A = (PORT_INFO_1A *)lpBuffer;
512     pPortInfoFF = (WINSPOOL_PORT_INFO_FF*)lpBuffer;
513 
514     FIXME("AddPortExA(%s, %d, %p, %s): %s\n", debugstr_a(pName), Level, lpBuffer, debugstr_a(lpMonitorName), debugstr_a(pi1A ? pi1A->pName : NULL));
515 
516     if ( !lpBuffer || !lpMonitorName )
517     {
518         SetLastError(ERROR_INVALID_PARAMETER);
519         return FALSE;
520     }
521 
522     if (pName)
523     {
524         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
525         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
526         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
527     }
528 
529     if (lpMonitorName)
530     {
531         len = MultiByteToWideChar(CP_ACP, 0, lpMonitorName, -1, NULL, 0);
532         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
533         MultiByteToWideChar(CP_ACP, 0, lpMonitorName, -1, monitorW, len);
534     }
535 
536     pi1W.pName = NULL;
537     ZeroMemory( &PortInfoFF, sizeof(WINSPOOL_PORT_INFO_FF));
538 
539     switch ( Level )
540     {
541         case 1:
542             if ( pi1A->pName )
543             {
544                 len = MultiByteToWideChar(CP_ACP, 0, pi1A->pName, -1, NULL, 0);
545                 pi1W.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
546                 MultiByteToWideChar(CP_ACP, 0, pi1A->pName, -1, pi1W.pName, len);
547             }
548             break;
549 
550         case 0xFFFFFFFF:
551             //
552             // Remember the calling parameter is Ansi.
553             //
554             if ( !pPortInfoFF->pPortName || !(PCHAR)pPortInfoFF->pPortName )
555             {
556                 SetLastError(ERROR_INVALID_PARAMETER);
557                 goto Cleanup;
558             }
559 
560             len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pPortInfoFF->pPortName, -1, NULL, 0);
561             PortInfoFF.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
562             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pPortInfoFF->pPortName, -1, (LPWSTR)PortInfoFF.pPortName, len);
563 
564             PortInfoFF.cbMonitorData = pPortInfoFF->cbMonitorData;
565             PortInfoFF.pMonitorData  = pPortInfoFF->pMonitorData;
566             break;
567 
568         default:
569             ERR("Level = %d, unsupported!\n", Level);
570             SetLastError(ERROR_INVALID_LEVEL);
571             goto Cleanup;
572     }
573 
574     res = AddPortExW( nameW, Level,  Level == 1 ? (PBYTE)&pi1W : (PBYTE)&PortInfoFF, monitorW );
575 
576 Cleanup:
577     if (nameW) HeapFree(GetProcessHeap(), 0, nameW);
578     if (monitorW) HeapFree(GetProcessHeap(), 0, monitorW);
579     if (pi1W.pName) HeapFree(GetProcessHeap(), 0, pi1W.pName);
580     if (PortInfoFF.pPortName) HeapFree(GetProcessHeap(), 0, PortInfoFF.pPortName);
581 
582     return res;
583 }
584 
585 BOOL WINAPI
586 AddPortW(PWSTR pName, HWND hWnd, PWSTR pMonitorName)
587 {
588     DWORD SessionId, dwErrorCode = 0;
589     PMONITORUIDATA pmuid;
590     PMONITORUI pmui = NULL;
591     BOOL Ret = FALSE;
592 
593     FIXME("AddPortW(%S, %p, %S)\n", pName, hWnd, pMonitorName);
594 
595     dwErrorCode = GetMonitorUI( pName, pMonitorName, L"XcvMonitor", &pmui, &pmuid );
596     FIXME("AddPortW Error %d\n",dwErrorCode);
597     if (dwErrorCode != ERROR_SUCCESS )
598     {
599         if ( dwErrorCode == ERROR_NOT_SUPPORTED          ||
600              dwErrorCode == ERROR_MOD_NOT_FOUND          ||
601              dwErrorCode == ERROR_INVALID_PRINT_MONITOR  ||
602              dwErrorCode == ERROR_UNKNOWN_PORT           ||
603              dwErrorCode == ERROR_INVALID_PRINTER_NAME )
604         {
605             if ( ProcessIdToSessionId( GetCurrentProcessId(), &SessionId ) && SessionId ) // Looking if this is remote.
606             {
607                 dwErrorCode = ERROR_NOT_SUPPORTED;
608             }
609             else
610             {
611                 Ret = StartPortThread( pName, hWnd, pMonitorName, (PPfpFunction)_RpcAddPort );
612                 FIXME("AddPortW return StartPortThread\n");
613                 dwErrorCode = GetLastError();
614             }
615         }
616     }
617     else
618     {
619         Ret = (*pmui->pfnAddPortUI)( pName, hWnd, pMonitorName, NULL );
620     }
621 
622     SetLastError(dwErrorCode);
623     FreeMonitorUI( pmuid );
624 
625     return Ret;
626 }
627 
628 BOOL WINAPI
629 ConfigurePortA(PSTR pName, HWND hWnd, PSTR pPortName)
630 {
631     LPWSTR  nameW = NULL;
632     LPWSTR  portW = NULL;
633     INT     len;
634     DWORD   res;
635 
636     TRACE("ConfigurePortA(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
637 
638     /* convert servername to unicode */
639     if (pName)
640     {
641         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
642         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
643         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
644     }
645 
646     /* convert portname to unicode */
647     if (pPortName)
648     {
649         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
650         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
651         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
652     }
653 
654     res = ConfigurePortW(nameW, hWnd, portW);
655 
656     if (nameW) HeapFree(GetProcessHeap(), 0, nameW);
657     if (portW) HeapFree(GetProcessHeap(), 0, portW);
658 
659     return res;
660 }
661 
662 BOOL WINAPI
663 ConfigurePortW(PWSTR pName, HWND hWnd, PWSTR pPortName)
664 {
665     DWORD SessionId, dwErrorCode = 0;
666     PMONITORUIDATA pmuid;
667     PMONITORUI pmui = NULL;
668     BOOL Ret = FALSE;
669 
670     FIXME("ConfigurePortW(%S, %p, %S)\n", pName, hWnd, pPortName);
671 
672     dwErrorCode = GetMonitorUI( pName, pPortName, L"XcvPort", &pmui, &pmuid );
673 
674     if (dwErrorCode != ERROR_SUCCESS )
675     {
676         if ( dwErrorCode == ERROR_NOT_SUPPORTED          ||
677              dwErrorCode == ERROR_MOD_NOT_FOUND          ||
678              dwErrorCode == ERROR_INVALID_PRINT_MONITOR  ||
679              dwErrorCode == ERROR_UNKNOWN_PORT           ||
680              dwErrorCode == ERROR_INVALID_PRINTER_NAME )
681         {
682             if ( ProcessIdToSessionId( GetCurrentProcessId(), &SessionId ) && SessionId ) // Looking if this is remote.
683             {
684                 dwErrorCode = ERROR_NOT_SUPPORTED;
685             }
686             else
687             {
688                 Ret = StartPortThread(pName, hWnd, pPortName, (PPfpFunction)_RpcConfigurePort );
689                 dwErrorCode = GetLastError();
690             }
691         }
692     }
693     else
694     {
695         Ret = (*pmui->pfnConfigurePortUI)( pName, hWnd, pPortName );
696     }
697 
698     SetLastError(dwErrorCode);
699     FreeMonitorUI( pmuid );
700 
701     return Ret;
702 }
703 
704 BOOL WINAPI
705 DeletePortA(PSTR pName, HWND hWnd, PSTR pPortName)
706 {
707     LPWSTR  nameW = NULL;
708     LPWSTR  portW = NULL;
709     INT     len;
710     DWORD   res;
711 
712     FIXME("DeletePortA(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
713 
714     /* convert servername to unicode */
715     if (pName)
716     {
717         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
718         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
719         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
720     }
721 
722     /* convert portname to unicode */
723     if (pPortName)
724     {
725         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
726         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
727         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
728     }
729 
730     res = DeletePortW(nameW, hWnd, portW);
731 
732     if (nameW) HeapFree(GetProcessHeap(), 0, nameW);
733     if (portW) HeapFree(GetProcessHeap(), 0, portW);
734 
735     return res;
736 }
737 
738 BOOL WINAPI
739 DeletePortW(PWSTR pName, HWND hWnd, PWSTR pPortName)
740 {
741     DWORD dwErrorCode = 0;
742     PMONITORUIDATA pmuid;
743     PMONITORUI pmui = NULL;
744     BOOL Ret = FALSE;
745 
746     FIXME("DeletePortW(%S, %p, %S)\n", pName, hWnd, pPortName);
747 
748     dwErrorCode = GetMonitorUI( pName, pPortName, L"XcvPort", &pmui, &pmuid );
749     FIXME("DeletePortW Error %d\n",dwErrorCode);
750     if (dwErrorCode != ERROR_SUCCESS )
751     {
752         if ( dwErrorCode == ERROR_NOT_SUPPORTED          ||
753              dwErrorCode == ERROR_MOD_NOT_FOUND          ||
754              dwErrorCode == ERROR_INVALID_PRINT_MONITOR  ||
755              dwErrorCode == ERROR_UNKNOWN_PORT           ||
756              dwErrorCode == ERROR_INVALID_PRINTER_NAME )
757         {
758             Ret = StartPortThread(pName, hWnd, pPortName, (PPfpFunction)_RpcDeletePort );
759             dwErrorCode = GetLastError();
760         }
761     }
762     else
763     {
764         Ret = (*pmui->pfnDeletePortUI)( pName, hWnd, pPortName );
765     }
766 
767     SetLastError(dwErrorCode);
768     FreeMonitorUI( pmuid );
769 
770     return Ret;
771 }
772 
773 BOOL WINAPI
774 EnumPortsA(PSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
775 {
776     BOOL    res;
777     LPBYTE  bufferW = NULL;
778     LPWSTR  nameW = NULL;
779     DWORD   needed = 0;
780     DWORD   numentries = 0;
781     INT     len;
782 
783     TRACE("EnumPortsA(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts, cbBuf, pcbNeeded, pcReturned);
784 
785     if ((Level < 1) || (Level > 2))
786     {
787         ERR("Level = %d, unsupported!\n", Level);
788         SetLastError(ERROR_INVALID_LEVEL);
789         return FALSE;
790     }
791 
792     /* convert servername to unicode */
793     if (pName)
794     {
795         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
796         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
797         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
798     }
799     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
800     needed = cbBuf * sizeof(WCHAR);
801     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
802     res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
803 
804     if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
805     {
806         if (pcbNeeded) needed = *pcbNeeded;
807         /* HeapReAlloc return NULL, when bufferW was NULL */
808         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
809                               HeapAlloc(GetProcessHeap(), 0, needed);
810 
811         /* Try again with the large Buffer */
812         res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
813     }
814     needed = pcbNeeded ? *pcbNeeded : 0;
815     numentries = pcReturned ? *pcReturned : 0;
816 
817     /*
818        W2k require the buffersize from EnumPortsW also for EnumPortsA.
819        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
820      */
821     if (res)
822     {
823         /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
824         DWORD   entrysize = 0;
825         DWORD   index;
826         LPSTR   ptr;
827         LPPORT_INFO_2W pi2w;
828         LPPORT_INFO_2A pi2a;
829 
830         needed = 0;
831         entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
832 
833         /* First pass: calculate the size for all Entries */
834         pi2w = (LPPORT_INFO_2W) bufferW;
835         pi2a = (LPPORT_INFO_2A) pPorts;
836         index = 0;
837         while (index < numentries)
838         {
839             index++;
840             needed += entrysize;    /* PORT_INFO_?A */
841             TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
842 
843             needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
844                                             NULL, 0, NULL, NULL);
845             if (Level > 1)
846             {
847                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
848                                                 NULL, 0, NULL, NULL);
849                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
850                                                 NULL, 0, NULL, NULL);
851             }
852             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
853             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
854             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
855         }
856 
857         /* check for errors and quit on failure */
858         if (cbBuf < needed)
859         {
860             SetLastError(ERROR_INSUFFICIENT_BUFFER);
861             res = FALSE;
862             goto cleanup;
863         }
864         len = entrysize * numentries;       /* room for all PORT_INFO_?A */
865         ptr = (LPSTR) &pPorts[len];         /* room for strings */
866         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
867         pi2w = (LPPORT_INFO_2W) bufferW;
868         pi2a = (LPPORT_INFO_2A) pPorts;
869         index = 0;
870         /* Second Pass: Fill the User Buffer (if we have one) */
871         while ((index < numentries) && pPorts)
872         {
873             index++;
874             TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
875             pi2a->pPortName = ptr;
876             len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
877                                             ptr, cbBuf , NULL, NULL);
878             ptr += len;
879             cbBuf -= len;
880             if (Level > 1)
881             {
882                 pi2a->pMonitorName = ptr;
883                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
884                                             ptr, cbBuf, NULL, NULL);
885                 ptr += len;
886                 cbBuf -= len;
887 
888                 pi2a->pDescription = ptr;
889                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
890                                             ptr, cbBuf, NULL, NULL);
891                 ptr += len;
892                 cbBuf -= len;
893 
894                 pi2a->fPortType = pi2w->fPortType;
895                 pi2a->Reserved = 0; /* documented: "must be zero" */
896 
897             }
898             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
899             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
900             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
901         }
902     }
903 
904 cleanup:
905     if (pcbNeeded)  *pcbNeeded = needed;
906     if (pcReturned) *pcReturned = (res) ? numentries : 0;
907 
908     if (nameW) HeapFree(GetProcessHeap(), 0, nameW);
909     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
910 
911     TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
912             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
913 
914     return (res);
915 }
916 
917 BOOL WINAPI
918 EnumPortsW(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
919 {
920     DWORD dwErrorCode;
921 
922     TRACE("EnumPortsW(%S, %lu, %p, %lu, %p, %p)\n", pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
923 
924     if ((Level < 1) || (Level > 2))
925     {
926         ERR("Level = %d, unsupported!\n", Level);
927         SetLastError(ERROR_INVALID_LEVEL);
928         return FALSE;
929     }
930 
931     // Do the RPC call
932     RpcTryExcept
933     {
934         dwErrorCode = _RpcEnumPorts(pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
935     }
936     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
937     {
938         dwErrorCode = RpcExceptionCode();
939         ERR("_RpcEnumPorts failed with exception code %lu!\n", dwErrorCode);
940     }
941     RpcEndExcept;
942 
943     if (dwErrorCode == ERROR_SUCCESS)
944     {
945         // Replace relative offset addresses in the output by absolute pointers.
946         ASSERT(Level >= 1 && Level <= 2);
947         MarshallUpStructuresArray(cbBuf, pPorts, *pcReturned, pPortInfoMarshalling[Level]->pInfo, pPortInfoMarshalling[Level]->cbStructureSize, TRUE);
948     }
949 
950     SetLastError(dwErrorCode);
951     return (dwErrorCode == ERROR_SUCCESS);
952 }
953 
954 BOOL WINAPI
955 SetPortA(PSTR pName, PSTR pPortName, DWORD dwLevel, PBYTE pPortInfo)
956 {
957     LPWSTR NameW = NULL;
958     LPWSTR PortNameW = NULL;
959     PORT_INFO_3W  pi3W;
960     PORT_INFO_3A *pi3A;
961     DWORD len;
962     BOOL res;
963 
964     pi3A = (PORT_INFO_3A*)pPortInfo;
965 
966     TRACE("SetPortA(%s, %s, %lu, %p)\n", pName, pPortName, dwLevel, pPortInfo);
967 
968     if ( dwLevel != 3 )
969     {
970         ERR("Level = %d, unsupported!\n", dwLevel);
971         SetLastError(ERROR_INVALID_LEVEL);
972         return FALSE;
973     }
974 
975     if (pName)
976     {
977         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
978         NameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
979         MultiByteToWideChar(CP_ACP, 0, pName, -1, NameW, len);
980     }
981 
982     if (pPortName)
983     {
984         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
985         PortNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
986         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, PortNameW, len);
987     }
988 
989     if (pi3A->pszStatus)
990     {
991         len = MultiByteToWideChar(CP_ACP, 0, pi3A->pszStatus, -1, NULL, 0);
992         pi3W.pszStatus = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
993         MultiByteToWideChar(CP_ACP, 0, pi3A->pszStatus, -1, pi3W.pszStatus, len);
994     }
995 
996     pi3W.dwStatus   = pi3A->dwStatus;
997     pi3W.dwSeverity = pi3A->dwSeverity;
998 
999     res = SetPortW( NameW, PortNameW, dwLevel, (PBYTE)&pi3W );
1000 
1001     if (NameW) HeapFree(GetProcessHeap(), 0, NameW);
1002     if (PortNameW) HeapFree(GetProcessHeap(), 0, PortNameW);
1003     if (pi3W.pszStatus) HeapFree(GetProcessHeap(), 0, pi3W.pszStatus);
1004 
1005     return res;
1006 }
1007 
1008 BOOL WINAPI
1009 SetPortW(PWSTR pName, PWSTR pPortName, DWORD dwLevel, PBYTE pPortInfo)
1010 {
1011     DWORD dwErrorCode;
1012     WINSPOOL_PORT_CONTAINER PortInfoContainer;
1013 
1014     TRACE("SetPortW(%S, %S, %lu, %p)\n", pName, pPortName, dwLevel, pPortInfo);
1015 
1016     if ( dwLevel != 3 )
1017     {
1018         ERR("Level = %d, unsupported!\n", dwLevel);
1019         SetLastError(ERROR_INVALID_LEVEL);
1020         return FALSE;
1021     }
1022 
1023     PortInfoContainer.PortInfo.pPortInfo3 = (WINSPOOL_PORT_INFO_3*)pPortInfo;
1024     PortInfoContainer.Level = dwLevel;
1025 
1026     // Do the RPC call
1027     RpcTryExcept
1028     {
1029         dwErrorCode = _RpcSetPort(pName, pPortName, &PortInfoContainer);
1030     }
1031     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1032     {
1033         dwErrorCode = RpcExceptionCode();
1034     }
1035     RpcEndExcept;
1036 
1037     SetLastError(dwErrorCode);
1038     return (dwErrorCode == ERROR_SUCCESS);
1039 }
1040