2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS WinSock 2 API
4 * FILE: dll/win32/ws2_32_new/src/addrinfo.c
5 * PURPOSE: Protocol-Independent Address Resolution
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES ******************************************************************/
18 /* DEFINES *******************************************************************/
20 #define Swap(a, b, c) { (c) = (a); (a) = (b); (b) = (c); }
21 #define FreeAddrInfoW(a) freeaddrinfo((LPADDRINFO)a)
23 /* FUNCTIONS *****************************************************************/
25 /* FIXME: put into dnslib */
28 Dns_Ip4AddressToReverseName_W(IN LPWSTR AddressBuffer
,
31 /* Convert the address into IPv4 format */
32 wsprintfW(AddressBuffer
, L
"%u.%u.%u.%u.in-addr.arpa.",
33 Address
.S_un
.S_un_b
.s_b4
,
34 Address
.S_un
.S_un_b
.s_b3
,
35 Address
.S_un
.S_un_b
.s_b2
,
36 Address
.S_un
.S_un_b
.s_b1
);
41 Dns_SplitHostFromDomainNameW(IN LPWSTR DomainName
)
49 ConvertAddrinfoFromUnicodeToAnsi(IN PADDRINFOW Addrinfo
)
54 /* Make sure we have a valid pointer */
60 UnicodeName
= &Addrinfo
->ai_canonname
;
62 /* Check if it exists */
66 AnsiName
= AnsiDupFromUnicode(*UnicodeName
);
69 /* Free the old one */
70 HeapFree(WsSockHeap
, 0, *UnicodeName
);
73 *UnicodeName
= (LPWSTR
)AnsiName
;
77 return GetLastError();
80 } while ((Addrinfo
= Addrinfo
->ai_next
));
90 ParseV4Address(IN PCWSTR AddressString
,
93 CHAR AnsiAddressString
[MAX_HOSTNAME_LEN
];
94 CHAR
* cp
= AnsiAddressString
;
97 DWORD parts
[4], *pp
= parts
;
100 WideCharToMultiByte(CP_ACP
,
105 sizeof(AnsiAddressString
),
108 if (!isdigit(*cp
)) return FALSE
;
112 * Collect number up to ``.''.
113 * Values are specified as for C:
114 * 0x=hex, 0=octal, other=decimal.
118 if (*++cp
== 'x' || *cp
== 'X')
125 val
= (val
* base
) + (c
- '0');
129 if (base
== 16 && isxdigit(c
)) {
130 val
= (val
<< 4) + (c
+ 10 - (islower(c
) ? 'a' : 'A'));
141 if (pp
>= parts
+ 4) return FALSE
;
147 * Check for trailing characters.
149 if (*cp
) return FALSE
;
153 * Concoct the address according to
154 * the number of parts specified.
156 if ((DWORD
)(pp
- parts
) != 4) return FALSE
;
157 if (parts
[0] > 0xff || parts
[1] > 0xff || parts
[2] > 0xff || parts
[3] > 0xff) return FALSE
;
158 val
= (parts
[0] << 24) | (parts
[1] << 16) | (parts
[2] << 8) | parts
[3];
161 *pAddress
= htonl(val
);
169 NewAddrInfo(IN INT SocketType
,
175 PSOCKADDR_IN SockAddress
;
177 /* Allocate a structure */
178 AddrInfo
= HeapAlloc(WsSockHeap
, 0, sizeof(ADDRINFOW
));
179 if (!AddrInfo
) return NULL
;
181 /* Allocate a sockaddr */
182 SockAddress
= HeapAlloc(WsSockHeap
, 0, sizeof(SOCKADDR_IN
));
185 /* Free the addrinfo and fail */
186 HeapFree(WsSockHeap
, 0, AddrInfo
);
190 /* Write data for socket address */
191 SockAddress
->sin_family
= AF_INET
;
192 SockAddress
->sin_port
= Port
;
193 SockAddress
->sin_addr
.s_addr
= Address
;
194 ZeroMemory(SockAddress
->sin_zero
, sizeof(SockAddress
->sin_zero
));
196 /* Fill out the addrinfo */
197 AddrInfo
->ai_family
= PF_INET
;
198 AddrInfo
->ai_socktype
= SocketType
;
199 AddrInfo
->ai_protocol
= Protocol
;
200 AddrInfo
->ai_flags
= 0;
201 AddrInfo
->ai_next
= 0;
202 AddrInfo
->ai_canonname
= NULL
;
203 AddrInfo
->ai_addrlen
= sizeof(SOCKADDR_IN
);
204 AddrInfo
->ai_addr
= (PSOCKADDR
)SockAddress
;
213 CloneAddrInfo(IN WORD Port
,
214 IN PADDRINFOW ptResult
)
216 PADDRINFOW Next
= NULL
;
217 PADDRINFOW New
= NULL
;
220 for (Next
= ptResult
; Next
;)
222 /* Create a new structure */
223 New
= NewAddrInfo(SOCK_DGRAM
,
226 ((PSOCKADDR_IN
)Next
->ai_addr
)->sin_addr
.s_addr
);
230 New
->ai_next
= Next
->ai_next
;
235 /* Check if we ran out of memory */
236 if (Next
) return EAI_MEMORY
;
245 QueryDNS(IN LPCSTR NodeName
,
249 OUT CHAR Alias
[NI_MAXHOST
],
250 OUT PADDRINFOW
*pptResult
)
252 PADDRINFOW
*Next
= pptResult
;
253 PHOSTENT Hostent
= NULL
;
256 /* Assume nothing found */
260 /* Get the hostent */
261 Hostent
= gethostbyname(NodeName
);
264 /* Check for valid addresses */
265 if ((Hostent
->h_addrtype
== AF_INET
) &&
266 (Hostent
->h_length
== sizeof(IN_ADDR
)))
268 /* Loop every address in it */
269 for (Addresses
= Hostent
->h_addr_list
; *Addresses
; Addresses
++)
271 /* Create an addrinfo structure for it*/
272 *Next
= NewAddrInfo(SocketType
,
275 ((PIN_ADDR
)*Addresses
)->s_addr
);
276 if (!*Next
) return EAI_MEMORY
;
278 /* Move to the next entry */
279 Next
= &((*Next
)->ai_next
);
283 /* Copy the canonical name */
284 strcpy(Alias
, Hostent
->h_name
);
290 /* Find out what the error was */
291 switch (GetLastError())
293 /* Convert the Winsock Error to an EAI error */
294 case WSAHOST_NOT_FOUND
: return EAI_NONAME
;
295 case WSATRY_AGAIN
: return EAI_AGAIN
;
296 case WSANO_RECOVERY
: return EAI_FAIL
;
297 case WSANO_DATA
: return EAI_NODATA
;
298 default: return EAI_NONAME
;
305 LookupNodeByAddr(IN LPWSTR pNodeBuffer
,
306 IN DWORD NodeBufferSize
,
307 IN BOOLEAN OnlyNodeName
,
310 IN INT AddressFamily
)
312 GUID LookupGuid
= SVCID_DNS_TYPE_PTR
;
313 PIN_ADDR Ip4Addr
= Addr
;
314 WCHAR ReverseBuffer
[76];
315 WSAQUERYSETW Restrictions
, Reply
;
320 /* Validate the address */
321 if (!Addr
) return WSAEFAULT
;
323 /* Make sure the family and address size match */
324 if (AddressFamily
== AF_INET6
)
326 /* Check the address size for this type */
327 if (AddrSize
!= sizeof(IN6_ADDR
)) return WSAEFAULT
;
328 Ip4Addr
= (PIN_ADDR
)&((PIN6_ADDR
)Addr
)->u
.Byte
[12];
330 else if (AddressFamily
== AF_INET
)
332 /* Check the address size for this type */
333 if (AddrSize
!= sizeof(IN_ADDR
)) return WSAEFAULT
;
337 /* Address family not supported */
338 return WSAEAFNOSUPPORT
;
341 /* Check if this is a mapped V4 IPv6 or pure IPv4 */
342 if (((AddressFamily
== AF_INET6
) && (IN6_IS_ADDR_V4MAPPED(Addr
))) ||
343 (AddressFamily
== AF_INET
))
345 /* Get the reverse name */
346 Dns_Ip4AddressToReverseName_W(ReverseBuffer
, *Ip4Addr
);
348 /* FIXME: Not implemented for now
351 /* By this point we have the Reverse Name, so prepare for lookup */
352 RtlZeroMemory(&Restrictions
, sizeof(Restrictions
));
353 Restrictions
.dwSize
= sizeof(Restrictions
);
354 Restrictions
.lpszServiceInstanceName
= ReverseBuffer
;
355 Restrictions
.lpServiceClassId
= &LookupGuid
;
356 Restrictions
.dwNameSpace
= NS_DNS
;
358 /* Now do the lookup */
359 ErrorCode
= WSALookupServiceBeginW(&Restrictions
,
362 if (ErrorCode
== ERROR_SUCCESS
)
364 /* Lookup successful, now get the data */
365 BufferLength
= (NI_MAXHOST
- 1) * sizeof(WCHAR
) + sizeof(Restrictions
);
366 ErrorCode
= WSALookupServiceNextW(LookupHandle
,
370 if (ErrorCode
== ERROR_SUCCESS
)
372 /* Now check if we have a name back */
373 Reply
= Restrictions
;
374 if (!Reply
.lpszServiceInstanceName
)
377 ErrorCode
= WSAHOST_NOT_FOUND
;
381 /* Check if the caller only wants the node name */
384 /* Split it and get only the partial host name */
385 Dns_SplitHostFromDomainNameW(Reply
.lpszServiceInstanceName
);
388 /* Check the length and see if it's within our limit */
389 if (wcslen(Reply
.lpszServiceInstanceName
) + 1 >
392 /* It's not, so fail */
393 ErrorCode
= WSAEFAULT
;
397 /* It will fit, let's copy it*/
398 wcscpy(pNodeBuffer
, Reply
.lpszServiceInstanceName
);
403 else if (ErrorCode
== WSASERVICE_NOT_FOUND
)
405 /* Normalize the error code */
406 ErrorCode
= WSAHOST_NOT_FOUND
;
409 /* Finish the lookup if one was in progress */
410 if (LookupHandle
) WSALookupServiceEnd(LookupHandle
);
412 /* Return the error code */
419 GetServiceNameForPort(IN LPWSTR pServiceBuffer
,
420 IN DWORD ServiceBufferSize
,
424 return ERROR_SUCCESS
;
430 LookupAddressForName(IN LPCSTR NodeName
,
434 IN BOOL bAI_CANONNAME
,
435 OUT PADDRINFOW
*pptResult
)
439 CHAR szFQDN1
[NI_MAXHOST
] = "";
440 CHAR szFQDN2
[NI_MAXHOST
] = "";
441 PCHAR Name
= szFQDN1
;
442 PCHAR Alias
= szFQDN2
;
443 PCHAR Scratch
= NULL
;
445 /* Make a copy of the name */
446 strcpy(Name
, NodeName
);
451 /* Do a DNS Query for the name */
452 iError
= QueryDNS(NodeName
,
460 /* Exit if we have a result */
461 if (*pptResult
) break;
463 /* Don't loop continuously if this is a DNS misconfiguration */
464 if ((!strlen(Alias
)) || (!strcmp(Name
, Alias
)) || (++AliasCount
== 16))
466 /* Exit the loop with a failure */
471 /* Restart loopup if we got a CNAME */
472 Swap(Name
, Alias
, Scratch
);
475 /* Check if we succeeded and the canonical name is requested */
476 if (!iError
&& bAI_CANONNAME
)
478 /* Allocate memory for a copy */
479 (*pptResult
)->ai_canonname
= HeapAlloc(WsSockHeap
, 0, 512);
481 /* Check if we had enough memory */
482 if (!(*pptResult
)->ai_canonname
)
484 /* Set failure code */
489 /* Convert the alias to UNICODE */
490 MultiByteToWideChar(CP_ACP
,
494 (*pptResult
)->ai_canonname
,
499 /* Return to caller */
508 GetAddrInfoW(IN PCWSTR pszNodeName
,
509 IN PCWSTR pszServiceName
,
510 IN
const ADDRINFOW
*ptHints
,
511 OUT PADDRINFOW
*pptResult
)
515 INT iFamily
= PF_UNSPEC
;
520 PSERVENT ptService
= NULL
;
525 WCHAR CanonicalName
[0x42];
526 CHAR AnsiServiceName
[256];
527 CHAR AnsiNodeName
[256];
528 DPRINT("GetAddrInfoW: %S, %S, %p, %p\n", pszNodeName
, pszServiceName
, ptHints
, pptResult
);
533 /* We must have at least one name to work with */
534 if (!(pszNodeName
) && !(pszServiceName
))
537 SetLastError(EAI_NONAME
);
541 /* Check if we got hints */
544 /* Make sure these are empty */
545 if ((ptHints
->ai_addrlen
) ||
546 (ptHints
->ai_canonname
) ||
547 (ptHints
->ai_addr
) ||
550 /* Fail if they aren't */
551 SetLastError(EAI_FAIL
);
555 /* Save the flags and validate them */
556 iFlags
= ptHints
->ai_flags
;
557 if ((iFlags
& AI_CANONNAME
) && !pszNodeName
)
559 SetLastError(EAI_BADFLAGS
);
563 /* Save family and validate it */
564 iFamily
= ptHints
->ai_family
;
565 if ((iFamily
!= PF_UNSPEC
) && (iFamily
!= PF_INET
))
567 SetLastError(EAI_FAMILY
);
571 /* Save socket type and validate it */
572 iSocketType
= ptHints
->ai_socktype
;
573 if ((iSocketType
!= 0) &&
574 (iSocketType
!= SOCK_STREAM
) &&
575 (iSocketType
!= SOCK_DGRAM
) &&
576 (iSocketType
!= SOCK_RAW
))
578 SetLastError(EAI_SOCKTYPE
);
582 /* Save the protocol */
583 iProtocol
= ptHints
->ai_protocol
;
586 /* Check if we have a service name */
589 /* We need to convert it to ANSI */
590 WideCharToMultiByte(CP_ACP
,
595 sizeof(AnsiServiceName
),
600 wPort
= (WORD
)strtoul(AnsiServiceName
, &pc
, 10);
602 /* Check if the port string is numeric */
605 /* Get the port directly */
606 wPort
= wTcpPort
= wUdpPort
= htons(wPort
);
609 /* Check if this is both TCP and UDP */
610 if (iSocketType
== 0)
612 /* Set it to TCP for now, but remember to clone */
614 iSocketType
= SOCK_STREAM
;
621 /* The port name was a string. Check if this is a UDP socket */
622 if ((iSocketType
== 0) || (iSocketType
== SOCK_DGRAM
))
624 /* It's UDP, do a getservbyname */
625 ptService
= getservbyname(AnsiServiceName
, "udp");
627 /* If we got a servent, return the port from it */
628 if (ptService
) wPort
= wUdpPort
= ptService
->s_port
;
631 /* Check if this is a TCP socket */
632 if ((iSocketType
== 0) || (iSocketType
== SOCK_STREAM
))
634 /* It's TCP, do a getserbyname */
635 ptService
= getservbyname(AnsiServiceName
, "tcp");
637 /* Return the port from the servent */
638 if (ptService
) wPort
= wTcpPort
= ptService
->s_port
;
641 /* If we got 0, then fail */
644 SetLastError(EAI_SERVICE
);
648 /* Check if this was for both */
649 if (iSocketType
== 0)
651 /* Do the TCP case right now */
652 if (wTcpPort
&& !wUdpPort
)
653 iSocketType
= SOCK_STREAM
;
654 if (!wTcpPort
&& wUdpPort
)
655 iSocketType
= SOCK_DGRAM
;
656 //bClone = (wTcpPort && wUdpPort);
661 /* Check if no node was given or if this is is a valid IPv4 address */
662 if ((!pszNodeName
) || (ParseV4Address(pszNodeName
, &dwAddress
)))
664 /* Check if we don't have a node name */
667 /* Make one up based on the flags */
668 dwAddress
= htonl((iFlags
& AI_PASSIVE
) ?
669 INADDR_ANY
: INADDR_LOOPBACK
);
672 /* Create the Addr Info */
673 *pptResult
= NewAddrInfo(iSocketType
, iProtocol
, wPort
, dwAddress
);
675 /* If we didn't get one back, assume out of memory */
676 if (!(*pptResult
)) iError
= EAI_MEMORY
;
678 /* Check if we have success and a nodename */
679 if (!iError
&& pszNodeName
)
681 /* Set AI_NUMERICHOST since this is a numeric string */
682 (*pptResult
)->ai_flags
|= AI_NUMERICHOST
;
684 /* Check if the canonical name was requested */
685 if (iFlags
& AI_CANONNAME
)
687 /* Get the canonical name */
688 GetNameInfoW((*pptResult
)->ai_addr
,
689 (socklen_t
)(*pptResult
)->ai_addrlen
,
696 /* Allocate memory for a copy */
697 (*pptResult
)->ai_canonname
= HeapAlloc(WsSockHeap
,
699 wcslen(CanonicalName
));
701 if (!(*pptResult
)->ai_canonname
)
703 /* No memory for the copy */
708 /* Duplicate the string */
709 RtlMoveMemory((*pptResult
)->ai_canonname
,
711 wcslen(CanonicalName
));
716 else if (iFlags
& AI_NUMERICHOST
)
718 /* No name for this request (we have a non-numeric name) */
723 /* We need to convert it to ANSI */
724 WideCharToMultiByte(CP_ACP
,
729 sizeof(AnsiNodeName
),
733 /* Non-numeric name, do DNS lookup */
734 iError
= LookupAddressForName(AnsiNodeName
,
738 (iFlags
& AI_CANONNAME
),
742 /* If all was good and the caller requested UDP and TCP */
743 if (!iError
&& bClone
)
745 /* Process UDP now, we already did TCP */
746 iError
= CloneAddrInfo(wUdpPort
, *pptResult
);
749 /* If we've hit an error till here */
752 /* Free the address info and return nothing */
753 FreeAddrInfoW(*pptResult
);
757 /* Return to caller */
758 SetLastError(iError
);
768 freeaddrinfo(PADDRINFOA AddrInfo
)
772 /* Loop the chain of structures */
773 for (NextInfo
= AddrInfo
; NextInfo
; NextInfo
= AddrInfo
)
775 /* Check if there is a canonical name */
776 if (NextInfo
->ai_canonname
)
779 HeapFree(WsSockHeap
, 0, NextInfo
->ai_canonname
);
782 /* Check if there is an address */
783 if (NextInfo
->ai_addr
)
786 HeapFree(WsSockHeap
, 0, NextInfo
->ai_addr
);
789 /* Move to the next entry */
790 AddrInfo
= NextInfo
->ai_next
;
792 /* Free this entry */
793 HeapFree(WsSockHeap
, 0, NextInfo
);
803 getaddrinfo(const char FAR
*nodename
,
804 const char FAR
*servname
,
805 const struct addrinfo FAR
*hints
,
806 struct addrinfo FAR
* FAR
*res
)
809 LPWSTR UnicodeNodeName
= NULL
;
810 LPWSTR UnicodeServName
= NULL
;
811 DPRINT("getaddrinfo: %s, %s, %p, %p\n", nodename
, servname
, hints
, res
);
813 /* Check for WSAStartup */
814 if ((ErrorCode
= WsQuickProlog()) != ERROR_SUCCESS
) return ErrorCode
;
816 /* Convert the node name */
819 UnicodeNodeName
= UnicodeDupFromAnsi(nodename
);
820 if (!UnicodeNodeName
)
822 /* Prepare to fail */
823 ErrorCode
= GetLastError();
828 /* Convert the servname too, if we have one */
831 UnicodeServName
= UnicodeDupFromAnsi(servname
);
832 if (!UnicodeServName
)
834 /* Prepare to fail */
835 ErrorCode
= GetLastError();
840 /* Now call the unicode function */
841 ErrorCode
= GetAddrInfoW(UnicodeNodeName
,
846 /* Convert it to ANSI if we succeeded */
847 if (ErrorCode
== ERROR_SUCCESS
) ConvertAddrinfoFromUnicodeToAnsi((PADDRINFOW
)*res
);
850 /* Check if we have a unicode node name and serv name */
851 if (UnicodeNodeName
) HeapFree(WsSockHeap
, 0, UnicodeNodeName
);
852 if (UnicodeServName
) HeapFree(WsSockHeap
, 0, UnicodeServName
);
854 /* Check if we are in error */
855 if (ErrorCode
!= ERROR_SUCCESS
)
857 /* Free the structure and return nothing */
862 /* Set the last error and return */
863 SetLastError(ErrorCode
);
872 GetNameInfoW(IN CONST SOCKADDR
*pSockaddr
,
873 IN socklen_t SockaddrLength
,
874 OUT PWCHAR pNodeBuffer
,
875 IN DWORD NodeBufferSize
,
876 OUT PWCHAR pServiceBuffer
,
877 IN DWORD ServiceBufferSize
,
880 DWORD AddressLength
, AddrSize
;
882 SOCKADDR_IN6 Address
;
883 INT ErrorCode
= ERROR_SUCCESS
;
885 DPRINT("GetNameInfoW: %p, %d, %p, %ld, %p, %ld, %d\n",
894 /* Check for valid socket */
898 /* Check which family this is */
899 if (pSockaddr
->sa_family
== AF_INET
)
902 AddressLength
= sizeof(SOCKADDR_IN
);
903 Addr
= &((PSOCKADDR_IN
)pSockaddr
)->sin_addr
;
904 AddrSize
= sizeof(IN_ADDR
);
906 else if (pSockaddr
->sa_family
== AF_INET6
)
909 AddressLength
= sizeof(SOCKADDR_IN6
);
910 Addr
= &((PSOCKADDR_IN6
)pSockaddr
)->sin6_addr
;
911 AddrSize
= sizeof(IN6_ADDR
);
915 /* Unsupported family */
916 SetLastError(EAI_FAMILY
);
920 /* Check for valid socket address length */
921 if ((DWORD
)SockaddrLength
< AddressLength
)
924 /* Check if we have a node name */
927 /* Check if only the numeric host is wanted */
928 if (!(Flags
& NI_NUMERICHOST
))
930 /* Do the lookup by addr */
931 ErrorCode
= LookupNodeByAddr(pNodeBuffer
,
936 pSockaddr
->sa_family
);
937 /* Check if we failed */
938 if (ErrorCode
!= ERROR_SUCCESS
)
940 /* Fail if the caller REALLY wants the NAME itself? */
941 if (Flags
& NI_NAMEREQD
) goto quickie
;
945 /* We succeeded, no need to get the numeric address */
950 /* Copy the address */
951 RtlMoveMemory(&Address
, pSockaddr
, AddressLength
);
953 /* Get the numeric address */
954 if (pSockaddr
->sa_family
== AF_INET
)
957 ((PSOCKADDR_IN
)&Address
)->sin_port
= 0;
959 else if (pSockaddr
->sa_family
== AF_INET6
)
962 ((PSOCKADDR_IN6
)&Address
)->sin6_port
= 0;
964 ErrorCode
= WSAAddressToStringW((LPSOCKADDR
)&Address
,
969 if (ErrorCode
== SOCKET_ERROR
)
971 /* Get the error code and exit with it */
972 ErrorCode
= GetLastError();
978 /* Check if we got a service name */
981 /* Handle this request */
982 ErrorCode
= GetServiceNameForPort(pServiceBuffer
,
984 ((PSOCKADDR_IN
)pSockaddr
)->sin_port
,
988 /* Set the error and return it (or success) */
990 SetLastError(ErrorCode
);
1000 getnameinfo(const struct sockaddr FAR
*sa
,
1010 WCHAR ServiceBuffer
[17];
1011 DWORD HostLength
= 0, ServLength
= 0;
1012 PWCHAR ServiceString
= NULL
, HostString
= NULL
;
1013 DPRINT("getnameinfo: %p, %p, %p, %lx\n", host
, serv
, sa
, salen
);
1015 /* Check for WSAStartup */
1016 if ((ErrorCode
= WsQuickProlog()) != ERROR_SUCCESS
) return ErrorCode
;
1018 /* Check if we have a host pointer */
1021 /* Setup the data for it */
1022 HostString
= Buffer
;
1023 HostLength
= sizeof(Buffer
) / sizeof(WCHAR
);
1026 /* Check if we have a service pointer */
1029 /* Setup the data for it */
1030 ServiceString
= ServiceBuffer
;
1031 ServLength
= sizeof(ServiceBuffer
) / sizeof(WCHAR
);
1034 /* Now call the unicode function */
1035 ErrorCode
= GetNameInfoW(sa
,
1043 /* Check for success */
1044 if (ErrorCode
== ERROR_SUCCESS
)
1046 /* Check if we had a host pointer */
1049 /* Convert it back to ANSI */
1050 ErrorCode
= WideCharToMultiByte(CP_ACP
,
1058 if (!ErrorCode
) goto Quickie
;
1061 /* Check if we have a service pointer */
1064 /* Convert it back to ANSI */
1065 ErrorCode
= WideCharToMultiByte(CP_ACP
,
1073 if (!ErrorCode
) goto Quickie
;
1076 /* Return success */
1077 return ERROR_SUCCESS
;
1080 /* Set the last error and return */
1082 SetLastError(ErrorCode
);