Partially fixed up tree after merge from HEAD. More to do.
[reactos.git] / reactos / dll / win32 / ws2_32 / misc / dllmain.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS WinSock 2 DLL
4 * FILE: misc/dllmain.c
5 * PURPOSE: DLL entry point
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/09-2000 Created
9 */
10 #include <roscfg.h>
11 #include <w32api.h>
12 #include <ws2_32.h>
13 #include <catalog.h>
14 #include <handle.h>
15 #include <upcall.h>
16
17 #ifdef DBG
18
19 /* See debug.h for debug/trace constants */
20 //DWORD DebugTraceLevel = MIN_TRACE;
21 //DWORD DebugTraceLevel = MAX_TRACE;
22 //DWORD DebugTraceLevel = DEBUG_ULTRA;
23 DWORD DebugTraceLevel = 0;
24 #endif /* DBG */
25
26 /* To make the linker happy */
27 VOID STDCALL KeBugCheck (ULONG BugCheckCode) {}
28
29
30 HANDLE GlobalHeap;
31 BOOL WsaInitialized = FALSE; /* TRUE if WSAStartup() has been successfully called */
32 WSPUPCALLTABLE UpcallTable;
33
34
35 /*
36 * @implemented
37 */
38 INT
39 EXPORT
40 WSAGetLastError(VOID)
41 {
42
43 PWINSOCK_THREAD_BLOCK p = NtCurrentTeb()->WinSockData;
44
45 if (p) {
46 return p->LastErrorValue;
47 } else {
48 /* FIXME: What error code should we use here? Can this even happen? */
49 return ERROR_BAD_ENVIRONMENT;
50 }
51 }
52
53
54 /*
55 * @implemented
56 */
57 VOID
58 EXPORT
59 WSASetLastError(
60 IN INT iError)
61 {
62 PWINSOCK_THREAD_BLOCK p = NtCurrentTeb()->WinSockData;
63
64 if (p)
65 p->LastErrorValue = iError;
66 }
67
68
69 /*
70 * @implemented
71 */
72 INT
73 EXPORT
74 WSAStartup(
75 IN WORD wVersionRequested,
76 OUT LPWSADATA lpWSAData)
77 {
78 WS_DbgPrint(MAX_TRACE, ("WSAStartup of ws2_32.dll\n"));
79
80 lpWSAData->wVersion = wVersionRequested;
81 lpWSAData->wHighVersion = 2;
82 lstrcpyA(lpWSAData->szDescription, "WinSock 2.0");
83 lstrcpyA(lpWSAData->szSystemStatus, "Running");
84 lpWSAData->iMaxSockets = 0;
85 lpWSAData->iMaxUdpDg = 0;
86 lpWSAData->lpVendorInfo = NULL;
87
88 WSASETINITIALIZED;
89
90 return NO_ERROR;
91 }
92
93
94 /*
95 * @implemented
96 */
97 INT
98 EXPORT
99 WSACleanup(VOID)
100 {
101 WS_DbgPrint(MAX_TRACE, ("WSACleanup of ws2_32.dll\n"));
102
103 if (!WSAINITIALIZED) {
104 WSASetLastError(WSANOTINITIALISED);
105 return WSANOTINITIALISED;
106 }
107
108 return NO_ERROR;
109 }
110
111
112 /*
113 * @implemented
114 */
115 SOCKET
116 EXPORT
117 socket(
118 IN INT af,
119 IN INT type,
120 IN INT protocol)
121 {
122 return WSASocketA(af, type, protocol, NULL, 0, 0);
123 }
124
125
126 /*
127 * @implemented
128 */
129 SOCKET
130 EXPORT
131 WSASocketA(
132 IN INT af,
133 IN INT type,
134 IN INT protocol,
135 IN LPWSAPROTOCOL_INFOA lpProtocolInfo,
136 IN GROUP g,
137 IN DWORD dwFlags)
138 /*
139 * FUNCTION: Creates a new socket
140 */
141 {
142 WSAPROTOCOL_INFOW ProtocolInfoW;
143 LPWSAPROTOCOL_INFOW p;
144 UNICODE_STRING StringU;
145 ANSI_STRING StringA;
146
147 WS_DbgPrint(MAX_TRACE, ("af (%d) type (%d) protocol (%d).\n",
148 af, type, protocol));
149
150 if (lpProtocolInfo) {
151 memcpy(&ProtocolInfoW,
152 lpProtocolInfo,
153 sizeof(WSAPROTOCOL_INFOA) -
154 sizeof(CHAR) * (WSAPROTOCOL_LEN + 1));
155 RtlInitAnsiString(&StringA, (LPSTR)lpProtocolInfo->szProtocol);
156 RtlInitUnicodeString(&StringU, (LPWSTR)&ProtocolInfoW.szProtocol);
157 RtlAnsiStringToUnicodeString(&StringU, &StringA, FALSE);
158 p = &ProtocolInfoW;
159 } else {
160 p = NULL;
161 }
162
163 return WSASocketW(af,
164 type,
165 protocol,
166 p,
167 g,
168 dwFlags);
169 }
170
171
172 /*
173 * @implemented
174 */
175 SOCKET
176 EXPORT
177 WSASocketW(
178 IN INT af,
179 IN INT type,
180 IN INT protocol,
181 IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
182 IN GROUP g,
183 IN DWORD dwFlags)
184 /*
185 * FUNCTION: Creates a new socket descriptor
186 * ARGUMENTS:
187 * af = Address family
188 * type = Socket type
189 * protocol = Protocol type
190 * lpProtocolInfo = Pointer to protocol information
191 * g = Reserved
192 * dwFlags = Socket flags
193 * RETURNS:
194 * Created socket descriptor, or INVALID_SOCKET if it could not be created
195 */
196 {
197 INT Status;
198 SOCKET Socket;
199 PCATALOG_ENTRY Provider;
200 WSAPROTOCOL_INFOW ProtocolInfo;
201
202 WS_DbgPrint(MAX_TRACE, ("af (%d) type (%d) protocol (%d).\n",
203 af, type, protocol));
204
205 if (!WSAINITIALIZED) {
206 WS_DbgPrint(MAX_TRACE, ("af (%d) type (%d) protocol (%d) = WSANOTINITIALISED.\n",
207 af, type, protocol));
208 WSASetLastError(WSANOTINITIALISED);
209 return INVALID_SOCKET;
210 }
211
212 if (!lpProtocolInfo) {
213 lpProtocolInfo = &ProtocolInfo;
214 ZeroMemory(&ProtocolInfo, sizeof(WSAPROTOCOL_INFOW));
215
216 ProtocolInfo.iAddressFamily = af;
217 ProtocolInfo.iSocketType = type;
218 ProtocolInfo.iProtocol = protocol;
219 }
220
221 Provider = LocateProvider(lpProtocolInfo);
222 if (!Provider) {
223 WS_DbgPrint(MAX_TRACE, ("af (%d) type (%d) protocol (%d) = WSAEAFNOSUPPORT.\n",
224 af, type, protocol));
225 WSASetLastError(WSAEAFNOSUPPORT);
226 return INVALID_SOCKET;
227 }
228
229 Status = LoadProvider(Provider, lpProtocolInfo);
230 if (Status != NO_ERROR) {
231 WS_DbgPrint(MAX_TRACE, ("af (%d) type (%d) protocol (%d) = %d.\n",
232 af, type, protocol, Status));
233 WSASetLastError(Status);
234 return INVALID_SOCKET;
235 }
236
237 WS_DbgPrint(MAX_TRACE, ("Calling WSPSocket at (0x%X).\n",
238 Provider->ProcTable.lpWSPSocket));
239
240 assert(Provider->ProcTable.lpWSPSocket);
241
242 WS_DbgPrint(MAX_TRACE,("About to call provider socket fn\n"));
243
244 Socket = Provider->ProcTable.lpWSPSocket(
245 af,
246 type,
247 protocol,
248 lpProtocolInfo,
249 g,
250 dwFlags,
251 &Status);
252
253 WS_DbgPrint(MAX_TRACE,("Socket: %x, Status: %x\n", Socket, Status));
254
255 if (Status != NO_ERROR) {
256 WSASetLastError(Status);
257 return INVALID_SOCKET;
258 }
259
260 WS_DbgPrint(MAX_TRACE,("Status: %x\n", Status));
261
262 return Socket;
263 }
264
265
266 /*
267 * @implemented
268 */
269 INT
270 EXPORT
271 closesocket(
272 IN SOCKET s)
273 /*
274 * FUNCTION: Closes a socket descriptor
275 * ARGUMENTS:
276 * s = Socket descriptor
277 * RETURNS:
278 * 0, or SOCKET_ERROR if an error ocurred
279 */
280 {
281 PCATALOG_ENTRY Provider;
282 INT Status;
283 INT Errno;
284
285 WS_DbgPrint(MAX_TRACE, ("s (0x%X).\n", s));
286
287 if (!WSAINITIALIZED) {
288 WSASetLastError(WSANOTINITIALISED);
289 return SOCKET_ERROR;
290 }
291
292 if (!ReferenceProviderByHandle((HANDLE)s, &Provider)) {
293 WSASetLastError(WSAENOTSOCK);
294 return SOCKET_ERROR;
295 }
296
297 CloseProviderHandle((HANDLE)s);
298
299 WS_DbgPrint(MAX_TRACE,("DereferenceProviderByHandle\n"));
300
301 DereferenceProviderByPointer(Provider);
302
303 WS_DbgPrint(MAX_TRACE,("DereferenceProviderByHandle Done\n"));
304
305 Status = Provider->ProcTable.lpWSPCloseSocket(s, &Errno);
306
307 WS_DbgPrint(MAX_TRACE,("Provider Close Done\n"));
308
309 if (Status == SOCKET_ERROR)
310 WSASetLastError(Errno);
311
312 WS_DbgPrint(MAX_TRACE,("Returning success\n"));
313
314 return 0;
315 }
316
317
318 /*
319 * @implemented
320 */
321 INT
322 EXPORT
323 select(
324 IN INT nfds,
325 IN OUT LPFD_SET readfds,
326 IN OUT LPFD_SET writefds,
327 IN OUT LPFD_SET exceptfds,
328 IN CONST struct timeval *timeout)
329 /*
330 * FUNCTION: Returns status of one or more sockets
331 * ARGUMENTS:
332 * nfds = Always ignored
333 * readfds = Pointer to socket set to be checked for readability (optional)
334 * writefds = Pointer to socket set to be checked for writability (optional)
335 * exceptfds = Pointer to socket set to be checked for errors (optional)
336 * timeout = Pointer to a TIMEVAL structure indicating maximum wait time
337 * (NULL means wait forever)
338 * RETURNS:
339 * Number of ready socket descriptors, or SOCKET_ERROR if an error ocurred
340 */
341 {
342 PCATALOG_ENTRY Provider = NULL;
343 INT Count;
344 INT Errno;
345
346 WS_DbgPrint(MAX_TRACE, ("readfds (0x%X) writefds (0x%X) exceptfds (0x%X).\n",
347 readfds, writefds, exceptfds));
348
349 if (!WSAINITIALIZED) {
350 WSASetLastError(WSANOTINITIALISED);
351 WS_DbgPrint(MID_TRACE,("Not initialized\n"));
352 return SOCKET_ERROR;
353 }
354
355 /* FIXME: Sockets in FD_SETs should be sorted by their provider */
356
357 /* FIXME: For now, assume only one service provider */
358 if ((readfds != NULL) && (readfds->fd_count > 0)) {
359 if (!ReferenceProviderByHandle((HANDLE)readfds->fd_array[0], &Provider)) {
360 WSASetLastError(WSAENOTSOCK);
361 WS_DbgPrint(MID_TRACE,("No provider (read)\n"));
362 return SOCKET_ERROR;
363 }
364 } else if ((writefds != NULL) && (writefds->fd_count > 0)) {
365 if (!ReferenceProviderByHandle((HANDLE)writefds->fd_array[0], &Provider)) {
366 WSASetLastError(WSAENOTSOCK);
367 WS_DbgPrint(MID_TRACE,("No provider (write)\n"));
368 return SOCKET_ERROR;
369 }
370 } else if ((exceptfds != NULL) && (exceptfds->fd_count > 0)) {
371 if (!ReferenceProviderByHandle((HANDLE)exceptfds->fd_array[0], &Provider)) {
372 WSASetLastError(WSAENOTSOCK);
373 WS_DbgPrint(MID_TRACE,("No provider (err)\n"));
374 return SOCKET_ERROR;
375 }
376 #if 0 /* XXX empty select is not an error */
377 } else {
378 WSASetLastError(WSAEINVAL);
379 return SOCKET_ERROR;
380 #endif
381 }
382
383 if( !Provider ) {
384 if( timeout ) {
385 WS_DbgPrint(MID_TRACE,("Select: used as timer\n"));
386 Sleep( timeout->tv_sec * 1000 + (timeout->tv_usec / 1000) );
387 }
388 return 0;
389 } else if (Provider->ProcTable.lpWSPSelect) {
390 WS_DbgPrint(MID_TRACE,("Calling WSPSelect:%x\n", Provider->ProcTable.lpWSPSelect));
391 Count = Provider->ProcTable.lpWSPSelect(
392 nfds, readfds, writefds, exceptfds, (LPTIMEVAL)timeout, &Errno);
393
394 WS_DbgPrint(MAX_TRACE, ("[%x] Select: Count %d Errno %x\n",
395 Provider, Count, Errno));
396
397 DereferenceProviderByPointer(Provider);
398
399 if (Errno != NO_ERROR) {
400 WSASetLastError(Errno);
401 return SOCKET_ERROR;
402 }
403 } else {
404 WSASetLastError(WSAEINVAL);
405 return SOCKET_ERROR;
406 }
407
408 return Count;
409 }
410
411
412 /*
413 * @implemented
414 */
415 INT
416 EXPORT
417 bind(
418 IN SOCKET s,
419 IN CONST struct sockaddr *name,
420 IN INT namelen)
421 {
422 PCATALOG_ENTRY Provider;
423 INT Status;
424 INT Errno;
425
426 if (!WSAINITIALIZED) {
427 WSASetLastError(WSANOTINITIALISED);
428 return SOCKET_ERROR;
429 }
430
431 if (!ReferenceProviderByHandle((HANDLE)s, &Provider)) {
432 WSASetLastError(WSAENOTSOCK);
433 return SOCKET_ERROR;
434 }
435
436 #if (__W32API_MAJOR_VERSION < 2 || __W32API_MINOR_VERSION < 5)
437 Status = Provider->ProcTable.lpWSPBind(
438 s, (CONST LPSOCKADDR) name, namelen, &Errno);
439 #else
440 Status = Provider->ProcTable.lpWSPBind(
441 s, name, namelen, &Errno);
442 #endif /* __W32API_MAJOR_VERSION < 2 || __W32API_MINOR_VERSION < 5 */
443
444 DereferenceProviderByPointer(Provider);
445
446 if (Status == SOCKET_ERROR) {
447 WSASetLastError(Errno);
448 }
449
450 return Status;
451 }
452
453
454 /*
455 * @implemented
456 */
457 INT
458 EXPORT
459 listen(
460 IN SOCKET s,
461 IN INT backlog)
462 {
463 PCATALOG_ENTRY Provider;
464 INT Status;
465 INT Errno;
466
467 if (!WSAINITIALIZED) {
468 WSASetLastError(WSANOTINITIALISED);
469 return SOCKET_ERROR;
470 }
471
472 if (!ReferenceProviderByHandle((HANDLE)s, &Provider)) {
473 WSASetLastError(WSAENOTSOCK);
474 return SOCKET_ERROR;
475 }
476
477 Status = Provider->ProcTable.lpWSPListen(
478 s, backlog, &Errno);
479
480 DereferenceProviderByPointer(Provider);
481
482 if (Status == SOCKET_ERROR) {
483 WSASetLastError(Errno);
484 }
485
486 return Status;
487 }
488
489
490 /*
491 * @implemented
492 */
493 SOCKET
494 EXPORT
495 accept(
496 IN SOCKET s,
497 OUT LPSOCKADDR addr,
498 OUT INT FAR* addrlen)
499 {
500 return WSAAccept(s, addr, addrlen, NULL, 0);
501 }
502
503
504 /*
505 * @implemented
506 */
507 INT
508 EXPORT
509 ioctlsocket(
510 IN SOCKET s,
511 IN LONG cmd,
512 IN OUT ULONG FAR* argp)
513 {
514 return WSAIoctl(s, cmd, argp, sizeof(ULONG), argp, sizeof(ULONG), argp, 0, 0);
515 }
516
517
518 /*
519 * @implemented
520 */
521 SOCKET
522 EXPORT
523 WSAAccept(
524 IN SOCKET s,
525 OUT LPSOCKADDR addr,
526 IN OUT LPINT addrlen,
527 IN LPCONDITIONPROC lpfnCondition,
528 IN DWORD dwCallbackData)
529 {
530 PCATALOG_ENTRY Provider;
531 SOCKET Socket;
532 INT Errno;
533
534 if (!WSAINITIALIZED) {
535 WSASetLastError(WSANOTINITIALISED);
536 return SOCKET_ERROR;
537 }
538
539 if (!ReferenceProviderByHandle((HANDLE)s, &Provider)) {
540 WSASetLastError(WSAENOTSOCK);
541 return SOCKET_ERROR;
542 }
543
544 WS_DbgPrint(MAX_TRACE,("Calling provider accept\n"));
545
546 Socket = Provider->ProcTable.lpWSPAccept(
547 s, addr, addrlen, lpfnCondition, dwCallbackData, &Errno);
548
549 WS_DbgPrint(MAX_TRACE,("Calling provider accept -> Socket %x, Errno %x\n",
550 Socket, Errno));
551
552 DereferenceProviderByPointer(Provider);
553
554 if (Socket == INVALID_SOCKET) {
555 WSASetLastError(Errno);
556 }
557
558 if( addr ) {
559 #ifdef DBG
560 LPSOCKADDR_IN sa = (LPSOCKADDR_IN)addr;
561 WS_DbgPrint(MAX_TRACE,("Returned address: %d %s:%d (len %d)\n",
562 sa->sin_family,
563 inet_ntoa(sa->sin_addr),
564 ntohs(sa->sin_port),
565 *addrlen));
566 #endif
567 }
568
569 return Socket;
570 }
571
572
573 /*
574 * @implemented
575 */
576 INT
577 EXPORT
578 connect(
579 IN SOCKET s,
580 IN CONST struct sockaddr *name,
581 IN INT namelen)
582 {
583 return WSAConnect(s, name, namelen, NULL, NULL, NULL, NULL);
584 }
585
586
587 /*
588 * @implemented
589 */
590 INT
591 EXPORT
592 WSAConnect(
593 IN SOCKET s,
594 IN CONST struct sockaddr *name,
595 IN INT namelen,
596 IN LPWSABUF lpCallerData,
597 OUT LPWSABUF lpCalleeData,
598 IN LPQOS lpSQOS,
599 IN LPQOS lpGQOS)
600 {
601 PCATALOG_ENTRY Provider;
602 INT Status;
603 INT Errno;
604
605 if (!WSAINITIALIZED) {
606 WSASetLastError(WSANOTINITIALISED);
607 return SOCKET_ERROR;
608 }
609
610 if (!ReferenceProviderByHandle((HANDLE)s, &Provider)) {
611 WSASetLastError(WSAENOTSOCK);
612 return SOCKET_ERROR;
613 }
614
615 #if (__W32API_MAJOR_VERSION < 2 || __W32API_MINOR_VERSION < 5)
616 Status = Provider->ProcTable.lpWSPConnect(
617 s, (CONST LPSOCKADDR) name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, &Errno);
618 #else
619 Status = Provider->ProcTable.lpWSPConnect(
620 s, name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, &Errno);
621 #endif
622
623 DereferenceProviderByPointer(Provider);
624
625 if (Status == SOCKET_ERROR) {
626 WSASetLastError(Errno);
627 }
628
629 return Status;
630 }
631
632
633 /*
634 * @implemented
635 */
636 INT
637 EXPORT
638 WSAIoctl(
639 IN SOCKET s,
640 IN DWORD dwIoControlCode,
641 IN LPVOID lpvInBuffer,
642 IN DWORD cbInBuffer,
643 OUT LPVOID lpvOutBuffer,
644 IN DWORD cbOutBuffer,
645 OUT LPDWORD lpcbBytesReturned,
646 IN LPWSAOVERLAPPED lpOverlapped,
647 IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
648 {
649 PCATALOG_ENTRY Provider;
650 INT Status;
651 INT Errno;
652
653 if (!WSAINITIALIZED) {
654 WSASetLastError(WSANOTINITIALISED);
655 return SOCKET_ERROR;
656 }
657
658 if (!ReferenceProviderByHandle((HANDLE)s, &Provider)) {
659 WSASetLastError(WSAENOTSOCK);
660 return SOCKET_ERROR;
661 }
662
663 Status = Provider->ProcTable.lpWSPIoctl(
664 s, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer,
665 cbOutBuffer, lpcbBytesReturned, lpOverlapped, lpCompletionRoutine,
666 NULL /* lpThreadId */, &Errno);
667
668 DereferenceProviderByPointer(Provider);
669
670 if (Status == SOCKET_ERROR) {
671 WSASetLastError(Errno);
672 }
673
674 return Status;
675 }
676
677 /*
678 * @implemented
679 */
680 INT
681 EXPORT
682 __WSAFDIsSet(SOCKET s, LPFD_SET set)
683 {
684 unsigned int i;
685
686 for( i = 0; i < set->fd_count; i++ )
687 if( set->fd_array[i] == s ) return TRUE;
688
689 return FALSE;
690 }
691
692 void free_winsock_thread_block(PWINSOCK_THREAD_BLOCK p) {
693 if(p) {
694 if(p->Hostent) { free_hostent(p->Hostent); p->Hostent = 0; }
695 if(p->Getservbyname){}
696 if(p->Getservbyport) {}
697
698
699 }
700 }
701
702 BOOL
703 STDCALL
704 DllMain(HANDLE hInstDll,
705 ULONG dwReason,
706 LPVOID lpReserved)
707 {
708 PWINSOCK_THREAD_BLOCK p;
709
710 WS_DbgPrint(MAX_TRACE, ("DllMain of ws2_32.dll.\n"));
711
712 switch (dwReason) {
713 case DLL_PROCESS_ATTACH:
714 GlobalHeap = GetProcessHeap();
715
716 CreateCatalog();
717
718 InitProviderHandleTable();
719
720 UpcallTable.lpWPUCloseEvent = WPUCloseEvent;
721 UpcallTable.lpWPUCloseSocketHandle = WPUCloseSocketHandle;
722 UpcallTable.lpWPUCreateEvent = WPUCreateEvent;
723 UpcallTable.lpWPUCreateSocketHandle = WPUCreateSocketHandle;
724 UpcallTable.lpWPUFDIsSet = WPUFDIsSet;
725 UpcallTable.lpWPUGetProviderPath = WPUGetProviderPath;
726 UpcallTable.lpWPUModifyIFSHandle = WPUModifyIFSHandle;
727 UpcallTable.lpWPUPostMessage = PostMessageW;
728 UpcallTable.lpWPUQueryBlockingCallback = WPUQueryBlockingCallback;
729 UpcallTable.lpWPUQuerySocketHandleContext = WPUQuerySocketHandleContext;
730 UpcallTable.lpWPUQueueApc = WPUQueueApc;
731 UpcallTable.lpWPUResetEvent = WPUResetEvent;
732 UpcallTable.lpWPUSetEvent = WPUSetEvent;
733 UpcallTable.lpWPUOpenCurrentThread = WPUOpenCurrentThread;
734 UpcallTable.lpWPUCloseThread = WPUCloseThread;
735
736 /* Fall through to thread attachment handler */
737
738 case DLL_THREAD_ATTACH:
739 p = HeapAlloc(GlobalHeap, 0, sizeof(WINSOCK_THREAD_BLOCK));
740
741 WS_DbgPrint(MAX_TRACE, ("Thread block at 0x%X.\n", p));
742
743 if (!p) {
744 return FALSE;
745 }
746
747 p->Hostent = NULL;
748 p->LastErrorValue = NO_ERROR;
749 p->Getservbyname = NULL;
750 p->Getservbyport = NULL;
751
752 NtCurrentTeb()->WinSockData = p;
753 break;
754
755 case DLL_PROCESS_DETACH:
756 p = NtCurrentTeb()->WinSockData;
757
758 if (p)
759 HeapFree(GlobalHeap, 0, p);
760
761 DestroyCatalog();
762
763 FreeProviderHandleTable();
764 break;
765
766 case DLL_THREAD_DETACH:
767 p = NtCurrentTeb()->WinSockData;
768
769 if (p)
770 HeapFree(GlobalHeap, 0, p);
771 break;
772 }
773
774 WS_DbgPrint(MAX_TRACE, ("DllMain of ws2_32.dll. Leaving.\n"));
775
776 return TRUE;
777 }
778
779 /* EOF */