Merge 25584, 25588.
[reactos.git] / reactos / subsystems / win32 / csrss / init.c
1 /* $Id$
2 *
3 * reactos/subsys/csrss/init.c
4 *
5 * Initialize the CSRSS subsystem server process.
6 *
7 * ReactOS Operating System
8 *
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <csrss.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS ******************************************************************/
19
20 HANDLE CsrHeap = (HANDLE) 0;
21
22 HANDLE CsrObjectDirectory = (HANDLE) 0;
23
24 UNICODE_STRING CsrDirectoryName;
25
26 extern HANDLE CsrssApiHeap;
27
28 static unsigned InitCompleteProcCount;
29 static CSRPLUGIN_INIT_COMPLETE_PROC *InitCompleteProcs = NULL;
30
31 static unsigned HardErrorProcCount;
32 static CSRPLUGIN_HARDERROR_PROC *HardErrorProcs = NULL;
33
34 HANDLE hSbApiPort = (HANDLE) 0;
35
36 HANDLE hBootstrapOk = (HANDLE) 0;
37
38 HANDLE hSmApiPort = (HANDLE) 0;
39
40 HANDLE hApiPort = (HANDLE) 0;
41
42 /**********************************************************************
43 * CsrpAddInitCompleteProc/1
44 */
45 static NTSTATUS FASTCALL
46 CsrpAddInitCompleteProc(CSRPLUGIN_INIT_COMPLETE_PROC Proc)
47 {
48 CSRPLUGIN_INIT_COMPLETE_PROC *NewProcs;
49
50 DPRINT("CSR: %s called\n", __FUNCTION__);
51
52 NewProcs = RtlAllocateHeap(CsrssApiHeap, 0,
53 (InitCompleteProcCount + 1)
54 * sizeof(CSRPLUGIN_INIT_COMPLETE_PROC));
55 if (NULL == NewProcs)
56 {
57 return STATUS_NO_MEMORY;
58 }
59 if (0 != InitCompleteProcCount)
60 {
61 RtlCopyMemory(NewProcs, InitCompleteProcs,
62 InitCompleteProcCount * sizeof(CSRPLUGIN_INIT_COMPLETE_PROC));
63 RtlFreeHeap(CsrssApiHeap, 0, InitCompleteProcs);
64 }
65 NewProcs[InitCompleteProcCount] = Proc;
66 InitCompleteProcs = NewProcs;
67 InitCompleteProcCount++;
68
69 return STATUS_SUCCESS;
70 }
71
72 static NTSTATUS FASTCALL
73 CsrpAddHardErrorProc(CSRPLUGIN_HARDERROR_PROC Proc)
74 {
75 CSRPLUGIN_HARDERROR_PROC *NewProcs;
76
77 DPRINT("CSR: %s called\n", __FUNCTION__);
78
79 NewProcs = RtlAllocateHeap(CsrssApiHeap, 0,
80 (HardErrorProcCount + 1)
81 * sizeof(CSRPLUGIN_HARDERROR_PROC));
82 if (NULL == NewProcs)
83 {
84 return STATUS_NO_MEMORY;
85 }
86 if (0 != HardErrorProcCount)
87 {
88 RtlCopyMemory(NewProcs, HardErrorProcs,
89 HardErrorProcCount * sizeof(CSRPLUGIN_HARDERROR_PROC));
90 RtlFreeHeap(CsrssApiHeap, 0, HardErrorProcs);
91 }
92
93 NewProcs[HardErrorProcCount] = Proc;
94 HardErrorProcs = NewProcs;
95 HardErrorProcCount++;
96
97 return STATUS_SUCCESS;
98 }
99
100 /**********************************************************************
101 * CallInitComplete/0
102 */
103 static BOOL FASTCALL
104 CallInitComplete(void)
105 {
106 BOOL Ok;
107 unsigned i;
108
109 DPRINT("CSR: %s called\n", __FUNCTION__);
110
111 Ok = TRUE;
112 if (0 != InitCompleteProcCount)
113 {
114 for (i = 0; i < InitCompleteProcCount && Ok; i++)
115 {
116 Ok = (*(InitCompleteProcs[i]))();
117 }
118 RtlFreeHeap(CsrssApiHeap, 0, InitCompleteProcs);
119 }
120
121 return Ok;
122 }
123
124 BOOL
125 FASTCALL
126 CallHardError(void)
127 {
128 BOOL Ok;
129 unsigned i;
130
131 DPRINT("CSR: %s called\n", __FUNCTION__);
132
133 Ok = TRUE;
134 if (0 != HardErrorProcCount)
135 {
136 for (i = 0; i < HardErrorProcCount && Ok; i++)
137 {
138 Ok = (*(HardErrorProcs[i]))();
139 }
140 }
141
142 return Ok;
143 }
144
145 ULONG
146 InitializeVideoAddressSpace(VOID);
147
148 /**********************************************************************
149 * CsrpCreateObjectDirectory/3
150 */
151 static NTSTATUS
152 CsrpCreateObjectDirectory (int argc, char ** argv, char ** envp)
153 {
154 NTSTATUS Status;
155 OBJECT_ATTRIBUTES Attributes;
156
157 DPRINT("CSR: %s called\n", __FUNCTION__);
158
159
160 /* create object directory ('\Windows') */
161 RtlCreateUnicodeString (&CsrDirectoryName,
162 L"\\Windows");
163
164 InitializeObjectAttributes (&Attributes,
165 &CsrDirectoryName,
166 OBJ_OPENIF,
167 NULL,
168 NULL);
169
170 Status = NtOpenDirectoryObject(&CsrObjectDirectory,
171 0xF000F, /* ea:??? */
172 &Attributes);
173 return Status;
174 }
175
176 /**********************************************************************
177 * CsrpInitVideo/3
178 *
179 * TODO: we need a virtual device for sessions other than
180 * TODO: the console one
181 */
182 static NTSTATUS
183 CsrpInitVideo (int argc, char ** argv, char ** envp)
184 {
185 OBJECT_ATTRIBUTES ObjectAttributes;
186 UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\??\\DISPLAY1");
187 IO_STATUS_BLOCK Iosb;
188 HANDLE VideoHandle = (HANDLE) 0;
189 NTSTATUS Status = STATUS_SUCCESS;
190
191 DPRINT("CSR: %s called\n", __FUNCTION__);
192
193 InitializeVideoAddressSpace();
194
195 InitializeObjectAttributes(&ObjectAttributes,
196 &DeviceName,
197 0,
198 NULL,
199 NULL);
200 Status = NtOpenFile(&VideoHandle,
201 FILE_ALL_ACCESS,
202 &ObjectAttributes,
203 &Iosb,
204 0,
205 0);
206 if (NT_SUCCESS(Status))
207 {
208 NtClose(VideoHandle);
209 }
210 return Status;
211 }
212
213 /**********************************************************************
214 * CsrpInitWin32Csr/3
215 *
216 * TODO: this function should be turned more general to load an
217 * TODO: hosted server DLL as received from the command line;
218 * TODO: for instance: ServerDll=winsrv:ConServerDllInitialization,2
219 * TODO: ^method ^dll ^api ^sid
220 * TODO:
221 * TODO: CsrpHostServerDll (LPWSTR DllName,
222 * TODO: LPWSTR ApiName,
223 * TODO: DWORD ServerId)
224 */
225 static NTSTATUS
226 CsrpInitWin32Csr (int argc, char ** argv, char ** envp)
227 {
228 NTSTATUS Status;
229 UNICODE_STRING DllName;
230 HINSTANCE hInst;
231 ANSI_STRING ProcName;
232 CSRPLUGIN_INITIALIZE_PROC InitProc;
233 CSRSS_EXPORTED_FUNCS Exports;
234 PCSRSS_API_DEFINITION ApiDefinitions;
235 PCSRSS_OBJECT_DEFINITION ObjectDefinitions;
236 CSRPLUGIN_INIT_COMPLETE_PROC InitCompleteProc;
237 CSRPLUGIN_HARDERROR_PROC HardErrorProc;
238
239 DPRINT("CSR: %s called\n", __FUNCTION__);
240
241 RtlInitUnicodeString(&DllName, L"win32csr.dll");
242 Status = LdrLoadDll(NULL, 0, &DllName, (PVOID *) &hInst);
243 if (! NT_SUCCESS(Status))
244 {
245 return Status;
246 }
247 RtlInitAnsiString(&ProcName, "Win32CsrInitialization");
248 Status = LdrGetProcedureAddress(hInst, &ProcName, 0, (PVOID *) &InitProc);
249 if (! NT_SUCCESS(Status))
250 {
251 return Status;
252 }
253 Exports.CsrInsertObjectProc = CsrInsertObject;
254 Exports.CsrGetObjectProc = CsrGetObject;
255 Exports.CsrReleaseObjectProc = CsrReleaseObject;
256 Exports.CsrEnumProcessesProc = CsrEnumProcesses;
257 if (! (*InitProc)(&ApiDefinitions, &ObjectDefinitions, &InitCompleteProc,
258 &HardErrorProc, &Exports, CsrssApiHeap))
259 {
260 return STATUS_UNSUCCESSFUL;
261 }
262
263 Status = CsrApiRegisterDefinitions(ApiDefinitions);
264 if (! NT_SUCCESS(Status))
265 {
266 return Status;
267 }
268 Status = CsrRegisterObjectDefinitions(ObjectDefinitions);
269 if (! NT_SUCCESS(Status))
270 {
271 return Status;
272 }
273 if (NULL != InitCompleteProc)
274 {
275 Status = CsrpAddInitCompleteProc(InitCompleteProc);
276 }
277 if (HardErrorProc) Status = CsrpAddHardErrorProc(HardErrorProc);
278
279 return Status;
280 }
281
282 CSRSS_API_DEFINITION NativeDefinitions[] =
283 {
284 CSRSS_DEFINE_API(CREATE_PROCESS, CsrCreateProcess),
285 CSRSS_DEFINE_API(TERMINATE_PROCESS, CsrTerminateProcess),
286 CSRSS_DEFINE_API(CONNECT_PROCESS, CsrConnectProcess),
287 CSRSS_DEFINE_API(REGISTER_SERVICES_PROCESS, CsrRegisterServicesProcess),
288 CSRSS_DEFINE_API(GET_SHUTDOWN_PARAMETERS, CsrGetShutdownParameters),
289 CSRSS_DEFINE_API(SET_SHUTDOWN_PARAMETERS, CsrSetShutdownParameters),
290 CSRSS_DEFINE_API(GET_INPUT_HANDLE, CsrGetInputHandle),
291 CSRSS_DEFINE_API(GET_OUTPUT_HANDLE, CsrGetOutputHandle),
292 CSRSS_DEFINE_API(CLOSE_HANDLE, CsrCloseHandle),
293 CSRSS_DEFINE_API(VERIFY_HANDLE, CsrVerifyHandle),
294 CSRSS_DEFINE_API(DUPLICATE_HANDLE, CsrDuplicateHandle),
295 CSRSS_DEFINE_API(GET_INPUT_WAIT_HANDLE, CsrGetInputWaitHandle),
296 { 0, 0, NULL }
297 };
298
299 static NTSTATUS STDCALL
300 CsrpCreateListenPort (IN LPWSTR Name,
301 IN OUT PHANDLE Port,
302 IN PTHREAD_START_ROUTINE ListenThread)
303 {
304 NTSTATUS Status = STATUS_SUCCESS;
305 OBJECT_ATTRIBUTES PortAttributes;
306 UNICODE_STRING PortName;
307
308 DPRINT("CSR: %s called\n", __FUNCTION__);
309
310 RtlInitUnicodeString (& PortName, Name);
311 InitializeObjectAttributes (& PortAttributes,
312 & PortName,
313 0,
314 NULL,
315 NULL);
316 Status = NtCreatePort ( Port,
317 & PortAttributes,
318 LPC_MAX_DATA_LENGTH, /* TODO: make caller set it*/
319 LPC_MAX_MESSAGE_LENGTH, /* TODO: make caller set it*/
320 0); /* TODO: make caller set it*/
321 if(!NT_SUCCESS(Status))
322 {
323 DPRINT1("CSR: %s: NtCreatePort failed (Status=%08lx)\n",
324 __FUNCTION__, Status);
325 return Status;
326 }
327 Status = RtlCreateUserThread(NtCurrentProcess(),
328 NULL,
329 FALSE,
330 0,
331 0,
332 0,
333 (PTHREAD_START_ROUTINE) ListenThread,
334 *Port,
335 NULL,
336 NULL);
337 return Status;
338 }
339
340 /* === INIT ROUTINES === */
341
342 /**********************************************************************
343 * CsrpCreateHeap/3
344 */
345 static NTSTATUS
346 CsrpCreateHeap (int argc, char ** argv, char ** envp)
347 {
348 DPRINT("CSR: %s called\n", __FUNCTION__);
349
350 CsrssApiHeap = RtlCreateHeap(HEAP_GROWABLE,
351 NULL,
352 65536,
353 65536,
354 NULL,
355 NULL);
356 if (CsrssApiHeap == NULL)
357 {
358 return STATUS_UNSUCCESSFUL;
359 }
360 return STATUS_SUCCESS;
361 }
362
363 /**********************************************************************
364 * CsrpCreateCallbackPort/3
365 */
366 static NTSTATUS
367 CsrpCreateCallbackPort (int argc, char ** argv, char ** envp)
368 {
369 DPRINT("CSR: %s called\n", __FUNCTION__);
370
371 return CsrpCreateListenPort (L"\\Windows\\SbApiPort",
372 & hSbApiPort,
373 ServerSbApiPortThread);
374 }
375
376 /**********************************************************************
377 * CsrpRegisterSubsystem/3
378 */
379 static NTSTATUS
380 CsrpRegisterSubsystem (int argc, char ** argv, char ** envp)
381 {
382 NTSTATUS Status = STATUS_SUCCESS;
383 OBJECT_ATTRIBUTES BootstrapOkAttributes;
384 UNICODE_STRING Name;
385
386 DPRINT("CSR: %s called\n", __FUNCTION__);
387
388 /*
389 * Create the event object the callback port
390 * thread will signal *if* the SM will
391 * authorize us to bootstrap.
392 */
393 RtlInitUnicodeString (& Name, L"\\CsrssBooting");
394 InitializeObjectAttributes(& BootstrapOkAttributes,
395 & Name,
396 0, NULL, NULL);
397 Status = NtCreateEvent (& hBootstrapOk,
398 EVENT_ALL_ACCESS,
399 & BootstrapOkAttributes,
400 SynchronizationEvent,
401 FALSE);
402 if(!NT_SUCCESS(Status))
403 {
404 DPRINT("CSR: %s: NtCreateEvent failed (Status=0x%08lx)\n",
405 __FUNCTION__, Status);
406 return Status;
407 }
408 /*
409 * Let's tell the SM a new environment
410 * subsystem server is in the system.
411 */
412 RtlInitUnicodeString (& Name, L"\\Windows\\SbApiPort");
413 DPRINT("CSR: %s: registering with SM for\n IMAGE_SUBSYSTEM_WINDOWS_CUI == 3\n", __FUNCTION__);
414 Status = SmConnectApiPort (& Name,
415 hSbApiPort,
416 IMAGE_SUBSYSTEM_WINDOWS_CUI,
417 & hSmApiPort);
418 if(!NT_SUCCESS(Status))
419 {
420 DPRINT("CSR: %s unable to connect to the SM (Status=0x%08lx)\n",
421 __FUNCTION__, Status);
422 NtClose (hBootstrapOk);
423 return Status;
424 }
425 /*
426 * Wait for SM to reply OK... If the SM
427 * won't answer, we hang here forever!
428 */
429 DPRINT("CSR: %s: waiting for SM to OK boot...\n", __FUNCTION__);
430 Status = NtWaitForSingleObject (hBootstrapOk,
431 FALSE,
432 NULL);
433 NtClose (hBootstrapOk);
434 return Status;
435 }
436
437 /**********************************************************************
438 * EnvpToUnicodeString/2
439 */
440 static ULONG FASTCALL
441 EnvpToUnicodeString (char ** envp, PUNICODE_STRING UnicodeEnv)
442 {
443 ULONG CharCount = 0;
444 ULONG Index = 0;
445 ANSI_STRING AnsiEnv;
446
447 UnicodeEnv->Buffer = NULL;
448
449 for (Index=0; NULL != envp[Index]; Index++)
450 {
451 CharCount += strlen (envp[Index]);
452 ++ CharCount;
453 }
454 ++ CharCount;
455
456 AnsiEnv.Buffer = RtlAllocateHeap (RtlGetProcessHeap(), 0, CharCount);
457 if (NULL != AnsiEnv.Buffer)
458 {
459
460 PCHAR WritePos = AnsiEnv.Buffer;
461
462 for (Index=0; NULL != envp[Index]; Index++)
463 {
464 strcpy (WritePos, envp[Index]);
465 WritePos += strlen (envp[Index]) + 1;
466 }
467
468 /* FIXME: the last (double) nullterm should perhaps not be included in Length
469 * but only in MaximumLength. -Gunnar */
470 AnsiEnv.Buffer [CharCount-1] = '\0';
471 AnsiEnv.Length = CharCount;
472 AnsiEnv.MaximumLength = CharCount;
473
474 RtlAnsiStringToUnicodeString (UnicodeEnv, & AnsiEnv, TRUE);
475 RtlFreeHeap (RtlGetProcessHeap(), 0, AnsiEnv.Buffer);
476 }
477 return CharCount;
478 }
479 /**********************************************************************
480 * CsrpLoadKernelModeDriver/3
481 */
482 static NTSTATUS
483 CsrpLoadKernelModeDriver (int argc, char ** argv, char ** envp)
484 {
485 NTSTATUS Status = STATUS_SUCCESS;
486 WCHAR Data [MAX_PATH + 1];
487 ULONG DataLength = sizeof Data;
488 ULONG DataType = 0;
489 UNICODE_STRING Environment;
490
491
492 DPRINT("SM: %s called\n", __FUNCTION__);
493
494
495 EnvpToUnicodeString (envp, & Environment);
496 Status = SmLookupSubsystem (L"Kmode",
497 Data,
498 & DataLength,
499 & DataType,
500 Environment.Buffer);
501 RtlFreeUnicodeString (& Environment);
502 if((STATUS_SUCCESS == Status) && (DataLength > sizeof Data[0]))
503 {
504 WCHAR ImagePath [MAX_PATH + 1] = {0};
505 UNICODE_STRING ModuleName;
506
507 wcscpy (ImagePath, L"\\??\\");
508 wcscat (ImagePath, Data);
509 RtlInitUnicodeString (& ModuleName, ImagePath);
510 Status = NtSetSystemInformation(/* FIXME: SystemLoadAndCallImage */
511 SystemExtendServiceTableInformation,
512 & ModuleName,
513 sizeof ModuleName);
514 if(!NT_SUCCESS(Status))
515 {
516 DPRINT("WIN: %s: loading Kmode failed (Status=0x%08lx)\n",
517 __FUNCTION__, Status);
518 }
519 }
520 return Status;
521 }
522
523 /**********************************************************************
524 * CsrpCreateApiPort/2
525 */
526 static NTSTATUS
527 CsrpCreateApiPort (int argc, char ** argv, char ** envp)
528 {
529 DPRINT("CSR: %s called\n", __FUNCTION__);
530
531 CsrInitProcessData();
532 return CsrpCreateListenPort (L"\\Windows\\ApiPort",
533 & hApiPort,
534 #ifdef NTLPC
535 (PTHREAD_START_ROUTINE)ClientConnectionThread);
536 #else
537 ServerApiPortThread);
538 #endif
539 }
540
541 /**********************************************************************
542 * CsrpApiRegisterDef/0
543 */
544 static NTSTATUS
545 CsrpApiRegisterDef (int argc, char ** argv, char ** envp)
546 {
547 return CsrApiRegisterDefinitions(NativeDefinitions);
548 }
549
550 /**********************************************************************
551 * CsrpCCTS/2
552 */
553 static NTSTATUS
554 CsrpCCTS (int argc, char ** argv, char ** envp)
555 {
556 ULONG Dummy;
557 ULONG DummyLength = sizeof(Dummy);
558 return CsrClientConnectToServer(L"\\Windows",
559 0, &Dummy, &DummyLength, NULL);
560 }
561
562 /**********************************************************************
563 * CsrpRunWinlogon/0
564 *
565 * Start the logon process (winlogon.exe).
566 *
567 * TODO: this should be moved in CsrpCreateSession/x (one per session)
568 * TODO: in its own desktop (one logon desktop per winstation).
569 */
570 static NTSTATUS
571 CsrpRunWinlogon (int argc, char ** argv, char ** envp)
572 {
573 NTSTATUS Status = STATUS_SUCCESS;
574 UNICODE_STRING ImagePath;
575 UNICODE_STRING CommandLine;
576 PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;
577 RTL_USER_PROCESS_INFORMATION ProcessInfo;
578
579
580 DPRINT("CSR: %s called\n", __FUNCTION__);
581
582 /* initialize the process parameters */
583 RtlInitUnicodeString (& ImagePath, L"\\SystemRoot\\system32\\winlogon.exe");
584 RtlInitUnicodeString (& CommandLine, L"");
585 RtlCreateProcessParameters(& ProcessParameters,
586 & ImagePath,
587 NULL,
588 NULL,
589 & CommandLine,
590 NULL,
591 NULL,
592 NULL,
593 NULL,
594 NULL);
595 /* Create the winlogon process */
596 Status = RtlCreateUserProcess (& ImagePath,
597 OBJ_CASE_INSENSITIVE,
598 ProcessParameters,
599 NULL,
600 NULL,
601 NULL,
602 FALSE,
603 NULL,
604 NULL,
605 & ProcessInfo);
606 /* Cleanup */
607 RtlDestroyProcessParameters (ProcessParameters);
608 if (!NT_SUCCESS(Status))
609 {
610 DPRINT1("SM: %s: loading winlogon.exe failed (Status=%08lx)\n",
611 __FUNCTION__, Status);
612 }
613 ZwResumeThread(ProcessInfo.ThreadHandle, NULL);
614 return Status;
615 }
616
617 static NTSTATUS
618 CsrpCreateHardErrorPort (int argc, char ** argv, char ** envp)
619 {
620 return NtSetDefaultHardErrorPort(hApiPort);
621 }
622
623 typedef NTSTATUS (* CSR_INIT_ROUTINE)(int,char**,char**);
624
625 struct {
626 BOOL Required;
627 CSR_INIT_ROUTINE EntryPoint;
628 PCHAR ErrorMessage;
629 } InitRoutine [] = {
630 {TRUE, CsrpCreateCallbackPort, "create the callback port \\Windows\\SbApiPort"},
631 {TRUE, CsrpRegisterSubsystem, "register with SM"},
632 {TRUE, CsrpCreateHeap, "create the CSR heap"},
633 {TRUE, CsrpCreateApiPort, "create the api port \\Windows\\ApiPort"},
634 {TRUE, CsrpCreateHardErrorPort, "create the hard error port"},
635 {TRUE, CsrpCreateObjectDirectory,"create the object directory \\Windows"},
636 {TRUE, CsrpLoadKernelModeDriver, "load Kmode driver"},
637 {TRUE, CsrpInitVideo, "initialize video"},
638 {TRUE, CsrpApiRegisterDef, "initialize api definitions"},
639 {TRUE, CsrpCCTS, "connect client to server"},
640 {TRUE, CsrpInitWin32Csr, "load usermode dll"},
641 {TRUE, CsrpRunWinlogon, "run WinLogon"},
642 };
643
644 /**********************************************************************
645 * NAME
646 * CsrServerInitialization
647 *
648 * DESCRIPTION
649 * Initialize the Win32 environment subsystem server.
650 *
651 * RETURN VALUE
652 * TRUE: Initialization OK; otherwise FALSE.
653 */
654 BOOL STDCALL
655 CsrServerInitialization (
656 int argc,
657 char ** argv,
658 char ** envp
659 )
660 {
661 UINT i = 0;
662 NTSTATUS Status = STATUS_SUCCESS;
663
664 DPRINT("CSR: %s called\n", __FUNCTION__);
665
666 for (i=0; i < (sizeof InitRoutine / sizeof InitRoutine[0]); i++)
667 {
668 Status = InitRoutine[i].EntryPoint(argc,argv,envp);
669 if(!NT_SUCCESS(Status))
670 {
671 DPRINT1("CSR: %s: failed to %s (Status=%08lx)\n",
672 __FUNCTION__,
673 InitRoutine[i].ErrorMessage,
674 Status);
675 if (InitRoutine[i].Required)
676 {
677 return FALSE;
678 }
679 }
680 }
681 if (CallInitComplete())
682 {
683 Status = SmCompleteSession (hSmApiPort,hSbApiPort,hApiPort);
684 return TRUE;
685 }
686 return FALSE;
687 }
688
689 /* EOF */