Don't open device if already open.
[wine] / dlls / crypt32 / encode.c
1 /*
2  * Copyright 2002 Mike McCormack for CodeWeavers
3  * Copyright 2005 Juan Lang
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wincrypt.h"
24 #include "winreg.h"
25 #include "snmp.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
29
30 static const WCHAR szDllName[] = { 'D','l','l',0 };
31
32 static char *CRYPT_GetKeyName(DWORD dwEncodingType, LPCSTR pszFuncName,
33  LPCSTR pszOID)
34 {
35     static const char szEncodingTypeFmt[] =
36      "Software\\Microsoft\\Cryptography\\OID\\EncodingType %ld\\%s\\%s";
37     UINT len;
38     char numericOID[7]; /* enough for "#65535" */
39     const char *oid;
40     LPSTR szKey;
41
42     /* MSDN says the encoding type is a mask, but it isn't treated that way.
43      * (E.g., if dwEncodingType were 3, the key names "EncodingType 1" and
44      * "EncodingType 2" would be expected if it were a mask.  Instead native
45      * stores values in "EncodingType 3".
46      */
47     if (!HIWORD(pszOID))
48     {
49         snprintf(numericOID, sizeof(numericOID), "#%d", (int)pszOID);
50         oid = numericOID;
51     }
52     else
53         oid = pszOID;
54
55     /* This is enough: the lengths of the two string parameters are explicitly
56      * counted, and we need up to five additional characters for the encoding
57      * type.  These are covered by the "%d", "%s", and "%s" characters in the
58      * format specifier that are removed by sprintf.
59      */
60     len = sizeof(szEncodingTypeFmt) + lstrlenA(pszFuncName) + lstrlenA(oid);
61     szKey = HeapAlloc(GetProcessHeap(), 0, len);
62     if (szKey)
63         sprintf(szKey, szEncodingTypeFmt, dwEncodingType, pszFuncName, oid);
64     return szKey;
65 }
66
67 BOOL WINAPI CryptRegisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName,
68                   LPCSTR pszOID, LPCWSTR pwszDll, LPCSTR pszOverrideFuncName)
69 {
70     LONG r;
71     HKEY hKey;
72     LPSTR szKey;
73
74     TRACE("%lx %s %s %s %s\n", dwEncodingType, pszFuncName, pszOID,
75           debugstr_w(pwszDll), pszOverrideFuncName);
76
77     /* This only registers functions for encoding certs, not messages */
78     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
79         return TRUE;
80
81     /* Native does nothing pwszDll is NULL */
82     if (!pwszDll)
83         return TRUE;
84
85     /* I'm not matching MS bug for bug here, because I doubt any app depends on
86      * it:
87      * - native "succeeds" if pszFuncName is NULL, but the nonsensical entry
88      *   it creates would never be used
89      * - native returns an HRESULT rather than a Win32 error if pszOID is NULL.
90      * Instead I disallow both of these with ERROR_INVALID_PARAMETER.
91      */
92     if (!pszFuncName || !pszOID)
93     {
94         SetLastError(ERROR_INVALID_PARAMETER);
95         return FALSE;
96     }
97
98     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
99     TRACE("Key name is %s\n", debugstr_a(szKey));
100
101     if (!szKey)
102         return FALSE;
103
104     r = RegCreateKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
105     HeapFree(GetProcessHeap(), 0, szKey);
106     if(r != ERROR_SUCCESS)
107         return FALSE;
108
109     /* write the values */
110     if (pszOverrideFuncName)
111         RegSetValueExA(hKey, "FuncName", 0, REG_SZ, pszOverrideFuncName,
112          lstrlenA(pszOverrideFuncName) + 1);
113     RegSetValueExW(hKey, szDllName, 0, REG_SZ, (const BYTE*) pwszDll,
114                     (lstrlenW(pwszDll) + 1) * sizeof (WCHAR));
115
116     RegCloseKey(hKey);
117     return TRUE;
118 }
119
120 BOOL WINAPI CryptUnregisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName,
121  LPCSTR pszOID)
122 {
123     LPSTR szKey;
124     LONG rc;
125
126     TRACE("%lx %s %s\n", dwEncodingType, pszFuncName, pszOID);
127
128     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
129         return TRUE;
130
131     if (!pszFuncName || !pszOID)
132     {
133         SetLastError(ERROR_INVALID_PARAMETER);
134         return FALSE;
135     }
136
137     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
138     rc = RegDeleteKeyA(HKEY_LOCAL_MACHINE, szKey);
139     HeapFree(GetProcessHeap(), 0, szKey);
140     if (rc)
141         SetLastError(rc);
142     return rc ? FALSE : TRUE;
143 }
144
145 BOOL WINAPI CryptGetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName,
146  LPCSTR pszOID, LPCWSTR pwszValueName, DWORD *pdwValueType, BYTE *pbValueData,
147  DWORD *pcbValueData)
148 {
149     LPSTR szKey;
150     LONG rc;
151     HKEY hKey;
152
153     TRACE("%lx %s %s %s %p %p %p\n", dwEncodingType, debugstr_a(pszFuncName),
154      debugstr_a(pszOID), debugstr_w(pwszValueName), pdwValueType, pbValueData,
155      pcbValueData);
156
157     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
158         return TRUE;
159
160     if (!pszFuncName || !pszOID || !pwszValueName)
161     {
162         SetLastError(ERROR_INVALID_PARAMETER);
163         return FALSE;
164     }
165
166     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
167     rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
168     HeapFree(GetProcessHeap(), 0, szKey);
169     if (rc)
170         SetLastError(rc);
171     else
172     {
173         rc = RegQueryValueExW(hKey, pwszValueName, NULL, pdwValueType,
174          pbValueData, pcbValueData);
175         if (rc)
176             SetLastError(rc);
177         RegCloseKey(hKey);
178     }
179     return rc ? FALSE : TRUE;
180 }
181
182 BOOL WINAPI CryptSetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName,
183  LPCSTR pszOID, LPCWSTR pwszValueName, DWORD dwValueType,
184  const BYTE *pbValueData, DWORD cbValueData)
185 {
186     LPSTR szKey;
187     LONG rc;
188     HKEY hKey;
189
190     TRACE("%lx %s %s %s %ld %p %ld\n", dwEncodingType, debugstr_a(pszFuncName),
191      debugstr_a(pszOID), debugstr_w(pwszValueName), dwValueType, pbValueData,
192      cbValueData);
193
194     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
195         return TRUE;
196
197     if (!pszFuncName || !pszOID || !pwszValueName)
198     {
199         SetLastError(ERROR_INVALID_PARAMETER);
200         return FALSE;
201     }
202
203     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
204     rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
205     HeapFree(GetProcessHeap(), 0, szKey);
206     if (rc)
207         SetLastError(rc);
208     else
209     {
210         rc = RegSetValueExW(hKey, pwszValueName, 0, dwValueType, pbValueData,
211          cbValueData);
212         if (rc)
213             SetLastError(rc);
214         RegCloseKey(hKey);
215     }
216     return rc ? FALSE : TRUE;
217 }
218
219 /* Gets the registered function named szFuncName for dwCertEncodingType and
220  * lpszStructType, or NULL if one could not be found.  *lib will be set to the
221  * handle of the module it's in, or NULL if no module was loaded.  If the
222  * return value is NULL, *lib will also be NULL, to simplify error handling.
223  */
224 static void *CRYPT_GetFunc(DWORD dwCertEncodingType, LPCSTR lpszStructType,
225  LPCSTR szFuncName, HMODULE *lib)
226 {
227     void *ret = NULL;
228     char *szKey = CRYPT_GetKeyName(dwCertEncodingType, szFuncName,
229      lpszStructType);
230     const char *funcName;
231     long r;
232     HKEY hKey;
233     DWORD type, size = 0;
234
235     *lib = NULL;
236     r = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
237     HeapFree(GetProcessHeap(), 0, szKey);
238     if(r != ERROR_SUCCESS)
239         return NULL;
240
241     RegQueryValueExA(hKey, "FuncName", NULL, &type, NULL, &size);
242     if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ)
243     {
244         funcName = HeapAlloc(GetProcessHeap(), 0, size);
245         RegQueryValueExA(hKey, "FuncName", NULL, &type, (LPBYTE)funcName,
246          &size);
247     }
248     else
249         funcName = szFuncName;
250     RegQueryValueExW(hKey, szDllName, NULL, &type, NULL, &size);
251     if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ)
252     {
253         LPWSTR dllName = HeapAlloc(GetProcessHeap(), 0, size);
254
255         RegQueryValueExW(hKey, szDllName, NULL, &type, (LPBYTE)dllName,
256          &size);
257         *lib = LoadLibraryW(dllName);
258         if (*lib)
259         {
260              ret = GetProcAddress(*lib, funcName);
261              if (!ret)
262              {
263                  /* Unload the library, the caller doesn't want to unload it
264                   * when the return value is NULL.
265                   */
266                  FreeLibrary(*lib);
267                  *lib = NULL;
268              }
269         }
270         HeapFree(GetProcessHeap(), 0, dllName);
271     }
272     if (funcName != szFuncName)
273         HeapFree(GetProcessHeap(), 0, (char *)funcName);
274     return ret;
275 }
276
277 typedef BOOL (WINAPI *CryptEncodeObjectFunc)(DWORD, LPCSTR, const void *,
278  BYTE *, DWORD *);
279
280 BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
281  const void *pvStructInfo, BYTE *pbEncoded, DWORD *pcbEncoded)
282 {
283     BOOL ret = FALSE;
284     HMODULE lib;
285     CryptEncodeObjectFunc pCryptEncodeObject;
286
287     TRACE("(0x%08lx, %s, %p, %p, %p)\n",
288      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
289      "(integer value)", pvStructInfo, pbEncoded, pcbEncoded);
290
291     if (!pbEncoded && !pcbEncoded)
292     {
293         SetLastError(ERROR_INVALID_PARAMETER);
294         return FALSE;
295     }
296
297     /* Try registered DLL first.. */
298     pCryptEncodeObject =
299      (CryptEncodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType,
300      lpszStructType, "CryptEncodeObject", &lib);
301     if (pCryptEncodeObject)
302     {
303         ret = pCryptEncodeObject(dwCertEncodingType, lpszStructType,
304          pvStructInfo, pbEncoded, pcbEncoded);
305         FreeLibrary(lib);
306     }
307     else
308     {
309         /* If not, use CryptEncodeObjectEx */
310         ret = CryptEncodeObjectEx(dwCertEncodingType, lpszStructType,
311          pvStructInfo, 0, NULL, pbEncoded, pcbEncoded);
312     }
313     return ret;
314 }
315
316 static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
317  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
318  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
319 {
320     INT val, i;
321     BYTE significantBytes, padByte = 0, bytesNeeded;
322     BOOL neg = FALSE, pad = FALSE;
323
324     if (!pvStructInfo)
325     {
326         SetLastError(ERROR_INVALID_PARAMETER);
327         return FALSE;
328     }
329
330     memcpy(&val, pvStructInfo, sizeof(val));
331     /* Count the number of significant bytes.  Temporarily swap sign for
332      * negatives so I count the minimum number of bytes.
333      */
334     if (val < 0)
335     {
336         neg = TRUE;
337         val = -val;
338     }
339     for (significantBytes = sizeof(val); !(val & 0xff000000);
340      val <<= 8, significantBytes--)
341         ;
342     if (neg)
343     {
344         val = -val;
345         if ((val & 0xff000000) < 0x80000000)
346         {
347             padByte = 0xff;
348             pad = TRUE;
349         }
350     }
351     else if ((val & 0xff000000) > 0x7f000000)
352     {
353         padByte = 0;
354         pad = TRUE;
355     }
356     bytesNeeded = 2 + significantBytes;
357     if (pad)
358         bytesNeeded++;
359     if (!pbEncoded || bytesNeeded > *pcbEncoded)
360     {
361         *pcbEncoded = bytesNeeded;
362         SetLastError(ERROR_MORE_DATA);
363         return FALSE;
364     }
365     if (!pbEncoded)
366     {
367         SetLastError(ERROR_INVALID_PARAMETER);
368         return FALSE;
369     }
370     if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
371     {
372         if (pEncodePara && pEncodePara->pfnAlloc)
373             *(BYTE **)pbEncoded = pEncodePara->pfnAlloc(bytesNeeded);
374         else
375             *(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded);
376         if (!*(BYTE **)pbEncoded)
377             return FALSE;
378         pbEncoded = *(BYTE **)pbEncoded;
379     }
380     *pbEncoded++ = ASN_INTEGER;
381     if (pad)
382     {
383         *pbEncoded++ = significantBytes + 1;
384         *pbEncoded++ = padByte;
385     }
386     else
387         *pbEncoded++ = significantBytes;
388     for (i = 0; i < significantBytes; i++, val <<= 8)
389         *(pbEncoded + i) = (BYTE)((val & 0xff000000) >> 24);
390     return TRUE;
391 }
392
393 typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *,
394  DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *);
395
396 BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
397  const void *pvStructInfo, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara,
398  BYTE *pbEncoded, DWORD *pcbEncoded)
399 {
400     BOOL ret = FALSE;
401     HMODULE lib = NULL;
402     CryptEncodeObjectExFunc encodeFunc = NULL;
403
404     TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p): semi-stub\n",
405      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
406      "(integer value)", pvStructInfo, dwFlags, pEncodePara, pbEncoded,
407      pcbEncoded);
408
409     if (!pbEncoded && !pcbEncoded)
410     {
411         SetLastError(ERROR_INVALID_PARAMETER);
412         return FALSE;
413     }
414
415     if (!HIWORD(lpszStructType))
416     {
417         if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING
418          || (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) ==
419          PKCS_7_ASN_ENCODING)
420         {
421             switch (LOWORD(lpszStructType))
422             {
423             case (WORD)X509_INTEGER:
424                 encodeFunc = CRYPT_AsnEncodeInt;
425                 break;
426             default:
427                 FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
428             }
429         }
430     }
431     if (!encodeFunc)
432         encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
433          lpszStructType, "CryptEncodeObjectEx", &lib);
434     if (encodeFunc)
435         ret = encodeFunc(dwCertEncodingType, lpszStructType, pvStructInfo,
436          dwFlags, pEncodePara, pbEncoded, pcbEncoded);
437     else
438         SetLastError(ERROR_FILE_NOT_FOUND);
439     if (lib)
440         FreeLibrary(lib);
441     return ret;
442 }
443
444 typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *,
445  DWORD, DWORD, void *, DWORD *);
446
447 BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
448  const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo,
449  DWORD *pcbStructInfo)
450 {
451     BOOL ret = FALSE;
452     HMODULE lib;
453     CryptDecodeObjectFunc pCryptDecodeObject;
454
455     TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p)\n",
456      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
457      "(integer value)", pbEncoded, cbEncoded, dwFlags, pvStructInfo,
458      pcbStructInfo);
459
460     if (!pvStructInfo && !pcbStructInfo)
461     {
462         SetLastError(ERROR_INVALID_PARAMETER);
463         return FALSE;
464     }
465
466     /* Try registered DLL first.. */
467     pCryptDecodeObject =
468      (CryptDecodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType,
469      lpszStructType, "CryptDecodeObject", &lib);
470     if (pCryptDecodeObject)
471     {
472         ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
473          pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo);
474         FreeLibrary(lib);
475     }
476     else
477     {
478         /* If not, use CryptDecodeObjectEx */
479         ret = CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, pbEncoded,
480          cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo);
481     }
482     return ret;
483 }
484
485 static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
486  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
487  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
488 {
489     int val, i;
490     BOOL ret;
491
492     if (!pbEncoded || !cbEncoded)
493     {
494         SetLastError(ERROR_INVALID_PARAMETER);
495         return FALSE;
496     }
497     if (!pvStructInfo)
498     {
499         *pcbStructInfo = sizeof(int);
500         SetLastError(ERROR_MORE_DATA);
501         return FALSE;
502     }
503     if (pbEncoded[0] != ASN_INTEGER)
504     {
505         SetLastError(CRYPT_E_ASN1_BADTAG);
506         return FALSE;
507     }
508     if (pbEncoded[1] == 0)
509     {
510         SetLastError(CRYPT_E_ASN1_CORRUPT);
511         return FALSE;
512     }
513     if (pbEncoded[1] > sizeof(int))
514     {
515         SetLastError(CRYPT_E_ASN1_LARGE);
516         return FALSE;
517     }
518     if (pbEncoded[2] & 0x80)
519     {
520         /* initialize to a negative value to sign-extend */
521         val = -1;
522     }
523     else
524         val = 0;
525     for (i = 0; i < pbEncoded[1]; i++)
526     {
527         val <<= 8;
528         val |= pbEncoded[2 + i];
529     }
530     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
531     {
532         if (pDecodePara && pDecodePara->pfnAlloc)
533             *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(sizeof(int));
534         else
535             *(BYTE **)pvStructInfo = LocalAlloc(0, sizeof(int));
536         if (!*(BYTE **)pvStructInfo)
537             return FALSE;
538         memcpy(*(BYTE **)pvStructInfo, &val, sizeof(int));
539         ret = TRUE;
540     }
541     else if (*pcbStructInfo < sizeof(int))
542     {
543         *pcbStructInfo = sizeof(int);
544         SetLastError(ERROR_MORE_DATA);
545         ret = FALSE;
546     }
547     else
548     {
549         *pcbStructInfo = sizeof(int);
550         memcpy(pvStructInfo, &val, sizeof(int));
551         ret = TRUE;
552     }
553     return ret;
554 }
555
556 typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *,
557  DWORD, DWORD, PCRYPT_DECODE_PARA, void *, DWORD *);
558
559 BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
560  const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
561  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
562 {
563     BOOL ret = FALSE;
564     HMODULE lib = NULL;
565     CryptDecodeObjectExFunc decodeFunc = NULL;
566
567     TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p): semi-stub\n",
568      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
569      "(integer value)", pbEncoded, cbEncoded, dwFlags, pDecodePara,
570      pvStructInfo, pcbStructInfo);
571
572     if (!pvStructInfo && !pcbStructInfo)
573     {
574         SetLastError(ERROR_INVALID_PARAMETER);
575         return FALSE;
576     }
577
578     if (!HIWORD(lpszStructType))
579     {
580         if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING
581          || (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) ==
582          PKCS_7_ASN_ENCODING)
583         {
584             switch (LOWORD(lpszStructType))
585             {
586             case (WORD)X509_INTEGER:
587                 decodeFunc = CRYPT_AsnDecodeInt;
588                 break;
589             default:
590                 FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
591             }
592         }
593     }
594     if (!decodeFunc)
595         decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
596          lpszStructType, "CryptDecodeObjectEx", &lib);
597     if (decodeFunc)
598         ret = decodeFunc(dwCertEncodingType, lpszStructType, pbEncoded,
599          cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
600     else
601         SetLastError(ERROR_FILE_NOT_FOUND);
602     if (lib)
603         FreeLibrary(lib);
604     return ret;
605 }