Fix compile without XRender.
[wine] / dlls / winmm / driver.c
1 /*
2  * WINE Drivers functions
3  *
4  * Copyright 1994 Martin Ayotte
5  * Copyright 1998 Marcus Meissner
6  * Copyright 1999 Eric Pouech
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <string.h>
24 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "mmddk.h"
32 #include "winemm.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35 #include "excpt.h"
36 #include "wine/exception.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(driver);
39
40 static LPWINE_DRIVER   lpDrvItemList  /* = NULL */;
41 static const WCHAR HKLM_BASE[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
42                                   'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
43
44 WINE_MMTHREAD*  (*pFnGetMMThread16)(UINT16 h) /* = NULL */;
45 LPWINE_DRIVER   (*pFnOpenDriver16)(LPCWSTR,LPCWSTR,LPARAM) /* = NULL */;
46 LRESULT         (*pFnCloseDriver16)(UINT16,LPARAM,LPARAM) /* = NULL */;
47 LRESULT         (*pFnSendMessage16)(UINT16,UINT,LPARAM,LPARAM) /* = NULL */;
48
49 /**************************************************************************
50  *                      DRIVER_GetNumberOfModuleRefs            [internal]
51  *
52  * Returns the number of open drivers which share the same module.
53  */
54 static  unsigned DRIVER_GetNumberOfModuleRefs(HMODULE hModule, WINE_DRIVER** found)
55 {
56     LPWINE_DRIVER       lpDrv;
57     unsigned            count = 0;
58
59     if (found) *found = NULL;
60     for (lpDrv = lpDrvItemList; lpDrv; lpDrv = lpDrv->lpNextItem)
61     {
62         if (!(lpDrv->dwFlags & WINE_GDF_16BIT) && lpDrv->d.d32.hModule == hModule)
63         {
64             if (found && !*found) *found = lpDrv;
65             count++;
66         }
67     }
68     return count;
69 }
70
71 /**************************************************************************
72  *                              DRIVER_FindFromHDrvr            [internal]
73  *
74  * From a hDrvr being 32 bits, returns the WINE internal structure.
75  */
76 LPWINE_DRIVER   DRIVER_FindFromHDrvr(HDRVR hDrvr)
77 {
78     LPWINE_DRIVER d;
79
80     __TRY
81     {
82         d = (LPWINE_DRIVER)hDrvr;
83         if (d && d->dwMagic != WINE_DI_MAGIC) d = NULL;
84     }
85     __EXCEPT(NULL)
86     {
87         return NULL;
88     }
89     __ENDTRY;
90
91     return d;
92 }
93
94 /**************************************************************************
95  *                              DRIVER_SendMessage              [internal]
96  */
97 static LRESULT inline DRIVER_SendMessage(LPWINE_DRIVER lpDrv, UINT msg,
98                                          LPARAM lParam1, LPARAM lParam2)
99 {
100     LRESULT             ret = 0;
101
102     if (lpDrv->dwFlags & WINE_GDF_16BIT) {
103         /* no need to check mmsystem presence: the driver must have been opened as a 16 bit one,
104          */
105         if (pFnSendMessage16)
106             ret = pFnSendMessage16(lpDrv->d.d16.hDriver16, msg, lParam1, lParam2);
107     } else {
108         TRACE("Before call32 proc=%p drvrID=%08lx hDrv=%p wMsg=%04x p1=%08lx p2=%08lx\n", 
109               lpDrv->d.d32.lpDrvProc, lpDrv->d.d32.dwDriverID, (HDRVR)lpDrv, msg, lParam1, lParam2);
110         ret = lpDrv->d.d32.lpDrvProc(lpDrv->d.d32.dwDriverID, (HDRVR)lpDrv, msg, lParam1, lParam2);
111         TRACE("After  call32 proc=%p drvrID=%08lx hDrv=%p wMsg=%04x p1=%08lx p2=%08lx => %08lx\n", 
112               lpDrv->d.d32.lpDrvProc, lpDrv->d.d32.dwDriverID, (HDRVR)lpDrv, msg, lParam1, lParam2, ret);
113     }
114     return ret;
115 }
116
117 /**************************************************************************
118  *                              SendDriverMessage               [WINMM.@]
119  *                              DrvSendMessage                  [WINMM.@]
120  */
121 LRESULT WINAPI SendDriverMessage(HDRVR hDriver, UINT msg, LPARAM lParam1,
122                                  LPARAM lParam2)
123 {
124     LPWINE_DRIVER       lpDrv;
125     LRESULT             retval = 0;
126
127     TRACE("(%p, %04X, %08lX, %08lX)\n", hDriver, msg, lParam1, lParam2);
128
129     if ((lpDrv = DRIVER_FindFromHDrvr(hDriver)) != NULL) {
130         retval = DRIVER_SendMessage(lpDrv, msg, lParam1, lParam2);
131     } else {
132         WARN("Bad driver handle %p\n", hDriver);
133     }
134     TRACE("retval = %ld\n", retval);
135
136     return retval;
137 }
138
139 /**************************************************************************
140  *                              DRIVER_RemoveFromList           [internal]
141  *
142  * Generates all the logic to handle driver closure / deletion
143  * Removes a driver struct to the list of open drivers.
144  */
145 static  BOOL    DRIVER_RemoveFromList(LPWINE_DRIVER lpDrv)
146 {
147     if (!(lpDrv->dwFlags & WINE_GDF_16BIT)) {
148         /* last of this driver in list ? */
149         if (DRIVER_GetNumberOfModuleRefs(lpDrv->d.d32.hModule, NULL) == 1) {
150             DRIVER_SendMessage(lpDrv, DRV_DISABLE, 0L, 0L);
151             DRIVER_SendMessage(lpDrv, DRV_FREE,    0L, 0L);
152         }
153     }
154
155     if (lpDrv->lpPrevItem)
156         lpDrv->lpPrevItem->lpNextItem = lpDrv->lpNextItem;
157     else
158         lpDrvItemList = lpDrv->lpNextItem;
159     if (lpDrv->lpNextItem)
160         lpDrv->lpNextItem->lpPrevItem = lpDrv->lpPrevItem;
161     /* trash magic number */
162     lpDrv->dwMagic ^= 0xa5a5a5a5;
163
164     return TRUE;
165 }
166
167 /**************************************************************************
168  *                              DRIVER_AddToList                [internal]
169  *
170  * Adds a driver struct to the list of open drivers.
171  * Generates all the logic to handle driver creation / open.
172  */
173 static  BOOL    DRIVER_AddToList(LPWINE_DRIVER lpNewDrv, LPARAM lParam1, LPARAM lParam2)
174 {
175     lpNewDrv->dwMagic = WINE_DI_MAGIC;
176     /* First driver to be loaded for this module, need to load correctly the module */
177     if (!(lpNewDrv->dwFlags & WINE_GDF_16BIT)) {
178         /* first of this driver in list ? */
179         if (DRIVER_GetNumberOfModuleRefs(lpNewDrv->d.d32.hModule, NULL) == 0) {
180             if (DRIVER_SendMessage(lpNewDrv, DRV_LOAD, 0L, 0L) != DRV_SUCCESS) {
181                 TRACE("DRV_LOAD failed on driver %p\n", lpNewDrv);
182                 return FALSE;
183             }
184             /* returned value is not checked */
185             DRIVER_SendMessage(lpNewDrv, DRV_ENABLE, 0L, 0L);
186         }
187     }
188
189     lpNewDrv->lpNextItem = NULL;
190     if (lpDrvItemList == NULL) {
191         lpDrvItemList = lpNewDrv;
192         lpNewDrv->lpPrevItem = NULL;
193     } else {
194         LPWINE_DRIVER   lpDrv = lpDrvItemList;  /* find end of list */
195         while (lpDrv->lpNextItem != NULL)
196             lpDrv = lpDrv->lpNextItem;
197
198         lpDrv->lpNextItem = lpNewDrv;
199         lpNewDrv->lpPrevItem = lpDrv;
200     }
201
202     if (!(lpNewDrv->dwFlags & WINE_GDF_16BIT)) {
203         /* Now just open a new instance of a driver on this module */
204         lpNewDrv->d.d32.dwDriverID = DRIVER_SendMessage(lpNewDrv, DRV_OPEN, lParam1, lParam2);
205
206         if (lpNewDrv->d.d32.dwDriverID == 0) {
207             TRACE("DRV_OPEN failed on driver %p\n", lpNewDrv);
208             DRIVER_RemoveFromList(lpNewDrv);
209             return FALSE;
210         }
211     }
212     return TRUE;
213 }
214
215 /**************************************************************************
216  *                              DRIVER_GetLibName               [internal]
217  *
218  */
219 BOOL    DRIVER_GetLibName(LPCWSTR keyName, LPCWSTR sectName, LPWSTR buf, int sz)
220 {
221     HKEY        hKey, hSecKey;
222     DWORD       bufLen, lRet;
223     static const WCHAR wszSystemIni[] = {'S','Y','S','T','E','M','.','I','N','I',0};
224     WCHAR       wsznull = '\0';
225
226     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, HKLM_BASE, 0, KEY_QUERY_VALUE, &hKey);
227     if (lRet == ERROR_SUCCESS) {
228         lRet = RegOpenKeyExW(hKey, sectName, 0, KEY_QUERY_VALUE, &hSecKey);
229         if (lRet == ERROR_SUCCESS) {
230             bufLen = sz;
231             lRet = RegQueryValueExW(hSecKey, keyName, 0, 0, (void*)buf, &bufLen);
232             RegCloseKey( hSecKey );
233         }
234         RegCloseKey( hKey );
235     }
236     if (lRet == ERROR_SUCCESS) return TRUE;
237     /* default to system.ini if we can't find it in the registry,
238      * to support native installations where system.ini is still used */
239     return GetPrivateProfileStringW(sectName, keyName, &wsznull, buf, sz / sizeof(WCHAR), wszSystemIni);
240 }
241
242 /**************************************************************************
243  *                              DRIVER_TryOpenDriver32          [internal]
244  *
245  * Tries to load a 32 bit driver whose DLL's (module) name is fn
246  */
247 LPWINE_DRIVER   DRIVER_TryOpenDriver32(LPCWSTR fn, LPARAM lParam2)
248 {
249     LPWINE_DRIVER       lpDrv = NULL;
250     HMODULE             hModule = 0;
251     LPWSTR              ptr;
252     LPCSTR              cause = 0;
253
254     TRACE("(%s, %08lX);\n", debugstr_w(fn), lParam2);
255
256     if ((ptr = strchrW(fn, ' ')) != NULL) {
257         *ptr++ = '\0';
258         while (*ptr == ' ') ptr++;
259         if (*ptr == '\0') ptr = NULL;
260     }
261
262     lpDrv = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_DRIVER));
263     if (lpDrv == NULL) {cause = "OOM"; goto exit;}
264
265     if ((hModule = LoadLibraryW(fn)) == 0) {cause = "Not a 32 bit lib"; goto exit;}
266
267     lpDrv->d.d32.lpDrvProc = (DRIVERPROC)GetProcAddress(hModule, "DriverProc");
268     if (lpDrv->d.d32.lpDrvProc == NULL) {cause = "no DriverProc"; goto exit;}
269
270     lpDrv->dwFlags          = 0;
271     lpDrv->d.d32.hModule    = hModule;
272     lpDrv->d.d32.dwDriverID = 0;
273
274     /* Win32 installable drivers must support a two phase opening scheme:
275      * + first open with NULL as lParam2 (session instance),
276      * + then do a second open with the real non null lParam2)
277      */
278     if (DRIVER_GetNumberOfModuleRefs(lpDrv->d.d32.hModule, NULL) == 0 && lParam2)
279     {
280         LPWINE_DRIVER   ret;
281
282         if (!DRIVER_AddToList(lpDrv, (LPARAM)ptr, 0L))
283         {
284             cause = "load0 failed";
285             goto exit;
286         }
287         ret = DRIVER_TryOpenDriver32(fn, lParam2);
288         if (!ret)
289         {
290             CloseDriver((HDRVR)lpDrv, 0L, 0L);
291             cause = "load1 failed";
292             goto exit;
293         }
294         return ret;
295     }
296
297     if (!DRIVER_AddToList(lpDrv, (LPARAM)ptr, lParam2))
298     {cause = "load failed"; goto exit;}
299
300     TRACE("=> %p\n", lpDrv);
301     return lpDrv;
302  exit:
303     FreeLibrary(hModule);
304     HeapFree(GetProcessHeap(), 0, lpDrv);
305     TRACE("Unable to load 32 bit module %s: %s\n", debugstr_w(fn), cause);
306     return NULL;
307 }
308
309 /**************************************************************************
310  *                              OpenDriverA                     [WINMM.@]
311  *                              DrvOpenA                        [WINMM.@]
312  * (0,1,DRV_LOAD  ,0       ,0)
313  * (0,1,DRV_ENABLE,0       ,0)
314  * (0,1,DRV_OPEN  ,buf[256],0)
315  */
316 HDRVR WINAPI OpenDriverA(LPCSTR lpDriverName, LPCSTR lpSectionName, LPARAM lParam)
317 {
318     INT                 len;
319     LPWSTR              dn = NULL;
320     LPWSTR              sn = NULL;
321     HDRVR               ret = 0;
322
323     if (lpDriverName)
324     {
325         len = MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, NULL, 0 );
326         dn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
327         if (!dn) goto done;
328         MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, dn, len );
329     }
330
331     if (lpSectionName)
332     {
333         len = MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, NULL, 0 );
334         sn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
335         if (!sn) goto done;
336         MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, sn, len );
337     }
338
339     ret = OpenDriver(dn, sn, lParam);
340
341 done:
342     HeapFree(GetProcessHeap(), 0, dn);
343     HeapFree(GetProcessHeap(), 0, sn);
344     return ret;
345 }
346
347 /**************************************************************************
348  *                              OpenDriver                      [WINMM.@]
349  *                              DrvOpen                         [WINMM.@]
350  */
351 HDRVR WINAPI OpenDriver(LPCWSTR lpDriverName, LPCWSTR lpSectionName, LPARAM lParam)
352 {
353     LPWINE_DRIVER       lpDrv = NULL;
354     WCHAR               libName[128];
355     LPCWSTR             lsn = lpSectionName;
356
357     TRACE("(%s, %s, 0x%08lx);\n", 
358           debugstr_w(lpDriverName), debugstr_w(lpSectionName), lParam);
359
360     if (lsn == NULL) {
361         static const WCHAR wszDrivers32[] = {'D','r','i','v','e','r','s','3','2',0};
362         lstrcpynW(libName, lpDriverName, sizeof(libName) / sizeof(WCHAR));
363
364         if ((lpDrv = DRIVER_TryOpenDriver32(libName, lParam)))
365             goto the_end;
366         lsn = wszDrivers32;
367     }
368     if (DRIVER_GetLibName(lpDriverName, lsn, libName, sizeof(libName)) &&
369         (lpDrv = DRIVER_TryOpenDriver32(libName, lParam)))
370         goto the_end;
371
372     /* now we will try a 16 bit driver (and add all the glue to make it work... which
373      * is located in our mmsystem implementation)
374      * so ensure, we can load our mmsystem, otherwise just fail
375      */
376     WINMM_CheckForMMSystem();
377     if (pFnOpenDriver16 &&
378         (lpDrv = pFnOpenDriver16(lpDriverName, lpSectionName, lParam)))
379     {
380         if (DRIVER_AddToList(lpDrv, 0, lParam)) goto the_end;
381         HeapFree(GetProcessHeap(), 0, lpDrv);
382     }
383     TRACE("Failed to open driver %s from system.ini file, section %s\n", 
384           debugstr_w(lpDriverName), debugstr_w(lpSectionName));
385     return 0;
386
387  the_end:
388     if (lpDrv)  TRACE("=> %p\n", lpDrv);
389     return (HDRVR)lpDrv;
390 }
391
392 /**************************************************************************
393  *                      CloseDriver                             [WINMM.@]
394  *                      DrvClose                                [WINMM.@]
395  */
396 LRESULT WINAPI CloseDriver(HDRVR hDrvr, LPARAM lParam1, LPARAM lParam2)
397 {
398     LPWINE_DRIVER       lpDrv;
399
400     TRACE("(%p, %08lX, %08lX);\n", hDrvr, lParam1, lParam2);
401
402     if ((lpDrv = DRIVER_FindFromHDrvr(hDrvr)) != NULL)
403     {
404         if (lpDrv->dwFlags & WINE_GDF_16BIT)
405         {
406             if (pFnCloseDriver16)
407                 pFnCloseDriver16(lpDrv->d.d16.hDriver16, lParam1, lParam2);
408         }
409         else
410         {
411             DRIVER_SendMessage(lpDrv, DRV_CLOSE, lParam1, lParam2);
412             lpDrv->d.d32.dwDriverID = 0;
413         }
414         if (DRIVER_RemoveFromList(lpDrv)) {
415             if (!(lpDrv->dwFlags & WINE_GDF_16BIT))
416             {
417                 LPWINE_DRIVER       lpDrv0;
418
419                 /* if driver has an opened session instance, we have to close it too */
420                 if (DRIVER_GetNumberOfModuleRefs(lpDrv->d.d32.hModule, &lpDrv0) == 1)
421                 {
422                     DRIVER_SendMessage(lpDrv0, DRV_CLOSE, 0L, 0L);
423                     lpDrv0->d.d32.dwDriverID = 0;
424                     DRIVER_RemoveFromList(lpDrv0);
425                     FreeLibrary(lpDrv0->d.d32.hModule);
426                     HeapFree(GetProcessHeap(), 0, lpDrv0);
427                 }
428                 FreeLibrary(lpDrv->d.d32.hModule);
429             }
430             HeapFree(GetProcessHeap(), 0, lpDrv);
431             return TRUE;
432         }
433     }
434     WARN("Failed to close driver\n");
435     return FALSE;
436 }
437
438 /**************************************************************************
439  *                              GetDriverFlags          [WINMM.@]
440  * [in] hDrvr handle to the driver
441  *
442  * Returns:
443  *      0x00000000 if hDrvr is an invalid handle
444  *      0x80000000 if hDrvr is a valid 32 bit driver
445  *      0x90000000 if hDrvr is a valid 16 bit driver
446  *
447  * native WINMM doesn't return those flags
448  *      0x80000000 for a valid 32 bit driver and that's it
449  *      (I may have mixed up the two flags :-(
450  */
451 DWORD   WINAPI GetDriverFlags(HDRVR hDrvr)
452 {
453     LPWINE_DRIVER       lpDrv;
454     DWORD               ret = 0;
455
456     TRACE("(%p)\n", hDrvr);
457
458     if ((lpDrv = DRIVER_FindFromHDrvr(hDrvr)) != NULL) {
459         ret = WINE_GDF_EXIST | lpDrv->dwFlags;
460     }
461     return ret;
462 }
463
464 /**************************************************************************
465  *                              GetDriverModuleHandle   [WINMM.@]
466  *                              DrvGetModuleHandle      [WINMM.@]
467  */
468 HMODULE WINAPI GetDriverModuleHandle(HDRVR hDrvr)
469 {
470     LPWINE_DRIVER       lpDrv;
471     HMODULE             hModule = 0;
472
473     TRACE("(%p);\n", hDrvr);
474
475     if ((lpDrv = DRIVER_FindFromHDrvr(hDrvr)) != NULL) {
476         if (!(lpDrv->dwFlags & WINE_GDF_16BIT))
477             hModule = lpDrv->d.d32.hModule;
478     }
479     TRACE("=> %p\n", hModule);
480     return hModule;
481 }
482
483 /**************************************************************************
484  *                              DefDriverProc                     [WINMM.@]
485  *                              DrvDefDriverProc                  [WINMM.@]
486  */
487 LRESULT WINAPI DefDriverProc(DWORD_PTR dwDriverIdentifier, HDRVR hDrv,
488                              UINT Msg, LPARAM lParam1, LPARAM lParam2)
489 {
490     switch (Msg) {
491     case DRV_LOAD:
492     case DRV_FREE:
493     case DRV_ENABLE:
494     case DRV_DISABLE:
495         return 1;
496     case DRV_INSTALL:
497     case DRV_REMOVE:
498         return DRV_SUCCESS;
499     default:
500         return 0;
501     }
502 }
503
504 /**************************************************************************
505  *                              DriverCallback                  [WINMM.@]
506  */
507 BOOL WINAPI DriverCallback(DWORD dwCallBack, UINT uFlags, HDRVR hDev,
508                            UINT wMsg, DWORD dwUser, DWORD dwParam1,
509                            DWORD dwParam2)
510 {
511     TRACE("(%08lX, %04X, %p, %04X, %08lX, %08lX, %08lX); !\n",
512           dwCallBack, uFlags, hDev, wMsg, dwUser, dwParam1, dwParam2);
513
514     switch (uFlags & DCB_TYPEMASK) {
515     case DCB_NULL:
516         TRACE("Null !\n");
517         if (dwCallBack)
518             WARN("uFlags=%04X has null DCB value, but dwCallBack=%08lX is not null !\n", uFlags, dwCallBack);
519         break;
520     case DCB_WINDOW:
521         TRACE("Window(%04lX) handle=%p!\n", dwCallBack, hDev);
522         PostMessageA((HWND)dwCallBack, wMsg, (WPARAM)hDev, dwParam1);
523         break;
524     case DCB_TASK: /* aka DCB_THREAD */
525         TRACE("Task(%04lx) !\n", dwCallBack);
526         PostThreadMessageA(dwCallBack, wMsg, (WPARAM)hDev, dwParam1);
527         break;
528     case DCB_FUNCTION:
529         TRACE("Function (32 bit) !\n");
530         ((LPDRVCALLBACK)dwCallBack)(hDev, wMsg, dwUser, dwParam1, dwParam2);
531         break;
532     case DCB_EVENT:
533         TRACE("Event(%08lx) !\n", dwCallBack);
534         SetEvent((HANDLE)dwCallBack);
535         break;
536     case 6: /* I would dub it DCB_MMTHREADSIGNAL */
537         /* this is an undocumented DCB_ value used for mmThreads
538          * loword of dwCallBack contains the handle of the lpMMThd block
539          * which dwSignalCount has to be incremented
540          */     
541         if (pFnGetMMThread16)
542         {
543             WINE_MMTHREAD*      lpMMThd = pFnGetMMThread16(LOWORD(dwCallBack));
544
545             TRACE("mmThread (%04x, %p) !\n", LOWORD(dwCallBack), lpMMThd);
546             /* same as mmThreadSignal16 */
547             InterlockedIncrement(&lpMMThd->dwSignalCount);
548             SetEvent(lpMMThd->hEvent);
549             /* some other stuff on lpMMThd->hVxD */
550         }
551         break;
552 #if 0
553     case 4:
554         /* this is an undocumented DCB_ value for... I don't know */
555         break;
556 #endif
557     default:
558         WARN("Unknown callback type %d\n", uFlags & DCB_TYPEMASK);
559         return FALSE;
560     }
561     TRACE("Done\n");
562     return TRUE;
563 }
564
565 /******************************************************************
566  *              DRIVER_UnloadAll
567  *
568  *
569  */
570 void    DRIVER_UnloadAll(void)
571 {
572     LPWINE_DRIVER       lpDrv;
573     LPWINE_DRIVER       lpNextDrv = NULL;
574     unsigned            count = 0;
575
576     for (lpDrv = lpDrvItemList; lpDrv != NULL; lpDrv = lpNextDrv)
577     {
578         lpNextDrv = lpDrv->lpNextItem;
579         CloseDriver((HDRVR)lpDrv, 0, 0);
580         count++;
581     }
582     TRACE("Unloaded %u drivers\n", count);
583 }