xref: /reactos/dll/win32/odbccp32/odbccp32.c (revision e08ae510)
1 /*
2  * Implementation of the ODBC driver installer
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Hans Leidekker
6  * Copyright 2007 Bill Medland
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <assert.h>
24 #include <stdarg.h>
25 
26 #define COBJMACROS
27 
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winnls.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34 #include "wine/heap.h"
35 
36 #include "odbcinst.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(odbc);
39 
40 /* Registry key names */
41 static const WCHAR drivers_key[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C','\\','O','D','B','C','I','N','S','T','.','I','N','I','\\','O','D','B','C',' ','D','r','i','v','e','r','s',0};
42 static const WCHAR odbcW[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C',0};
43 static const WCHAR odbcini[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C','\\','O','D','B','C','I','N','S','T','.','I','N','I','\\',0};
44 static const WCHAR odbcdrivers[] = {'O','D','B','C',' ','D','r','i','v','e','r','s',0};
45 static const WCHAR odbctranslators[] = {'O','D','B','C',' ','T','r','a','n','s','l','a','t','o','r','s',0};
46 
47 /* This config mode is known to be process-wide.
48  * MSDN documentation suggests that the value is hidden somewhere in the registry but I haven't found it yet.
49  * Although both the registry and the ODBC.ini files appear to be maintained together they are not maintained automatically through the registry's IniFileMapping.
50  */
51 static UWORD config_mode = ODBC_BOTH_DSN;
52 
53 /* MSDN documentation suggests that the error subsystem handles errors 1 to 8
54  * only and experimentation (Windows 2000) shows that the errors are process-
55  * wide so go for the simple solution; static arrays.
56  */
57 static int num_errors;
58 static int error_code[8];
59 static const WCHAR *error_msg[8];
60 static const WCHAR odbc_error_general_err[] = {'G','e','n','e','r','a','l',' ','e','r','r','o','r',0};
61 static const WCHAR odbc_error_invalid_buff_len[] = {'I','n','v','a','l','i','d',' ','b','u','f','f','e','r',' ','l','e','n','g','t','h',0};
62 static const WCHAR odbc_error_component_not_found[] = {'C','o','m','p','o','n','e','n','t',' ','n','o','t',' ','f','o','u','n','d',0};
63 static const WCHAR odbc_error_out_of_mem[] = {'O','u','t',' ','o','f',' ','m','e','m','o','r','y',0};
64 static const WCHAR odbc_error_invalid_param_sequence[] = {'I','n','v','a','l','i','d',' ','p','a','r','a','m','e','t','e','r',' ','s','e','q','u','e','n','c','e',0};
65 static const WCHAR odbc_error_invalid_param_string[] = {'I','n','v','a','l','i','d',' ','p','a','r','a','m','e','t','e','r',' ','s','t','r','i','n','g',0};
66 static const WCHAR odbc_error_invalid_dsn[] = {'I','n','v','a','l','i','d',' ','D','S','N',0};
67 static const WCHAR odbc_error_load_lib_failed[] = {'L','o','a','d',' ','L','i','b','r','a','r','y',' ','F','a','i','l','e','d',0};
68 static const WCHAR odbc_error_request_failed[] = {'R','e','q','u','e','s','t',' ','F','a','i','l','e','d',0};
69 static const WCHAR odbc_error_invalid_keyword[] = {'I','n','v','a','l','i','d',' ','k','e','y','w','o','r','d',' ','v','a','l','u','e',0};
70 
71 /* Push an error onto the error stack, taking care of ranges etc. */
72 static void push_error(int code, LPCWSTR msg)
73 {
74     if (num_errors < sizeof error_code/sizeof error_code[0])
75     {
76         error_code[num_errors] = code;
77         error_msg[num_errors] = msg;
78         num_errors++;
79     }
80 }
81 
82 /* Clear the error stack */
83 static void clear_errors(void)
84 {
85     num_errors = 0;
86 }
87 
88 static inline WCHAR *heap_strdupAtoW(const char *str)
89 {
90     LPWSTR ret = NULL;
91 
92     if(str) {
93         DWORD len;
94 
95         len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
96         ret = heap_alloc(len*sizeof(WCHAR));
97         if(ret)
98             MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
99     }
100 
101     return ret;
102 }
103 
104 
105 BOOL WINAPI ODBCCPlApplet( LONG i, LONG j, LONG * p1, LONG * p2)
106 {
107     clear_errors();
108     FIXME( "( %d %d %p %p) : stub!\n", i, j, p1, p2);
109     return FALSE;
110 }
111 
112 static LPWSTR SQLInstall_strdup_multi(LPCSTR str)
113 {
114     LPCSTR p;
115     LPWSTR ret = NULL;
116     DWORD len;
117 
118     if (!str)
119         return ret;
120 
121     for (p = str; *p; p += lstrlenA(p) + 1)
122         ;
123 
124     len = MultiByteToWideChar(CP_ACP, 0, str, p - str, NULL, 0 );
125     ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
126     MultiByteToWideChar(CP_ACP, 0, str, p - str, ret, len );
127     ret[len] = 0;
128 
129     return ret;
130 }
131 
132 static LPWSTR SQLInstall_strdup(LPCSTR str)
133 {
134     DWORD len;
135     LPWSTR ret = NULL;
136 
137     if (!str)
138         return ret;
139 
140     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0 );
141     ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
142     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len );
143 
144     return ret;
145 }
146 
147 /* Convert the wide string or zero-length-terminated list of wide strings to a
148  * narrow string or zero-length-terminated list of narrow strings.
149  * Do not try to emulate windows undocumented excesses (e.g. adding a third \0
150  * to a list)
151  * Arguments
152  *   mode Indicates the sort of string.
153  *     1 denotes that the buffers contain strings terminated by a single nul
154  *       character
155  *     2 denotes that the buffers contain zero-length-terminated lists
156  *       (frequently erroneously referred to as double-null-terminated)
157  *   buffer The narrow-character buffer into which to place the result.  This
158  *          must be a non-null pointer to the first element of a buffer whose
159  *          length is passed in buffer_length.
160  *   str The wide-character buffer containing the string or list of strings to
161  *       be converted.  str_length defines how many wide characters in the
162  *       buffer are to be converted, including all desired terminating nul
163  *       characters.
164  *   str_length Effective length of str
165  *   buffer_length Length of buffer
166  *   returned_length A pointer to a variable that will receive the number of
167  *                   narrow characters placed into the buffer.  This pointer
168  *                   may be NULL.
169  */
170 static BOOL SQLInstall_narrow(int mode, LPSTR buffer, LPCWSTR str, WORD str_length, WORD buffer_length, WORD *returned_length)
171 {
172     LPSTR pbuf; /* allows us to allocate a temporary buffer only if needed */
173     int len; /* Length of the converted list */
174     BOOL success = FALSE;
175     assert(mode == 1 || mode == 2);
176     assert(buffer_length);
177     len = WideCharToMultiByte(CP_ACP, 0, str, str_length, 0, 0, NULL, NULL);
178     if (len > 0)
179     {
180         if (len > buffer_length)
181         {
182             pbuf = HeapAlloc(GetProcessHeap(), 0, len);
183         }
184         else
185         {
186             pbuf = buffer;
187         }
188         len = WideCharToMultiByte(CP_ACP, 0, str, str_length, pbuf, len, NULL, NULL);
189         if (len > 0)
190         {
191             if (pbuf != buffer)
192             {
193                 if (buffer_length > (mode - 1))
194                 {
195                     memcpy (buffer, pbuf, buffer_length-mode);
196                     *(buffer+buffer_length-mode) = '\0';
197                 }
198                 *(buffer+buffer_length-1) = '\0';
199             }
200             if (returned_length)
201             {
202                 *returned_length = pbuf == buffer ? len : buffer_length;
203             }
204             success = TRUE;
205         }
206         else
207         {
208             ERR("transferring wide to narrow\n");
209         }
210         if (pbuf != buffer)
211         {
212             HeapFree(GetProcessHeap(), 0, pbuf);
213         }
214     }
215     else
216     {
217         ERR("measuring wide to narrow\n");
218     }
219     return success;
220 }
221 
222 BOOL WINAPI SQLConfigDataSourceW(HWND hwndParent, WORD fRequest,
223                LPCWSTR lpszDriver, LPCWSTR lpszAttributes)
224 {
225     LPCWSTR p;
226 
227     clear_errors();
228     FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_w(lpszDriver),
229           debugstr_w(lpszAttributes));
230 
231     for (p = lpszAttributes; *p; p += lstrlenW(p) + 1)
232         FIXME("%s\n", debugstr_w(p));
233 
234     return TRUE;
235 }
236 
237 BOOL WINAPI SQLConfigDataSource(HWND hwndParent, WORD fRequest,
238                LPCSTR lpszDriver, LPCSTR lpszAttributes)
239 {
240     FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_a(lpszDriver),
241           debugstr_a(lpszAttributes));
242     clear_errors();
243     return TRUE;
244 }
245 
246 static HMODULE load_config_driver(const WCHAR *driver)
247 {
248     static WCHAR reg_driver[] = {'d','r','i','v','e','r',0};
249     long ret;
250     HMODULE hmod;
251     WCHAR *filename = NULL;
252     DWORD size = 0, type;
253     HKEY hkey;
254 
255     if ((ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey)) == ERROR_SUCCESS)
256     {
257         HKEY hkeydriver;
258 
259         if ((ret = RegOpenKeyW(hkey, driver, &hkeydriver)) == ERROR_SUCCESS)
260         {
261             ret = RegGetValueW(hkeydriver, NULL, reg_driver, RRF_RT_REG_SZ, &type, NULL, &size);
262             if(ret == ERROR_MORE_DATA)
263             {
264                 filename = HeapAlloc(GetProcessHeap(), 0, size);
265                 if(!filename)
266                 {
267                     RegCloseKey(hkeydriver);
268                     RegCloseKey(hkey);
269                     push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
270 
271                     return NULL;
272                 }
273                 ret = RegGetValueW(hkeydriver, NULL, driver, RRF_RT_REG_SZ, &type, filename, &size);
274             }
275 
276             RegCloseKey(hkeydriver);
277         }
278 
279         RegCloseKey(hkey);
280     }
281 
282     if(ret != ERROR_SUCCESS)
283     {
284         HeapFree(GetProcessHeap(), 0, filename);
285         push_error(ODBC_ERROR_INVALID_DSN, odbc_error_invalid_dsn);
286         return NULL;
287     }
288 
289     hmod = LoadLibraryW(filename);
290     HeapFree(GetProcessHeap(), 0, filename);
291 
292     if(!hmod)
293         push_error(ODBC_ERROR_LOAD_LIB_FAILED, odbc_error_load_lib_failed);
294 
295     return hmod;
296 }
297 
298 static BOOL write_config_value(const WCHAR *driver, const WCHAR *args)
299 {
300     long ret;
301     HKEY hkey, hkeydriver;
302     WCHAR *name = NULL;
303 
304     if(!args)
305         return FALSE;
306 
307     if((ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey)) == ERROR_SUCCESS)
308     {
309         if((ret = RegOpenKeyW(hkey, driver, &hkeydriver)) == ERROR_SUCCESS)
310         {
311             WCHAR *divider, *value;
312 
313             name = heap_alloc( (strlenW(args) + 1) * sizeof(WCHAR));
314             if(!name)
315             {
316                 push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
317                 goto fail;
318             }
319             lstrcpyW(name, args);
320 
321             divider = strchrW(name,'=');
322             if(!divider)
323             {
324                 push_error(ODBC_ERROR_INVALID_KEYWORD_VALUE, odbc_error_invalid_keyword);
325                 goto fail;
326             }
327 
328             value = divider + 1;
329             *divider = '\0';
330 
331             TRACE("Write pair: %s = %s\n", debugstr_w(name), debugstr_w(value));
332             if(RegSetValueExW(hkeydriver, name, 0, REG_SZ, (BYTE*)value,
333                                (strlenW(value)+1) * sizeof(WCHAR)) != ERROR_SUCCESS)
334                 ERR("Failed to write registry installed key\n");
335             heap_free(name);
336 
337             RegCloseKey(hkeydriver);
338         }
339 
340         RegCloseKey(hkey);
341     }
342 
343     if(ret != ERROR_SUCCESS)
344         push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
345 
346     return ret == ERROR_SUCCESS;
347 
348 fail:
349     RegCloseKey(hkeydriver);
350     RegCloseKey(hkey);
351     heap_free(name);
352 
353     return FALSE;
354 }
355 
356 BOOL WINAPI SQLConfigDriverW(HWND hwnd, WORD request, LPCWSTR driver,
357                LPCWSTR args, LPWSTR msg, WORD msgmax, WORD *msgout)
358 {
359     BOOL (WINAPI *pConfigDriverW)(HWND hwnd, WORD request, const WCHAR *driver, const WCHAR *args, const WCHAR *msg, WORD msgmax, WORD *msgout);
360     HMODULE hmod;
361     BOOL funcret = FALSE;
362 
363     clear_errors();
364     TRACE("(%p %d %s %s %p %d %p)\n", hwnd, request, debugstr_w(driver),
365           debugstr_w(args), msg, msgmax, msgout);
366 
367     if(request == ODBC_CONFIG_DRIVER)
368     {
369         return write_config_value(driver, args);
370     }
371 
372     hmod = load_config_driver(driver);
373     if(!hmod)
374         return FALSE;
375 
376     pConfigDriverW = (void*)GetProcAddress(hmod, "ConfigDriverW");
377     if(pConfigDriverW)
378         funcret = pConfigDriverW(hwnd, request, driver, args, msg, msgmax, msgout);
379 
380     if(!funcret)
381         push_error(ODBC_ERROR_REQUEST_FAILED, odbc_error_request_failed);
382 
383     FreeLibrary(hmod);
384 
385     return funcret;
386 }
387 
388 BOOL WINAPI SQLConfigDriver(HWND hwnd, WORD request, LPCSTR driver,
389                LPCSTR args, LPSTR msg, WORD msgmax, WORD *msgout)
390 {
391     BOOL (WINAPI *pConfigDriverA)(HWND hwnd, WORD request, const char *driver, const char *args, const char *msg, WORD msgmax, WORD *msgout);
392     HMODULE hmod;
393     WCHAR *driverW;
394     BOOL funcret = FALSE;
395 
396     clear_errors();
397     TRACE("(%p %d %s %s %p %d %p)\n", hwnd, request, debugstr_a(driver),
398           debugstr_a(args), msg, msgmax, msgout);
399 
400     driverW = heap_strdupAtoW(driver);
401     if(!driverW)
402     {
403         push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
404         return FALSE;
405     }
406     if(request == ODBC_CONFIG_DRIVER)
407     {
408         BOOL ret = FALSE;
409         WCHAR *argsW = heap_strdupAtoW(args);
410         if(argsW)
411         {
412             ret = write_config_value(driverW, argsW);
413             HeapFree(GetProcessHeap(), 0, argsW);
414         }
415         else
416         {
417             push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
418         }
419 
420         HeapFree(GetProcessHeap(), 0, driverW);
421 
422         return ret;
423     }
424 
425     hmod = load_config_driver(driverW);
426     HeapFree(GetProcessHeap(), 0, driverW);
427     if(!hmod)
428         return FALSE;
429 
430     pConfigDriverA = (void*)GetProcAddress(hmod, "ConfigDriver");
431     if(pConfigDriverA)
432         funcret = pConfigDriverA(hwnd, request, driver, args, msg, msgmax, msgout);
433 
434     if(!funcret)
435         push_error(ODBC_ERROR_REQUEST_FAILED, odbc_error_request_failed);
436 
437     FreeLibrary(hmod);
438 
439     return funcret;
440 }
441 
442 BOOL WINAPI SQLCreateDataSourceW(HWND hwnd, LPCWSTR lpszDS)
443 {
444     clear_errors();
445     FIXME("%p %s\n", hwnd, debugstr_w(lpszDS));
446     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
447     return FALSE;
448 }
449 
450 BOOL WINAPI SQLCreateDataSource(HWND hwnd, LPCSTR lpszDS)
451 {
452     clear_errors();
453     FIXME("%p %s\n", hwnd, debugstr_a(lpszDS));
454     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
455     return FALSE;
456 }
457 
458 BOOL WINAPI SQLGetAvailableDriversW(LPCWSTR lpszInfFile, LPWSTR lpszBuf,
459                WORD cbBufMax, WORD *pcbBufOut)
460 {
461     clear_errors();
462     FIXME("%s %p %d %p\n", debugstr_w(lpszInfFile), lpszBuf, cbBufMax, pcbBufOut);
463     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
464     return FALSE;
465 }
466 
467 BOOL WINAPI SQLGetAvailableDrivers(LPCSTR lpszInfFile, LPSTR lpszBuf,
468                WORD cbBufMax, WORD *pcbBufOut)
469 {
470     clear_errors();
471     FIXME("%s %p %d %p\n", debugstr_a(lpszInfFile), lpszBuf, cbBufMax, pcbBufOut);
472     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
473     return FALSE;
474 }
475 
476 BOOL WINAPI SQLGetConfigMode(UWORD *pwConfigMode)
477 {
478     clear_errors();
479     TRACE("%p\n", pwConfigMode);
480     if (pwConfigMode)
481         *pwConfigMode = config_mode;
482     return TRUE;
483 }
484 
485 BOOL WINAPI SQLGetInstalledDriversW(WCHAR *buf, WORD size, WORD *sizeout)
486 {
487     WORD written = 0;
488     DWORD index = 0;
489     BOOL ret = TRUE;
490     DWORD valuelen;
491     WCHAR *value;
492     HKEY drivers;
493     DWORD len;
494     LONG res;
495 
496     clear_errors();
497 
498     TRACE("%p %d %p\n", buf, size, sizeout);
499 
500     if (!buf || !size)
501     {
502         push_error(ODBC_ERROR_INVALID_BUFF_LEN, odbc_error_invalid_buff_len);
503         return FALSE;
504     }
505 
506     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, drivers_key, 0, KEY_QUERY_VALUE, &drivers);
507     if (res)
508     {
509         push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
510         return FALSE;
511     }
512 
513     valuelen = 256;
514     value = heap_alloc(valuelen * sizeof(WCHAR));
515 
516     size--;
517 
518     while (1)
519     {
520         len = valuelen;
521         res = RegEnumValueW(drivers, index, value, &len, NULL, NULL, NULL, NULL);
522         while (res == ERROR_MORE_DATA)
523         {
524             value = heap_realloc(value, ++len * sizeof(WCHAR));
525             res = RegEnumValueW(drivers, index, value, &len, NULL, NULL, NULL, NULL);
526         }
527         if (res == ERROR_SUCCESS)
528         {
529             lstrcpynW(buf + written, value, size - written);
530             written += min(len + 1, size - written);
531         }
532         else if (res == ERROR_NO_MORE_ITEMS)
533             break;
534         else
535         {
536             push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err);
537             ret = FALSE;
538             break;
539         }
540         index++;
541     }
542 
543     buf[written++] = 0;
544 
545     heap_free(value);
546     RegCloseKey(drivers);
547     if (sizeout)
548         *sizeout = written;
549     return ret;
550 }
551 
552 BOOL WINAPI SQLGetInstalledDrivers(char *buf, WORD size, WORD *sizeout)
553 {
554     WORD written;
555     WCHAR *wbuf;
556     BOOL ret;
557 
558     TRACE("%p %d %p\n", buf, size, sizeout);
559 
560     if (!buf || !size)
561     {
562         push_error(ODBC_ERROR_INVALID_BUFF_LEN, odbc_error_invalid_buff_len);
563         return FALSE;
564     }
565 
566     wbuf = heap_alloc(size * sizeof(WCHAR));
567     if (!wbuf)
568     {
569         push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
570         return FALSE;
571     }
572 
573     ret = SQLGetInstalledDriversW(wbuf, size, &written);
574     if (!ret)
575         return FALSE;
576 
577     *sizeout = WideCharToMultiByte(CP_ACP, 0, wbuf, written, NULL, 0, NULL, NULL);
578     WideCharToMultiByte(CP_ACP, 0, wbuf, written, buf, size, NULL, NULL);
579 
580     heap_free(wbuf);
581     return TRUE;
582 }
583 
584 static HKEY get_privateprofile_sectionkey(LPCWSTR section, LPCWSTR filename)
585 {
586     HKEY hkey, hkeyfilename, hkeysection;
587     LONG ret;
588 
589     if (RegOpenKeyW(HKEY_CURRENT_USER, odbcW, &hkey))
590         return NULL;
591 
592     ret = RegOpenKeyW(hkey, filename, &hkeyfilename);
593     RegCloseKey(hkey);
594     if (ret)
595         return NULL;
596 
597     ret = RegOpenKeyW(hkeyfilename, section, &hkeysection);
598     RegCloseKey(hkeyfilename);
599 
600     return ret ? NULL : hkeysection;
601 }
602 
603 int WINAPI SQLGetPrivateProfileStringW(LPCWSTR section, LPCWSTR entry,
604     LPCWSTR defvalue, LPWSTR buff, int buff_len, LPCWSTR filename)
605 {
606     BOOL usedefault = TRUE;
607     HKEY sectionkey;
608     LONG ret = 0;
609 
610     TRACE("%s %s %s %p %d %s\n", debugstr_w(section), debugstr_w(entry),
611                debugstr_w(defvalue), buff, buff_len, debugstr_w(filename));
612 
613     clear_errors();
614 
615     if (buff_len <= 0 || !section)
616         return 0;
617 
618     if(buff)
619         buff[0] = 0;
620 
621     if (!defvalue || !buff)
622         return 0;
623 
624     sectionkey = get_privateprofile_sectionkey(section, filename);
625     if (sectionkey)
626     {
627         DWORD type, size;
628 
629         if (entry)
630         {
631             size = buff_len * sizeof(*buff);
632             if (RegGetValueW(sectionkey, NULL, entry, RRF_RT_REG_SZ, &type, buff, &size) == ERROR_SUCCESS)
633             {
634                 usedefault = FALSE;
635                 ret = (size / sizeof(*buff)) - 1;
636             }
637         }
638         else
639         {
640             WCHAR name[MAX_PATH];
641             DWORD index = 0;
642             DWORD namelen;
643 
644             usedefault = FALSE;
645 
646             memset(buff, 0, buff_len);
647 
648             namelen = sizeof(name);
649             while (RegEnumValueW(sectionkey, index, name, &namelen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
650             {
651                 if ((ret +  namelen+1) > buff_len)
652                     break;
653 
654                 lstrcpyW(buff+ret, name);
655                 ret += namelen+1;
656                 namelen = sizeof(name);
657                 index++;
658             }
659         }
660 
661         RegCloseKey(sectionkey);
662     }
663     else
664         usedefault = entry != NULL;
665 
666     if (usedefault)
667     {
668         lstrcpynW(buff, defvalue, buff_len);
669         ret = lstrlenW(buff);
670     }
671 
672     return ret;
673 }
674 
675 int WINAPI SQLGetPrivateProfileString(LPCSTR section, LPCSTR entry,
676     LPCSTR defvalue, LPSTR buff, int buff_len, LPCSTR filename)
677 {
678     WCHAR *sectionW, *filenameW;
679     BOOL usedefault = TRUE;
680     HKEY sectionkey;
681     LONG ret = 0;
682 
683     TRACE("%s %s %s %p %d %s\n", debugstr_a(section), debugstr_a(entry),
684                debugstr_a(defvalue), buff, buff_len, debugstr_a(filename));
685 
686     clear_errors();
687 
688     if (buff_len <= 0)
689         return 0;
690 
691     if (buff)
692         buff[0] = 0;
693 
694     if (!section || !defvalue || !buff)
695         return 0;
696 
697     sectionW = heap_strdupAtoW(section);
698     filenameW = heap_strdupAtoW(filename);
699 
700     sectionkey = get_privateprofile_sectionkey(sectionW, filenameW);
701 
702     heap_free(sectionW);
703     heap_free(filenameW);
704 
705     if (sectionkey)
706     {
707         DWORD type, size;
708 
709         if (entry)
710         {
711             size = buff_len * sizeof(*buff);
712             if (RegGetValueA(sectionkey, NULL, entry, RRF_RT_REG_SZ, &type, buff, &size) == ERROR_SUCCESS)
713             {
714                 usedefault = FALSE;
715                 ret = (size / sizeof(*buff)) - 1;
716             }
717         }
718         else
719         {
720             char name[MAX_PATH] = {0};
721             DWORD index = 0;
722             DWORD namelen;
723 
724             usedefault = FALSE;
725 
726             memset(buff, 0, buff_len);
727 
728             namelen = sizeof(name);
729             while (RegEnumValueA(sectionkey, index, name, &namelen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
730             {
731                 if ((ret +  namelen+1) > buff_len)
732                     break;
733 
734                 lstrcpyA(buff+ret, name);
735 
736                 ret += namelen+1;
737                 namelen = sizeof(name);
738                 index++;
739             }
740         }
741 
742         RegCloseKey(sectionkey);
743     }
744     else
745         usedefault = entry != NULL;
746 
747     if (usedefault)
748     {
749         lstrcpynA(buff, defvalue, buff_len);
750         ret = strlen(buff);
751     }
752 
753     return ret;
754 }
755 
756 BOOL WINAPI SQLGetTranslatorW(HWND hwndParent, LPWSTR lpszName, WORD cbNameMax,
757                WORD *pcbNameOut, LPWSTR lpszPath, WORD cbPathMax,
758                WORD *pcbPathOut, DWORD *pvOption)
759 {
760     clear_errors();
761     FIXME("%p %s %d %p %p %d %p %p\n", hwndParent, debugstr_w(lpszName), cbNameMax,
762                pcbNameOut, lpszPath, cbPathMax, pcbPathOut, pvOption);
763     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
764     return FALSE;
765 }
766 
767 BOOL WINAPI SQLGetTranslator(HWND hwndParent, LPSTR lpszName, WORD cbNameMax,
768                WORD *pcbNameOut, LPSTR lpszPath, WORD cbPathMax,
769                WORD *pcbPathOut, DWORD *pvOption)
770 {
771     clear_errors();
772     FIXME("%p %s %d %p %p %d %p %p\n", hwndParent, debugstr_a(lpszName), cbNameMax,
773                pcbNameOut, lpszPath, cbPathMax, pcbPathOut, pvOption);
774     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
775     return FALSE;
776 }
777 
778 BOOL WINAPI SQLInstallDriverW(LPCWSTR lpszInfFile, LPCWSTR lpszDriver,
779                LPWSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut)
780 {
781     DWORD usage;
782 
783     clear_errors();
784     TRACE("%s %s %p %d %p\n", debugstr_w(lpszInfFile),
785           debugstr_w(lpszDriver), lpszPath, cbPathMax, pcbPathOut);
786 
787     if (lpszInfFile)
788         return FALSE;
789 
790     return SQLInstallDriverExW(lpszDriver, NULL, lpszPath, cbPathMax,
791                                pcbPathOut, ODBC_INSTALL_COMPLETE, &usage);
792 }
793 
794 BOOL WINAPI SQLInstallDriver(LPCSTR lpszInfFile, LPCSTR lpszDriver,
795                LPSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut)
796 {
797     DWORD usage;
798 
799     clear_errors();
800     TRACE("%s %s %p %d %p\n", debugstr_a(lpszInfFile),
801           debugstr_a(lpszDriver), lpszPath, cbPathMax, pcbPathOut);
802 
803     if (lpszInfFile)
804         return FALSE;
805 
806     return SQLInstallDriverEx(lpszDriver, NULL, lpszPath, cbPathMax,
807                               pcbPathOut, ODBC_INSTALL_COMPLETE, &usage);
808 }
809 
810 static void write_registry_values(const WCHAR *regkey, const WCHAR *driver, const  WCHAR *path_in, WCHAR *path,
811                                   DWORD *usage_count)
812 {
813     static const WCHAR installed[] = {'I','n','s','t','a','l','l','e','d',0};
814     static const WCHAR slash[] = {'\\', 0};
815     static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
816     static const WCHAR setupW[] = {'S','e','t','u','p',0};
817     static const WCHAR translator[] = {'T','r','a','n','s','l','a','t','o','r',0};
818     HKEY hkey, hkeydriver;
819 
820     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey) == ERROR_SUCCESS)
821     {
822         if (RegCreateKeyW(hkey, regkey, &hkeydriver) == ERROR_SUCCESS)
823         {
824             if(RegSetValueExW(hkeydriver, driver, 0, REG_SZ, (BYTE*)installed, sizeof(installed)) != ERROR_SUCCESS)
825                 ERR("Failed to write registry installed key\n");
826 
827             RegCloseKey(hkeydriver);
828         }
829 
830         if (RegCreateKeyW(hkey, driver, &hkeydriver) == ERROR_SUCCESS)
831         {
832             WCHAR entry[1024];
833             const WCHAR *p;
834             DWORD usagecount = 0;
835             DWORD type, size;
836 
837             /* Skip name entry */
838             p = driver;
839             p += lstrlenW(p) + 1;
840 
841             if (!path_in)
842                 GetSystemDirectoryW(path, MAX_PATH);
843             else
844                 lstrcpyW(path, path_in);
845 
846             /* Store Usage */
847             size = sizeof(usagecount);
848             RegGetValueA(hkeydriver, NULL, "UsageCount", RRF_RT_DWORD, &type, &usagecount, &size);
849             TRACE("Usage count %d\n", usagecount);
850 
851             for (; *p; p += lstrlenW(p) + 1)
852             {
853                 WCHAR *divider = strchrW(p,'=');
854 
855                 if (divider)
856                 {
857                     WCHAR *value;
858                     int len;
859 
860                     /* Write pair values to the registry. */
861                     lstrcpynW(entry, p, divider - p + 1);
862 
863                     divider++;
864                     TRACE("Writing pair %s,%s\n", debugstr_w(entry), debugstr_w(divider));
865 
866                     /* Driver, Setup, Translator entries use the system path unless a path is specified. */
867                     if(lstrcmpiW(driverW, entry) == 0 || lstrcmpiW(setupW, entry) == 0 ||
868                        lstrcmpiW(translator, entry) == 0)
869                     {
870                         len = lstrlenW(path) + lstrlenW(slash) + lstrlenW(divider) + 1;
871                         value = heap_alloc(len * sizeof(WCHAR));
872                         if(!value)
873                         {
874                             ERR("Out of memory\n");
875                             return;
876                         }
877 
878                         lstrcpyW(value, path);
879                         lstrcatW(value, slash);
880                         lstrcatW(value, divider);
881                     }
882                     else
883                     {
884                         len = lstrlenW(divider) + 1;
885                         value = heap_alloc(len * sizeof(WCHAR));
886                         lstrcpyW(value, divider);
887                     }
888 
889                     if (RegSetValueExW(hkeydriver, entry, 0, REG_SZ, (BYTE*)value,
890                                     (lstrlenW(value)+1)*sizeof(WCHAR)) != ERROR_SUCCESS)
891                         ERR("Failed to write registry data %s %s\n", debugstr_w(entry), debugstr_w(value));
892                     heap_free(value);
893                 }
894                 else
895                 {
896                     ERR("No pair found. %s\n", debugstr_w(p));
897                     break;
898                 }
899             }
900 
901             /* Set Usage Count */
902             usagecount++;
903             if (RegSetValueExA(hkeydriver, "UsageCount", 0, REG_DWORD, (BYTE*)&usagecount, sizeof(usagecount)) != ERROR_SUCCESS)
904                 ERR("Failed to write registry UsageCount key\n");
905 
906             if (usage_count)
907                 *usage_count = usagecount;
908 
909             RegCloseKey(hkeydriver);
910         }
911 
912         RegCloseKey(hkey);
913     }
914 }
915 
916 BOOL WINAPI SQLInstallDriverExW(LPCWSTR lpszDriver, LPCWSTR lpszPathIn,
917                LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
918                WORD fRequest, LPDWORD lpdwUsageCount)
919 {
920     UINT len;
921     WCHAR path[MAX_PATH];
922 
923     clear_errors();
924     TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszDriver),
925           debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
926           fRequest, lpdwUsageCount);
927 
928     write_registry_values(odbcdrivers, lpszDriver, lpszPathIn, path, lpdwUsageCount);
929 
930     len = lstrlenW(path);
931 
932     if (pcbPathOut)
933         *pcbPathOut = len;
934 
935     if (lpszPathOut && cbPathOutMax > len)
936     {
937         lstrcpyW(lpszPathOut, path);
938         return TRUE;
939     }
940     return FALSE;
941 }
942 
943 BOOL WINAPI SQLInstallDriverEx(LPCSTR lpszDriver, LPCSTR lpszPathIn,
944                LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
945                WORD fRequest, LPDWORD lpdwUsageCount)
946 {
947     LPWSTR driver, pathin;
948     WCHAR pathout[MAX_PATH];
949     BOOL ret;
950     WORD cbOut = 0;
951 
952     clear_errors();
953     TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszDriver),
954           debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
955           fRequest, lpdwUsageCount);
956 
957     driver = SQLInstall_strdup_multi(lpszDriver);
958     pathin = SQLInstall_strdup(lpszPathIn);
959 
960     ret = SQLInstallDriverExW(driver, pathin, pathout, MAX_PATH, &cbOut,
961                               fRequest, lpdwUsageCount);
962     if (ret)
963     {
964         int len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
965                                        0, NULL, NULL);
966         if (len)
967         {
968             if (pcbPathOut)
969                 *pcbPathOut = len - 1;
970 
971             if (!lpszPathOut || cbPathOutMax < len)
972             {
973                 ret = FALSE;
974                 goto out;
975             }
976             len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
977                                        cbPathOutMax, NULL, NULL);
978         }
979     }
980 
981 out:
982     HeapFree(GetProcessHeap(), 0, driver);
983     HeapFree(GetProcessHeap(), 0, pathin);
984     return ret;
985 }
986 
987 BOOL WINAPI SQLInstallDriverManagerW(LPWSTR lpszPath, WORD cbPathMax,
988                WORD *pcbPathOut)
989 {
990     UINT len;
991     WCHAR path[MAX_PATH];
992 
993     TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut);
994 
995     if (cbPathMax < MAX_PATH)
996         return FALSE;
997 
998     clear_errors();
999 
1000     len = GetSystemDirectoryW(path, MAX_PATH);
1001 
1002     if (pcbPathOut)
1003         *pcbPathOut = len;
1004 
1005     if (lpszPath && cbPathMax > len)
1006     {
1007     	lstrcpyW(lpszPath, path);
1008     	return TRUE;
1009     }
1010     return FALSE;
1011 }
1012 
1013 BOOL WINAPI SQLInstallDriverManager(LPSTR lpszPath, WORD cbPathMax,
1014                WORD *pcbPathOut)
1015 {
1016     BOOL ret;
1017     WORD len, cbOut = 0;
1018     WCHAR path[MAX_PATH];
1019 
1020     TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut);
1021 
1022     if (cbPathMax < MAX_PATH)
1023         return FALSE;
1024 
1025     clear_errors();
1026 
1027     ret = SQLInstallDriverManagerW(path, MAX_PATH, &cbOut);
1028     if (ret)
1029     {
1030         len =  WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath, 0,
1031                                    NULL, NULL);
1032         if (len)
1033         {
1034             if (pcbPathOut)
1035                 *pcbPathOut = len - 1;
1036 
1037             if (!lpszPath || cbPathMax < len)
1038                 return FALSE;
1039 
1040             len =  WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath,
1041                                        cbPathMax, NULL, NULL);
1042         }
1043     }
1044     return ret;
1045 }
1046 
1047 BOOL WINAPI SQLInstallODBCW(HWND hwndParent, LPCWSTR lpszInfFile,
1048                LPCWSTR lpszSrcPath, LPCWSTR lpszDrivers)
1049 {
1050     clear_errors();
1051     FIXME("%p %s %s %s\n", hwndParent, debugstr_w(lpszInfFile),
1052                debugstr_w(lpszSrcPath), debugstr_w(lpszDrivers));
1053     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1054     return FALSE;
1055 }
1056 
1057 BOOL WINAPI SQLInstallODBC(HWND hwndParent, LPCSTR lpszInfFile,
1058                LPCSTR lpszSrcPath, LPCSTR lpszDrivers)
1059 {
1060     clear_errors();
1061     FIXME("%p %s %s %s\n", hwndParent, debugstr_a(lpszInfFile),
1062                debugstr_a(lpszSrcPath), debugstr_a(lpszDrivers));
1063     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1064     return FALSE;
1065 }
1066 
1067 SQLRETURN WINAPI SQLInstallerErrorW(WORD iError, DWORD *pfErrorCode,
1068                LPWSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
1069 {
1070     TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg,
1071           cbErrorMsgMax, pcbErrorMsg);
1072 
1073     if (iError == 0)
1074     {
1075         return SQL_ERROR;
1076     }
1077     else if (iError <= num_errors)
1078     {
1079         BOOL truncated = FALSE;
1080         WORD len;
1081         LPCWSTR msg;
1082         iError--;
1083         if (pfErrorCode)
1084             *pfErrorCode = error_code[iError];
1085         msg = error_msg[iError];
1086         len = msg ? lstrlenW(msg) : 0;
1087         if (pcbErrorMsg)
1088             *pcbErrorMsg = len;
1089         len++;
1090         if (cbErrorMsgMax < len)
1091         {
1092             len = cbErrorMsgMax;
1093             truncated = TRUE;
1094         }
1095         if (lpszErrorMsg && len)
1096         {
1097             if (msg)
1098             {
1099                 memcpy (lpszErrorMsg, msg, len * sizeof(WCHAR));
1100             }
1101             else
1102             {
1103                 assert(len==1);
1104                 *lpszErrorMsg = 0;
1105             }
1106         }
1107         else
1108         {
1109             /* Yes.  If you pass a null pointer and a large length it is not an error! */
1110             truncated = TRUE;
1111         }
1112 
1113         return truncated ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
1114     }
1115 
1116     /* At least on Windows 2000 , the buffers are not altered in this case.  However that is a little too dangerous a test for just now */
1117     if (pcbErrorMsg)
1118         *pcbErrorMsg = 0;
1119 
1120     if (lpszErrorMsg && cbErrorMsgMax > 0)
1121         *lpszErrorMsg = '\0';
1122 
1123     return SQL_NO_DATA;
1124 }
1125 
1126 SQLRETURN WINAPI SQLInstallerError(WORD iError, DWORD *pfErrorCode,
1127                LPSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
1128 {
1129     SQLRETURN ret;
1130     LPWSTR wbuf;
1131     WORD cbwbuf;
1132     TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg,
1133           cbErrorMsgMax, pcbErrorMsg);
1134 
1135     wbuf = 0;
1136     if (lpszErrorMsg && cbErrorMsgMax)
1137     {
1138         wbuf = HeapAlloc(GetProcessHeap(), 0, cbErrorMsgMax*sizeof(WCHAR));
1139         if (!wbuf)
1140             return SQL_ERROR;
1141     }
1142     ret = SQLInstallerErrorW(iError, pfErrorCode, wbuf, cbErrorMsgMax, &cbwbuf);
1143     if (wbuf)
1144     {
1145         WORD cbBuf = 0;
1146         SQLInstall_narrow(1, lpszErrorMsg, wbuf, cbwbuf+1, cbErrorMsgMax, &cbBuf);
1147         HeapFree(GetProcessHeap(), 0, wbuf);
1148         if (pcbErrorMsg)
1149             *pcbErrorMsg = cbBuf-1;
1150     }
1151     return ret;
1152 }
1153 
1154 BOOL WINAPI SQLInstallTranslatorExW(LPCWSTR lpszTranslator, LPCWSTR lpszPathIn,
1155                LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
1156                WORD fRequest, LPDWORD lpdwUsageCount)
1157 {
1158     UINT len;
1159     WCHAR path[MAX_PATH];
1160 
1161     clear_errors();
1162     TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszTranslator),
1163           debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
1164           fRequest, lpdwUsageCount);
1165 
1166     write_registry_values(odbctranslators, lpszTranslator, lpszPathIn, path, lpdwUsageCount);
1167 
1168     len = lstrlenW(path);
1169 
1170     if (pcbPathOut)
1171         *pcbPathOut = len;
1172 
1173     if (lpszPathOut && cbPathOutMax > len)
1174     {
1175         lstrcpyW(lpszPathOut, path);
1176         return TRUE;
1177     }
1178     return FALSE;
1179 }
1180 
1181 BOOL WINAPI SQLInstallTranslatorEx(LPCSTR lpszTranslator, LPCSTR lpszPathIn,
1182                LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
1183                WORD fRequest, LPDWORD lpdwUsageCount)
1184 {
1185     LPCSTR p;
1186     LPWSTR translator, pathin;
1187     WCHAR pathout[MAX_PATH];
1188     BOOL ret;
1189     WORD cbOut = 0;
1190 
1191     clear_errors();
1192     TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszTranslator),
1193           debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
1194           fRequest, lpdwUsageCount);
1195 
1196     for (p = lpszTranslator; *p; p += lstrlenA(p) + 1)
1197         TRACE("%s\n", debugstr_a(p));
1198 
1199     translator = SQLInstall_strdup_multi(lpszTranslator);
1200     pathin = SQLInstall_strdup(lpszPathIn);
1201 
1202     ret = SQLInstallTranslatorExW(translator, pathin, pathout, MAX_PATH,
1203                                   &cbOut, fRequest, lpdwUsageCount);
1204     if (ret)
1205     {
1206         int len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
1207                                        0, NULL, NULL);
1208         if (len)
1209         {
1210             if (pcbPathOut)
1211                 *pcbPathOut = len - 1;
1212 
1213             if (!lpszPathOut || cbPathOutMax < len)
1214             {
1215                 ret = FALSE;
1216                 goto out;
1217             }
1218             len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
1219                                        cbPathOutMax, NULL, NULL);
1220         }
1221     }
1222 
1223 out:
1224     HeapFree(GetProcessHeap(), 0, translator);
1225     HeapFree(GetProcessHeap(), 0, pathin);
1226     return ret;
1227 }
1228 
1229 BOOL WINAPI SQLInstallTranslator(LPCSTR lpszInfFile, LPCSTR lpszTranslator,
1230                LPCSTR lpszPathIn, LPSTR lpszPathOut, WORD cbPathOutMax,
1231                WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount)
1232 {
1233     clear_errors();
1234     TRACE("%s %s %s %p %d %p %d %p\n", debugstr_a(lpszInfFile),
1235           debugstr_a(lpszTranslator), debugstr_a(lpszPathIn), lpszPathOut,
1236           cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1237 
1238     if (lpszInfFile)
1239         return FALSE;
1240 
1241     return SQLInstallTranslatorEx(lpszTranslator, lpszPathIn, lpszPathOut,
1242                        cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1243 }
1244 
1245 BOOL WINAPI SQLInstallTranslatorW(LPCWSTR lpszInfFile, LPCWSTR lpszTranslator,
1246               LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax,
1247               WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount)
1248 {
1249     clear_errors();
1250     TRACE("%s %s %s %p %d %p %d %p\n", debugstr_w(lpszInfFile),
1251           debugstr_w(lpszTranslator), debugstr_w(lpszPathIn), lpszPathOut,
1252           cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1253 
1254     if (lpszInfFile)
1255         return FALSE;
1256 
1257     return SQLInstallTranslatorExW(lpszTranslator, lpszPathIn, lpszPathOut,
1258                         cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1259 }
1260 
1261 BOOL WINAPI SQLManageDataSources(HWND hwnd)
1262 {
1263     clear_errors();
1264     FIXME("%p\n", hwnd);
1265     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1266     return FALSE;
1267 }
1268 
1269 SQLRETURN WINAPI SQLPostInstallerErrorW(DWORD fErrorCode, LPCWSTR szErrorMsg)
1270 {
1271     FIXME("%u %s\n", fErrorCode, debugstr_w(szErrorMsg));
1272     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1273     return FALSE;
1274 }
1275 
1276 SQLRETURN WINAPI SQLPostInstallerError(DWORD fErrorCode, LPCSTR szErrorMsg)
1277 {
1278     FIXME("%u %s\n", fErrorCode, debugstr_a(szErrorMsg));
1279     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1280     return FALSE;
1281 }
1282 
1283 BOOL WINAPI SQLReadFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName,
1284                LPCWSTR lpszKeyName, LPWSTR lpszString, WORD cbString,
1285                WORD *pcbString)
1286 {
1287     clear_errors();
1288     FIXME("%s %s %s %s %d %p\n", debugstr_w(lpszFileName), debugstr_w(lpszAppName),
1289                debugstr_w(lpszKeyName), debugstr_w(lpszString), cbString, pcbString);
1290     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1291     return FALSE;
1292 }
1293 
1294 BOOL WINAPI SQLReadFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName,
1295                LPCSTR lpszKeyName, LPSTR lpszString, WORD cbString,
1296                WORD *pcbString)
1297 {
1298     clear_errors();
1299     FIXME("%s %s %s %s %d %p\n", debugstr_a(lpszFileName), debugstr_a(lpszAppName),
1300                debugstr_a(lpszKeyName), debugstr_a(lpszString), cbString, pcbString);
1301     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1302     return FALSE;
1303 }
1304 
1305 BOOL WINAPI SQLRemoveDefaultDataSource(void)
1306 {
1307     clear_errors();
1308     FIXME("\n");
1309     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1310     return FALSE;
1311 }
1312 
1313 BOOL WINAPI SQLRemoveDriverW(LPCWSTR drivername, BOOL remove_dsn, LPDWORD usage_count)
1314 {
1315     HKEY hkey;
1316     DWORD usagecount = 1;
1317 
1318     clear_errors();
1319     TRACE("%s %d %p\n", debugstr_w(drivername), remove_dsn, usage_count);
1320 
1321     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey) == ERROR_SUCCESS)
1322     {
1323         HKEY hkeydriver;
1324 
1325         if (RegOpenKeyW(hkey, drivername, &hkeydriver) == ERROR_SUCCESS)
1326         {
1327             DWORD size, type;
1328             DWORD count;
1329 
1330             size = sizeof(usagecount);
1331             RegGetValueA(hkeydriver, NULL, "UsageCount", RRF_RT_DWORD, &type, &usagecount, &size);
1332             TRACE("Usage count %d\n", usagecount);
1333             count = usagecount - 1;
1334             if (count)
1335             {
1336                  if (RegSetValueExA(hkeydriver, "UsageCount", 0, REG_DWORD, (BYTE*)&count, sizeof(count)) != ERROR_SUCCESS)
1337                     ERR("Failed to write registry UsageCount key\n");
1338             }
1339 
1340             RegCloseKey(hkeydriver);
1341         }
1342 
1343         if (usagecount)
1344             usagecount--;
1345 
1346         if (!usagecount)
1347         {
1348             if (RegDeleteKeyW(hkey, drivername) != ERROR_SUCCESS)
1349                 ERR("Failed to delete registry key: %s\n", debugstr_w(drivername));
1350 
1351             if (RegOpenKeyW(hkey, odbcdrivers, &hkeydriver) == ERROR_SUCCESS)
1352             {
1353                 if(RegDeleteValueW(hkeydriver, drivername) != ERROR_SUCCESS)
1354                     ERR("Failed to delete registry value: %s\n", debugstr_w(drivername));
1355                 RegCloseKey(hkeydriver);
1356             }
1357         }
1358 
1359         RegCloseKey(hkey);
1360     }
1361 
1362     if (usage_count)
1363         *usage_count = usagecount;
1364 
1365     return TRUE;
1366 }
1367 
1368 BOOL WINAPI SQLRemoveDriver(LPCSTR lpszDriver, BOOL fRemoveDSN,
1369                LPDWORD lpdwUsageCount)
1370 {
1371     WCHAR *driver;
1372     BOOL ret;
1373 
1374     clear_errors();
1375     TRACE("%s %d %p\n", debugstr_a(lpszDriver), fRemoveDSN, lpdwUsageCount);
1376 
1377     driver = SQLInstall_strdup(lpszDriver);
1378 
1379     ret =  SQLRemoveDriverW(driver, fRemoveDSN, lpdwUsageCount);
1380 
1381     HeapFree(GetProcessHeap(), 0, driver);
1382     return ret;
1383 }
1384 
1385 BOOL WINAPI SQLRemoveDriverManager(LPDWORD pdwUsageCount)
1386 {
1387     clear_errors();
1388     FIXME("%p\n", pdwUsageCount);
1389     if (pdwUsageCount) *pdwUsageCount = 1;
1390     return TRUE;
1391 }
1392 
1393 BOOL WINAPI SQLRemoveDSNFromIniW(LPCWSTR lpszDSN)
1394 {
1395     clear_errors();
1396     FIXME("%s\n", debugstr_w(lpszDSN));
1397     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1398     return FALSE;
1399 }
1400 
1401 BOOL WINAPI SQLRemoveDSNFromIni(LPCSTR lpszDSN)
1402 {
1403     clear_errors();
1404     FIXME("%s\n", debugstr_a(lpszDSN));
1405     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1406     return FALSE;
1407 }
1408 
1409 BOOL WINAPI SQLRemoveTranslatorW(const WCHAR *translator, DWORD *usage_count)
1410 {
1411     HKEY hkey;
1412     DWORD usagecount = 1;
1413     BOOL ret = TRUE;
1414 
1415     clear_errors();
1416     TRACE("%s %p\n", debugstr_w(translator), usage_count);
1417 
1418     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey) == ERROR_SUCCESS)
1419     {
1420         HKEY hkeydriver;
1421 
1422         if (RegOpenKeyW(hkey, translator, &hkeydriver) == ERROR_SUCCESS)
1423         {
1424             DWORD size, type;
1425             DWORD count;
1426 
1427             size = sizeof(usagecount);
1428             RegGetValueA(hkeydriver, NULL, "UsageCount", RRF_RT_DWORD, &type, &usagecount, &size);
1429             TRACE("Usage count %d\n", usagecount);
1430             count = usagecount - 1;
1431             if (count)
1432             {
1433                  if (RegSetValueExA(hkeydriver, "UsageCount", 0, REG_DWORD, (BYTE*)&count, sizeof(count)) != ERROR_SUCCESS)
1434                     ERR("Failed to write registry UsageCount key\n");
1435             }
1436 
1437             RegCloseKey(hkeydriver);
1438         }
1439 
1440         if (usagecount)
1441             usagecount--;
1442 
1443         if (!usagecount)
1444         {
1445             if(RegDeleteKeyW(hkey, translator) != ERROR_SUCCESS)
1446             {
1447                 push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
1448                 WARN("Failed to delete registry key: %s\n", debugstr_w(translator));
1449                 ret = FALSE;
1450             }
1451 
1452             if (ret && RegOpenKeyW(hkey, odbctranslators, &hkeydriver) == ERROR_SUCCESS)
1453             {
1454                 if(RegDeleteValueW(hkeydriver, translator) != ERROR_SUCCESS)
1455                 {
1456                     push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
1457                     WARN("Failed to delete registry key: %s\n", debugstr_w(translator));
1458                     ret = FALSE;
1459                 }
1460 
1461                 RegCloseKey(hkeydriver);
1462             }
1463         }
1464 
1465         RegCloseKey(hkey);
1466     }
1467 
1468     if (ret && usage_count)
1469         *usage_count = usagecount;
1470 
1471     return ret;
1472 }
1473 
1474 BOOL WINAPI SQLRemoveTranslator(LPCSTR lpszTranslator, LPDWORD lpdwUsageCount)
1475 {
1476     WCHAR *translator;
1477     BOOL ret;
1478 
1479     clear_errors();
1480     TRACE("%s %p\n", debugstr_a(lpszTranslator), lpdwUsageCount);
1481 
1482     translator = SQLInstall_strdup(lpszTranslator);
1483     ret =  SQLRemoveTranslatorW(translator, lpdwUsageCount);
1484 
1485     HeapFree(GetProcessHeap(), 0, translator);
1486     return ret;
1487 }
1488 
1489 BOOL WINAPI SQLSetConfigMode(UWORD wConfigMode)
1490 {
1491     clear_errors();
1492     TRACE("%u\n", wConfigMode);
1493 
1494     if (wConfigMode > ODBC_SYSTEM_DSN)
1495     {
1496         push_error(ODBC_ERROR_INVALID_PARAM_SEQUENCE, odbc_error_invalid_param_sequence);
1497         return FALSE;
1498     }
1499     else
1500     {
1501         config_mode = wConfigMode;
1502         return TRUE;
1503     }
1504 }
1505 
1506 BOOL WINAPI SQLValidDSNW(LPCWSTR lpszDSN)
1507 {
1508     clear_errors();
1509     FIXME("%s\n", debugstr_w(lpszDSN));
1510     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1511     return FALSE;
1512 }
1513 
1514 BOOL WINAPI SQLValidDSN(LPCSTR lpszDSN)
1515 {
1516     clear_errors();
1517     FIXME("%s\n", debugstr_a(lpszDSN));
1518     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1519     return FALSE;
1520 }
1521 
1522 BOOL WINAPI SQLWriteDSNToIniW(LPCWSTR lpszDSN, LPCWSTR lpszDriver)
1523 {
1524     clear_errors();
1525     FIXME("%s %s\n", debugstr_w(lpszDSN), debugstr_w(lpszDriver));
1526     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1527     return FALSE;
1528 }
1529 
1530 BOOL WINAPI SQLWriteDSNToIni(LPCSTR lpszDSN, LPCSTR lpszDriver)
1531 {
1532     clear_errors();
1533     FIXME("%s %s\n", debugstr_a(lpszDSN), debugstr_a(lpszDriver));
1534     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1535     return FALSE;
1536 }
1537 
1538 BOOL WINAPI SQLWriteFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName,
1539                LPCWSTR lpszKeyName, LPCWSTR lpszString)
1540 {
1541     clear_errors();
1542     FIXME("%s %s %s %s\n", debugstr_w(lpszFileName), debugstr_w(lpszAppName),
1543                  debugstr_w(lpszKeyName), debugstr_w(lpszString));
1544     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1545     return FALSE;
1546 }
1547 
1548 BOOL WINAPI SQLWriteFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName,
1549                LPCSTR lpszKeyName, LPCSTR lpszString)
1550 {
1551     clear_errors();
1552     FIXME("%s %s %s %s\n", debugstr_a(lpszFileName), debugstr_a(lpszAppName),
1553                  debugstr_a(lpszKeyName), debugstr_a(lpszString));
1554     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1555     return FALSE;
1556 }
1557 
1558 BOOL WINAPI SQLWritePrivateProfileStringW(LPCWSTR lpszSection, LPCWSTR lpszEntry,
1559                LPCWSTR lpszString, LPCWSTR lpszFilename)
1560 {
1561     LONG ret;
1562     HKEY hkey;
1563 
1564     clear_errors();
1565     TRACE("%s %s %s %s\n", debugstr_w(lpszSection), debugstr_w(lpszEntry),
1566                 debugstr_w(lpszString), debugstr_w(lpszFilename));
1567 
1568     if(!lpszFilename || !*lpszFilename)
1569     {
1570         push_error(ODBC_ERROR_INVALID_STR, odbc_error_invalid_param_string);
1571         return FALSE;
1572     }
1573 
1574     if ((ret = RegCreateKeyW(HKEY_CURRENT_USER, odbcW, &hkey)) == ERROR_SUCCESS)
1575     {
1576          HKEY hkeyfilename;
1577 
1578          if ((ret = RegCreateKeyW(hkey, lpszFilename, &hkeyfilename)) == ERROR_SUCCESS)
1579          {
1580               HKEY hkey_section;
1581 
1582               if ((ret = RegCreateKeyW(hkeyfilename, lpszSection, &hkey_section)) == ERROR_SUCCESS)
1583               {
1584                   ret = RegSetValueExW(hkey_section, lpszEntry, 0, REG_SZ, (BYTE*)lpszString, (lstrlenW(lpszString)+1)*sizeof(WCHAR));
1585                   RegCloseKey(hkey_section);
1586               }
1587 
1588               RegCloseKey(hkeyfilename);
1589          }
1590 
1591          RegCloseKey(hkey);
1592     }
1593 
1594     return ret == ERROR_SUCCESS;
1595 }
1596 
1597 BOOL WINAPI SQLWritePrivateProfileString(LPCSTR lpszSection, LPCSTR lpszEntry,
1598                LPCSTR lpszString, LPCSTR lpszFilename)
1599 {
1600     BOOL ret;
1601     WCHAR *sect, *entry, *string, *file;
1602     clear_errors();
1603     TRACE("%s %s %s %s\n", lpszSection, lpszEntry, lpszString, lpszFilename);
1604 
1605     sect = heap_strdupAtoW(lpszSection);
1606     entry = heap_strdupAtoW(lpszEntry);
1607     string = heap_strdupAtoW(lpszString);
1608     file = heap_strdupAtoW(lpszFilename);
1609 
1610     ret = SQLWritePrivateProfileStringW(sect, entry, string, file);
1611 
1612     heap_free(sect);
1613     heap_free(entry);
1614     heap_free(string);
1615     heap_free(file);
1616 
1617     return ret;
1618 }
1619