2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Ancillary Function Driver DLL
5 * PURPOSE: DLL entry point
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 01/09-2000 Created
13 #include <rosrtl/string.h>
17 /* See debug.h for debug/trace constants */
18 DWORD DebugTraceLevel
= MIN_TRACE
;
19 //DWORD DebugTraceLevel = DEBUG_ULTRA;
23 /* To make the linker happy */
24 VOID STDCALL
KeBugCheck (ULONG BugCheckCode
) {}
28 WSPUPCALLTABLE Upcalls
;
29 LPWPUCOMPLETEOVERLAPPEDREQUEST lpWPUCompleteOverlappedRequest
;
30 CRITICAL_SECTION InitCriticalSection
;
31 DWORD StartupCount
= 0;
32 HANDLE CommandChannel
;
41 DWORD NotificationEvents
,
42 PUNICODE_STRING TdiDeviceName
)
44 * FUNCTION: Opens a socket
46 * Socket = Address of buffer to place socket descriptor
47 * AddressFamily = Address family
48 * SocketType = Type of socket
49 * Protocol = Protocol type
50 * HelperContext = Pointer to context information for helper DLL
51 * NotificationEvents = Events for which helper DLL is to be notified
52 * TdiDeviceName = Pointer to name of TDI device to use
57 OBJECT_ATTRIBUTES ObjectAttributes
;
58 PAFD_SOCKET_INFORMATION SocketInfo
;
59 PFILE_FULL_EA_INFORMATION EaInfo
;
60 UNICODE_STRING DeviceName
;
67 AFD_DbgPrint(MAX_TRACE
, ("Socket (0x%X) TdiDeviceName (%wZ)\n",
68 Socket
, TdiDeviceName
));
70 AFD_DbgPrint(MAX_TRACE
, ("Socket2 (0x%X) TdiDeviceName (%S)\n",
71 Socket
, TdiDeviceName
->Buffer
));
73 EaShort
= sizeof(FILE_FULL_EA_INFORMATION
) +
75 sizeof(AFD_SOCKET_INFORMATION
);
77 EaLength
= EaShort
+ TdiDeviceName
->Length
+ sizeof(WCHAR
);
79 EaInfo
= (PFILE_FULL_EA_INFORMATION
)HeapAlloc(GlobalHeap
, 0, EaLength
);
81 return STATUS_INSUFFICIENT_RESOURCES
;
84 RtlZeroMemory(EaInfo
, EaLength
);
85 EaInfo
->EaNameLength
= AFD_SOCKET_LENGTH
;
86 RtlCopyMemory(EaInfo
->EaName
,
89 EaInfo
->EaValueLength
= sizeof(AFD_SOCKET_INFORMATION
);
91 SocketInfo
= (PAFD_SOCKET_INFORMATION
)((ULONG_PTR
)EaInfo
->EaName
+ AFD_SOCKET_LENGTH
);
92 SocketInfo
->CommandChannel
= FALSE
;
93 SocketInfo
->AddressFamily
= AddressFamily
;
94 SocketInfo
->SocketType
= SocketType
;
95 SocketInfo
->Protocol
= Protocol
;
96 SocketInfo
->HelperContext
= HelperContext
;
97 SocketInfo
->NotificationEvents
= NotificationEvents
;
98 /* Zeroed above so initialized to a wildcard address if a raw socket */
99 SocketInfo
->Name
.sa_family
= AddressFamily
;
101 /* Store TDI device name last in buffer */
102 SocketInfo
->TdiDeviceName
.Buffer
= (PWCHAR
)(EaInfo
+ EaShort
);
103 SocketInfo
->TdiDeviceName
.MaximumLength
= TdiDeviceName
->Length
+ sizeof(WCHAR
);
104 RtlCopyUnicodeString(&SocketInfo
->TdiDeviceName
, TdiDeviceName
);
106 AFD_DbgPrint(MAX_TRACE
, ("EaInfo at (0x%X) EaLength is (%d).\n", (UINT
)EaInfo
, (INT
)EaLength
));
108 RtlRosInitUnicodeStringFromLiteral(&DeviceName
, L
"\\Device\\Afd");
109 InitializeObjectAttributes(
116 Status
= NtCreateFile(
118 FILE_GENERIC_READ
| FILE_GENERIC_WRITE
,
125 FILE_SYNCHRONOUS_IO_ALERT
,
129 HeapFree(GlobalHeap
, 0, EaInfo
);
131 if (!NT_SUCCESS(Status
)) {
132 AFD_DbgPrint(MIN_TRACE
, ("Error opening device (Status 0x%X).\n",
134 return STATUS_INSUFFICIENT_RESOURCES
;
137 *Socket
= (SOCKET
)FileHandle
;
139 return STATUS_SUCCESS
;
149 IN LPWSAPROTOCOL_INFOW lpProtocolInfo
,
154 * FUNCTION: Creates a new socket
156 * af = Address family
158 * protocol = Protocol type
159 * lpProtocolInfo = Pointer to protocol information
161 * dwFlags = Socket flags
162 * lpErrno = Address of buffer for error information
164 * Created socket, or INVALID_SOCKET if it could not be created
167 WSAPROTOCOL_INFOW ProtocolInfo
;
168 UNICODE_STRING TdiDeviceName
;
169 DWORD NotificationEvents
;
170 PWSHELPER_DLL HelperDLL
;
180 AFD_DbgPrint(MAX_TRACE
, ("af (%d) type (%d) protocol (%d).\n",
181 af
, type
, protocol
));
183 if (!lpProtocolInfo
) {
184 lpProtocolInfo
= &ProtocolInfo
;
185 ZeroMemory(&ProtocolInfo
, sizeof(WSAPROTOCOL_INFOW
));
187 ProtocolInfo
.iAddressFamily
= af
;
188 ProtocolInfo
.iSocketType
= type
;
189 ProtocolInfo
.iProtocol
= protocol
;
192 HelperDLL
= LocateHelperDLL(lpProtocolInfo
);
194 *lpErrno
= WSAEAFNOSUPPORT
;
195 return INVALID_SOCKET
;
198 AddressFamily
= lpProtocolInfo
->iAddressFamily
;
199 SocketType
= lpProtocolInfo
->iSocketType
;
200 Protocol
= lpProtocolInfo
->iProtocol
;
202 /* The OPTIONAL export WSHOpenSocket2 supersedes WSHOpenSocket */
203 if (HelperDLL
->EntryTable
.lpWSHOpenSocket2
)
205 Status
= HelperDLL
->EntryTable
.lpWSHOpenSocket2(
213 &NotificationEvents
);
217 Status
= HelperDLL
->EntryTable
.lpWSHOpenSocket(
223 &NotificationEvents
);
226 if (Status
!= NO_ERROR
) {
227 AFD_DbgPrint(MAX_TRACE
, ("WinSock Helper DLL failed (0x%X).\n", Status
));
229 return INVALID_SOCKET
;
232 NtStatus
= OpenSocket(&Socket
,
240 RtlFreeUnicodeString(&TdiDeviceName
);
241 if (!NT_SUCCESS(NtStatus
)) {
242 *lpErrno
= RtlNtStatusToDosError(Status
);
243 return INVALID_SOCKET
;
246 /* FIXME: Assumes catalog entry id to be 1 */
247 Socket2
= Upcalls
.lpWPUModifyIFSHandle(1, Socket
, lpErrno
);
249 if (Socket2
== INVALID_SOCKET
) {
251 AFD_DbgPrint(MIN_TRACE
, ("FIXME: Cleanup.\n"));
252 return INVALID_SOCKET
;
257 AFD_DbgPrint(MID_TRACE
, ("Returning socket descriptor (0x%X).\n", Socket2
));
269 * FUNCTION: Closes an open socket
271 * s = Socket descriptor
272 * lpErrno = Address of buffer for error information
274 * NO_ERROR, or SOCKET_ERROR if the socket could not be closed
279 AFD_DbgPrint(MAX_TRACE
, ("s (0x%X).\n", s
));
281 Status
= NtClose((HANDLE
)s
);
283 if (NT_SUCCESS(Status
)) {
288 *lpErrno
= WSAENOTSOCK
;
297 IN CONST LPSOCKADDR name
,
301 * FUNCTION: Associates a local address with a socket
303 * s = Socket descriptor
304 * name = Pointer to local address
305 * namelen = Length of name
306 * lpErrno = Address of buffer for error information
308 * 0, or SOCKET_ERROR if the socket could not be bound
311 FILE_REQUEST_BIND Request
;
312 FILE_REPLY_BIND Reply
;
313 IO_STATUS_BLOCK Iosb
;
316 AFD_DbgPrint(MAX_TRACE
, ("s (0x%X) name (0x%X) namelen (%d).\n", s
, name
, namelen
));
318 RtlCopyMemory(&Request
.Name
, name
, sizeof(SOCKADDR
));
320 Status
= NtDeviceIoControlFile(
328 sizeof(FILE_REQUEST_BIND
),
330 sizeof(FILE_REPLY_BIND
));
331 if (Status
== STATUS_PENDING
) {
332 AFD_DbgPrint(MAX_TRACE
, ("Waiting on transport.\n"));
333 /* FIXME: Wait only for blocking sockets */
334 Status
= NtWaitForSingleObject((HANDLE
)s
, FALSE
, NULL
);
337 if (!NT_SUCCESS(Status
)) {
338 *lpErrno
= Reply
.Status
;
352 * FUNCTION: Listens for incoming connections
354 * s = Socket descriptor
355 * backlog = Maximum number of pending connection requests
356 * lpErrno = Address of buffer for error information
358 * 0, or SOCKET_ERROR if the socket could not be bound
361 FILE_REQUEST_LISTEN Request
;
362 FILE_REPLY_LISTEN Reply
;
363 IO_STATUS_BLOCK Iosb
;
366 AFD_DbgPrint(MAX_TRACE
, ("s (0x%X) backlog (%d).\n", s
, backlog
));
368 Request
.Backlog
= backlog
;
370 Status
= NtDeviceIoControlFile(
378 sizeof(FILE_REQUEST_LISTEN
),
380 sizeof(FILE_REPLY_LISTEN
));
381 if (Status
== STATUS_PENDING
) {
382 AFD_DbgPrint(MAX_TRACE
, ("Waiting on transport.\n"));
383 /* FIXME: Wait only for blocking sockets */
384 Status
= NtWaitForSingleObject((HANDLE
)s
, FALSE
, NULL
);
387 if (!NT_SUCCESS(Status
)) {
388 *lpErrno
= Reply
.Status
;
400 IN OUT LPFD_SET readfds
,
401 IN OUT LPFD_SET writefds
,
402 IN OUT LPFD_SET exceptfds
,
403 IN CONST LPTIMEVAL timeout
,
406 * FUNCTION: Returns status of one or more sockets
408 * nfds = Always ignored
409 * readfds = Pointer to socket set to be checked for readability (optional)
410 * writefds = Pointer to socket set to be checked for writability (optional)
411 * exceptfds = Pointer to socket set to be checked for errors (optional)
412 * timeout = Pointer to a TIMEVAL structure indicating maximum wait time
413 * (NULL means wait forever)
414 * lpErrno = Address of buffer for error information
416 * Number of ready socket descriptors, or SOCKET_ERROR if an error ocurred
419 PFILE_REQUEST_SELECT Request
;
420 FILE_REPLY_SELECT Reply
;
421 IO_STATUS_BLOCK Iosb
;
429 AFD_DbgPrint(MAX_TRACE
, ("readfds (0x%X) writefds (0x%X) exceptfds (0x%X).\n",
430 readfds
, writefds
, exceptfds
));
432 /* FIXME: For now, all reads are timed out immediately */
433 if (readfds
!= NULL
) {
434 AFD_DbgPrint(MID_TRACE
, ("Timing out read query.\n"));
435 *lpErrno
= WSAETIMEDOUT
;
439 /* FIXME: For now, always allow write */
440 if (writefds
!= NULL
) {
441 AFD_DbgPrint(MID_TRACE
, ("Setting one socket writeable.\n"));
448 if ((readfds
!= NULL
) && (readfds
->fd_count
> 0)) {
449 ReadSize
= (readfds
->fd_count
* sizeof(SOCKET
)) + sizeof(UINT
);
453 if ((writefds
!= NULL
) && (writefds
->fd_count
> 0)) {
454 WriteSize
= (writefds
->fd_count
* sizeof(SOCKET
)) + sizeof(UINT
);
458 if ((exceptfds
!= NULL
) && (exceptfds
->fd_count
> 0)) {
459 ExceptSize
= (exceptfds
->fd_count
* sizeof(SOCKET
)) + sizeof(UINT
);
462 Size
= ReadSize
+ WriteSize
+ ExceptSize
;
464 Request
= (PFILE_REQUEST_SELECT
)HeapAlloc(
465 GlobalHeap
, 0, sizeof(FILE_REQUEST_SELECT
) + Size
);
467 *lpErrno
= WSAENOBUFS
;
471 /* Put FD SETs after request structure */
472 Current
= (Request
+ 1);
475 Request
->ReadFDSet
= (LPFD_SET
)Current
;
477 RtlCopyMemory(Request
->ReadFDSet
, readfds
, ReadSize
);
479 Request
->ReadFDSet
= NULL
;
483 Request
->WriteFDSet
= (LPFD_SET
)Current
;
484 Current
+= WriteSize
;
485 RtlCopyMemory(Request
->WriteFDSet
, writefds
, WriteSize
);
487 Request
->WriteFDSet
= NULL
;
490 if (ExceptSize
> 0) {
491 Request
->ExceptFDSet
= (LPFD_SET
)Current
;
492 RtlCopyMemory(Request
->ExceptFDSet
, exceptfds
, ExceptSize
);
494 Request
->ExceptFDSet
= NULL
;
497 AFD_DbgPrint(MAX_TRACE
, ("R1 (0x%X) W1 (0x%X).\n", Request
->ReadFDSet
, Request
->WriteFDSet
));
499 Status
= NtDeviceIoControlFile(
507 sizeof(FILE_REQUEST_SELECT
) + Size
,
509 sizeof(FILE_REPLY_SELECT
));
511 HeapFree(GlobalHeap
, 0, Request
);
513 if (Status
== STATUS_PENDING
) {
514 AFD_DbgPrint(MAX_TRACE
, ("Waiting on transport.\n"));
515 /* FIXME: Wait only for blocking sockets */
516 Status
= NtWaitForSingleObject(CommandChannel
, FALSE
, NULL
);
519 if (!NT_SUCCESS(Status
)) {
520 AFD_DbgPrint(MAX_TRACE
, ("Status (0x%X).\n", Status
));
521 *lpErrno
= WSAENOBUFS
;
525 AFD_DbgPrint(MAX_TRACE
, ("Select successful. Status (0x%X) Count (0x%X).\n",
526 Reply
.Status
, Reply
.SocketCount
));
528 *lpErrno
= Reply
.Status
;
530 return Reply
.SocketCount
;
538 IN OUT LPINT addrlen
,
539 IN LPCONDITIONPROC lpfnCondition
,
540 IN DWORD dwCallbackData
,
543 FILE_REQUEST_ACCEPT Request
;
544 FILE_REPLY_ACCEPT Reply
;
545 IO_STATUS_BLOCK Iosb
;
548 AFD_DbgPrint(MAX_TRACE
, ("s (0x%X).\n", s
));
551 Request
.addrlen
= *addrlen
;
552 Request
.lpfnCondition
= lpfnCondition
;
553 Request
.dwCallbackData
= dwCallbackData
;
555 Status
= NtDeviceIoControlFile(
563 sizeof(FILE_REQUEST_ACCEPT
),
565 sizeof(FILE_REPLY_ACCEPT
));
566 if (Status
== STATUS_PENDING
) {
567 AFD_DbgPrint(MAX_TRACE
, ("Waiting on transport.\n"));
568 /* FIXME: Wait only for blocking sockets */
569 Status
= NtWaitForSingleObject((HANDLE
)s
, FALSE
, NULL
);
572 if (!NT_SUCCESS(Status
)) {
573 *lpErrno
= Reply
.Status
;
574 return INVALID_SOCKET
;
577 *addrlen
= Reply
.addrlen
;
587 IN CONST LPSOCKADDR name
,
589 IN LPWSABUF lpCallerData
,
590 OUT LPWSABUF lpCalleeData
,
595 FILE_REQUEST_CONNECT Request
;
596 FILE_REPLY_CONNECT Reply
;
597 IO_STATUS_BLOCK Iosb
;
600 AFD_DbgPrint(MAX_TRACE
, ("s (0x%X).\n", s
));
603 Request
.namelen
= namelen
;
604 Request
.lpCallerData
= lpCallerData
;
605 Request
.lpCalleeData
= lpCalleeData
;
606 Request
.lpSQOS
= lpSQOS
;
607 Request
.lpGQOS
= lpGQOS
;
609 Status
= NtDeviceIoControlFile(
617 sizeof(FILE_REQUEST_CONNECT
),
619 sizeof(FILE_REPLY_CONNECT
));
620 if (Status
== STATUS_PENDING
) {
621 AFD_DbgPrint(MAX_TRACE
, ("Waiting on transport.\n"));
622 /* FIXME: Wait only for blocking sockets */
623 Status
= NtWaitForSingleObject((HANDLE
)s
, FALSE
, NULL
);
626 if (!NT_SUCCESS(Status
)) {
627 *lpErrno
= Reply
.Status
;
628 return INVALID_SOCKET
;
635 NTSTATUS
OpenCommandChannel(
638 * FUNCTION: Opens a command channel to afd.sys
642 * Status of operation
645 OBJECT_ATTRIBUTES ObjectAttributes
;
646 PAFD_SOCKET_INFORMATION SocketInfo
;
647 PFILE_FULL_EA_INFORMATION EaInfo
;
648 UNICODE_STRING DeviceName
;
649 IO_STATUS_BLOCK Iosb
;
655 AFD_DbgPrint(MAX_TRACE
, ("Called\n"));
657 EaShort
= sizeof(FILE_FULL_EA_INFORMATION
) +
659 sizeof(AFD_SOCKET_INFORMATION
);
663 EaInfo
= (PFILE_FULL_EA_INFORMATION
)HeapAlloc(GlobalHeap
, 0, EaLength
);
665 return STATUS_INSUFFICIENT_RESOURCES
;
668 RtlZeroMemory(EaInfo
, EaLength
);
669 EaInfo
->EaNameLength
= AFD_SOCKET_LENGTH
;
670 RtlCopyMemory(EaInfo
->EaName
,
673 EaInfo
->EaValueLength
= sizeof(AFD_SOCKET_INFORMATION
);
675 SocketInfo
= (PAFD_SOCKET_INFORMATION
)((ULONG_PTR
)EaInfo
->EaName
+ AFD_SOCKET_LENGTH
);
676 SocketInfo
->CommandChannel
= TRUE
;
678 RtlRosInitUnicodeStringFromLiteral(&DeviceName
, L
"\\Device\\Afd");
679 InitializeObjectAttributes(
686 Status
= NtCreateFile(
688 FILE_GENERIC_READ
| FILE_GENERIC_WRITE
,
695 FILE_SYNCHRONOUS_IO_ALERT
,
699 if (!NT_SUCCESS(Status
)) {
700 AFD_DbgPrint(MIN_TRACE
, ("Error opening device (Status 0x%X).\n",
705 CommandChannel
= FileHandle
;
707 return STATUS_SUCCESS
;
711 NTSTATUS
CloseCommandChannel(
714 * FUNCTION: Closes command channel to afd.sys
718 * Status of operation
721 AFD_DbgPrint(MAX_TRACE
, ("Called.\n"));
723 return NtClose(CommandChannel
);
730 IN WORD wVersionRequested
,
731 OUT LPWSPDATA lpWSPData
,
732 IN LPWSAPROTOCOL_INFOW lpProtocolInfo
,
733 IN WSPUPCALLTABLE UpcallTable
,
734 OUT LPWSPPROC_TABLE lpProcTable
)
736 * FUNCTION: Initialize service provider for a client
738 * wVersionRequested = Highest WinSock SPI version that the caller can use
739 * lpWSPData = Address of WSPDATA structure to initialize
740 * lpProtocolInfo = Pointer to structure that defines the desired protocol
741 * UpcallTable = Pointer to upcall table of the WinSock DLL
742 * lpProcTable = Address of procedure table to initialize
744 * Status of operation
750 AFD_DbgPrint(MAX_TRACE
, ("wVersionRequested (0x%X) \n", wVersionRequested
));
752 EnterCriticalSection(&InitCriticalSection
);
754 Upcalls
= UpcallTable
;
756 if (StartupCount
== 0) {
757 /* First time called */
759 Status
= OpenCommandChannel();
760 if (NT_SUCCESS(Status
)) {
761 hWS2_32
= GetModuleHandle(L
"ws2_32.dll");
762 if (hWS2_32
!= NULL
) {
763 lpWPUCompleteOverlappedRequest
= (LPWPUCOMPLETEOVERLAPPEDREQUEST
)
764 GetProcAddress(hWS2_32
, "WPUCompleteOverlappedRequest");
765 if (lpWPUCompleteOverlappedRequest
!= NULL
) {
770 AFD_DbgPrint(MIN_TRACE
, ("GetModuleHandle() failed for ws2_32.dll\n"));
773 AFD_DbgPrint(MIN_TRACE
, ("Cannot open afd.sys\n"));
780 LeaveCriticalSection(&InitCriticalSection
);
782 if (Status
== NO_ERROR
) {
783 lpProcTable
->lpWSPAccept
= WSPAccept
;
784 lpProcTable
->lpWSPAddressToString
= WSPAddressToString
;
785 lpProcTable
->lpWSPAsyncSelect
= WSPAsyncSelect
;
786 lpProcTable
->lpWSPBind
= WSPBind
;
787 lpProcTable
->lpWSPCancelBlockingCall
= WSPCancelBlockingCall
;
788 lpProcTable
->lpWSPCleanup
= WSPCleanup
;
789 lpProcTable
->lpWSPCloseSocket
= WSPCloseSocket
;
790 lpProcTable
->lpWSPConnect
= WSPConnect
;
791 lpProcTable
->lpWSPDuplicateSocket
= WSPDuplicateSocket
;
792 lpProcTable
->lpWSPEnumNetworkEvents
= WSPEnumNetworkEvents
;
793 lpProcTable
->lpWSPEventSelect
= WSPEventSelect
;
794 lpProcTable
->lpWSPGetOverlappedResult
= WSPGetOverlappedResult
;
795 lpProcTable
->lpWSPGetPeerName
= WSPGetPeerName
;
796 lpProcTable
->lpWSPGetSockName
= WSPGetSockName
;
797 lpProcTable
->lpWSPGetSockOpt
= WSPGetSockOpt
;
798 lpProcTable
->lpWSPGetQOSByName
= WSPGetQOSByName
;
799 lpProcTable
->lpWSPIoctl
= WSPIoctl
;
800 lpProcTable
->lpWSPJoinLeaf
= WSPJoinLeaf
;
801 lpProcTable
->lpWSPListen
= WSPListen
;
802 lpProcTable
->lpWSPRecv
= WSPRecv
;
803 lpProcTable
->lpWSPRecvDisconnect
= WSPRecvDisconnect
;
804 lpProcTable
->lpWSPRecvFrom
= WSPRecvFrom
;
805 lpProcTable
->lpWSPSelect
= WSPSelect
;
806 lpProcTable
->lpWSPSend
= WSPSend
;
807 lpProcTable
->lpWSPSendDisconnect
= WSPSendDisconnect
;
808 lpProcTable
->lpWSPSendTo
= WSPSendTo
;
809 lpProcTable
->lpWSPSetSockOpt
= WSPSetSockOpt
;
810 lpProcTable
->lpWSPShutdown
= WSPShutdown
;
811 lpProcTable
->lpWSPSocket
= WSPSocket
;
812 lpProcTable
->lpWSPStringToAddress
= WSPStringToAddress
;
814 lpWSPData
->wVersion
= MAKEWORD(2, 2);
815 lpWSPData
->wHighVersion
= MAKEWORD(2, 2);
818 AFD_DbgPrint(MAX_TRACE
, ("Status (%d).\n", Status
));
829 * FUNCTION: Cleans up service provider for a client
831 * lpErrno = Address of buffer for error information
833 * 0 if successful, or SOCKET_ERROR if not
836 AFD_DbgPrint(MAX_TRACE
, ("\n"));
838 EnterCriticalSection(&InitCriticalSection
);
840 if (StartupCount
> 0) {
843 if (StartupCount
== 0) {
844 AFD_DbgPrint(MAX_TRACE
, ("Cleaning up msafd.dll.\n"));
846 CloseCommandChannel();
850 LeaveCriticalSection(&InitCriticalSection
);
852 AFD_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
862 DllMain(HANDLE hInstDll
,
866 AFD_DbgPrint(MAX_TRACE
, ("DllMain of msafd.dll\n"));
869 case DLL_PROCESS_ATTACH
:
870 /* Don't need thread attach notifications
871 so disable them to improve performance */
872 DisableThreadLibraryCalls(hInstDll
);
874 InitializeCriticalSection(&InitCriticalSection
);
876 GlobalHeap
= GetProcessHeap();
878 CreateHelperDLLDatabase();
881 case DLL_THREAD_ATTACH
:
884 case DLL_THREAD_DETACH
:
887 case DLL_PROCESS_DETACH
:
889 DestroyHelperDLLDatabase();
891 DeleteCriticalSection(&InitCriticalSection
);
896 AFD_DbgPrint(MAX_TRACE
, ("DllMain of msafd.dll (leaving)\n"));