kernel32: FindFirstChangeNotification needs a static IO_STATUS_BLOCK.
[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_PAGE_FAULT
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         lpDrv->dwFlags |= WINE_GDF_SESSION;
295         return ret;
296     }
297
298     if (!DRIVER_AddToList(lpDrv, (LPARAM)ptr, lParam2))
299     {cause = "load failed"; goto exit;}
300
301     TRACE("=> %p\n", lpDrv);
302     return lpDrv;
303  exit:
304     FreeLibrary(hModule);
305     HeapFree(GetProcessHeap(), 0, lpDrv);
306     TRACE("Unable to load 32 bit module %s: %s\n", debugstr_w(fn), cause);
307     return NULL;
308 }
309
310 /**************************************************************************
311  *                              OpenDriverA                     [WINMM.@]
312  *                              DrvOpenA                        [WINMM.@]
313  * (0,1,DRV_LOAD  ,0       ,0)
314  * (0,1,DRV_ENABLE,0       ,0)
315  * (0,1,DRV_OPEN  ,buf[256],0)
316  */
317 HDRVR WINAPI OpenDriverA(LPCSTR lpDriverName, LPCSTR lpSectionName, LPARAM lParam)
318 {
319     INT                 len;
320     LPWSTR              dn = NULL;
321     LPWSTR              sn = NULL;
322     HDRVR               ret = 0;
323
324     if (lpDriverName)
325     {
326         len = MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, NULL, 0 );
327         dn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
328         if (!dn) goto done;
329         MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, dn, len );
330     }
331
332     if (lpSectionName)
333     {
334         len = MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, NULL, 0 );
335         sn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
336         if (!sn) goto done;
337         MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, sn, len );
338     }
339
340     ret = OpenDriver(dn, sn, lParam);
341
342 done:
343     HeapFree(GetProcessHeap(), 0, dn);
344     HeapFree(GetProcessHeap(), 0, sn);
345     return ret;
346 }
347
348 /**************************************************************************
349  *                              OpenDriver                      [WINMM.@]
350  *                              DrvOpen                         [WINMM.@]
351  */
352 HDRVR WINAPI OpenDriver(LPCWSTR lpDriverName, LPCWSTR lpSectionName, LPARAM lParam)
353 {
354     LPWINE_DRIVER       lpDrv = NULL;
355     WCHAR               libName[128];
356     LPCWSTR             lsn = lpSectionName;
357
358     TRACE("(%s, %s, 0x%08lx);\n", 
359           debugstr_w(lpDriverName), debugstr_w(lpSectionName), lParam);
360
361     if (lsn == NULL) {
362         static const WCHAR wszDrivers32[] = {'D','r','i','v','e','r','s','3','2',0};
363         lstrcpynW(libName, lpDriverName, sizeof(libName) / sizeof(WCHAR));
364
365         if ((lpDrv = DRIVER_TryOpenDriver32(libName, lParam)))
366             goto the_end;
367         lsn = wszDrivers32;
368     }
369     if (DRIVER_GetLibName(lpDriverName, lsn, libName, sizeof(libName)) &&
370         (lpDrv = DRIVER_TryOpenDriver32(libName, lParam)))
371         goto the_end;
372
373     /* now we will try a 16 bit driver (and add all the glue to make it work... which
374      * is located in our mmsystem implementation)
375      * so ensure, we can load our mmsystem, otherwise just fail
376      */
377     WINMM_CheckForMMSystem();
378     if (pFnOpenDriver16 &&
379         (lpDrv = pFnOpenDriver16(lpDriverName, lpSectionName, lParam)))
380     {
381         if (DRIVER_AddToList(lpDrv, 0, lParam)) goto the_end;
382         HeapFree(GetProcessHeap(), 0, lpDrv);
383     }
384     TRACE("Failed to open driver %s from system.ini file, section %s\n", 
385           debugstr_w(lpDriverName), debugstr_w(lpSectionName));
386     return 0;
387
388  the_end:
389     if (lpDrv)  TRACE("=> %p\n", lpDrv);
390     return (HDRVR)lpDrv;
391 }
392
393 /**************************************************************************
394  *                      CloseDriver                             [WINMM.@]
395  *                      DrvClose                                [WINMM.@]
396  */
397 LRESULT WINAPI CloseDriver(HDRVR hDrvr, LPARAM lParam1, LPARAM lParam2)
398 {
399     LPWINE_DRIVER       lpDrv;
400
401     TRACE("(%p, %08lX, %08lX);\n", hDrvr, lParam1, lParam2);
402
403     if ((lpDrv = DRIVER_FindFromHDrvr(hDrvr)) != NULL)
404     {
405         if (lpDrv->dwFlags & WINE_GDF_16BIT)
406         {
407             if (pFnCloseDriver16)
408                 pFnCloseDriver16(lpDrv->d.d16.hDriver16, lParam1, lParam2);
409         }
410         else
411         {
412             DRIVER_SendMessage(lpDrv, DRV_CLOSE, lParam1, lParam2);
413             lpDrv->d.d32.dwDriverID = 0;
414         }
415         if (DRIVER_RemoveFromList(lpDrv)) {
416             if (!(lpDrv->dwFlags & WINE_GDF_16BIT))
417             {
418                 LPWINE_DRIVER       lpDrv0;
419
420                 if (lpDrv->dwFlags & WINE_GDF_SESSION)
421                     FIXME("Shouldn't happen (%p)\n", lpDrv);
422                 /* if driver has an opened session instance, we have to close it too */
423                 if (DRIVER_GetNumberOfModuleRefs(lpDrv->d.d32.hModule, &lpDrv0) == 1 &&
424                     (lpDrv0->dwFlags & WINE_GDF_SESSION))
425                 {
426                     DRIVER_SendMessage(lpDrv0, DRV_CLOSE, 0L, 0L);
427                     lpDrv0->d.d32.dwDriverID = 0;
428                     DRIVER_RemoveFromList(lpDrv0);
429                     FreeLibrary(lpDrv0->d.d32.hModule);
430                     HeapFree(GetProcessHeap(), 0, lpDrv0);
431                 }
432                 FreeLibrary(lpDrv->d.d32.hModule);
433             }
434             HeapFree(GetProcessHeap(), 0, lpDrv);
435             return TRUE;
436         }
437     }
438     WARN("Failed to close driver\n");
439     return FALSE;
440 }
441
442 /**************************************************************************
443  *                              GetDriverFlags          [WINMM.@]
444  * [in] hDrvr handle to the driver
445  *
446  * Returns:
447  *      0x00000000 if hDrvr is an invalid handle
448  *      0x80000000 if hDrvr is a valid 32 bit driver
449  *      0x90000000 if hDrvr is a valid 16 bit driver
450  *
451  * native WINMM doesn't return those flags
452  *      0x80000000 for a valid 32 bit driver and that's it
453  *      (I may have mixed up the two flags :-(
454  */
455 DWORD   WINAPI GetDriverFlags(HDRVR hDrvr)
456 {
457     LPWINE_DRIVER       lpDrv;
458     DWORD               ret = 0;
459
460     TRACE("(%p)\n", hDrvr);
461
462     if ((lpDrv = DRIVER_FindFromHDrvr(hDrvr)) != NULL) {
463         ret = WINE_GDF_EXIST | (lpDrv->dwFlags & WINE_GDF_EXTERNAL_MASK);
464     }
465     return ret;
466 }
467
468 /**************************************************************************
469  *                              GetDriverModuleHandle   [WINMM.@]
470  *                              DrvGetModuleHandle      [WINMM.@]
471  */
472 HMODULE WINAPI GetDriverModuleHandle(HDRVR hDrvr)
473 {
474     LPWINE_DRIVER       lpDrv;
475     HMODULE             hModule = 0;
476
477     TRACE("(%p);\n", hDrvr);
478
479     if ((lpDrv = DRIVER_FindFromHDrvr(hDrvr)) != NULL) {
480         if (!(lpDrv->dwFlags & WINE_GDF_16BIT))
481             hModule = lpDrv->d.d32.hModule;
482     }
483     TRACE("=> %p\n", hModule);
484     return hModule;
485 }
486
487 /**************************************************************************
488  *                              DefDriverProc                     [WINMM.@]
489  *                              DrvDefDriverProc                  [WINMM.@]
490  */
491 LRESULT WINAPI DefDriverProc(DWORD_PTR dwDriverIdentifier, HDRVR hDrv,
492                              UINT Msg, LPARAM lParam1, LPARAM lParam2)
493 {
494     switch (Msg) {
495     case DRV_LOAD:
496     case DRV_FREE:
497     case DRV_ENABLE:
498     case DRV_DISABLE:
499         return 1;
500     case DRV_INSTALL:
501     case DRV_REMOVE:
502         return DRV_SUCCESS;
503     default:
504         return 0;
505     }
506 }
507
508 /**************************************************************************
509  *                              DriverCallback                  [WINMM.@]
510  */
511 BOOL WINAPI DriverCallback(DWORD dwCallBack, UINT uFlags, HDRVR hDev,
512                            UINT wMsg, DWORD dwUser, DWORD dwParam1,
513                            DWORD dwParam2)
514 {
515     TRACE("(%08lX, %04X, %p, %04X, %08lX, %08lX, %08lX); !\n",
516           dwCallBack, uFlags, hDev, wMsg, dwUser, dwParam1, dwParam2);
517
518     switch (uFlags & DCB_TYPEMASK) {
519     case DCB_NULL:
520         TRACE("Null !\n");
521         if (dwCallBack)
522             WARN("uFlags=%04X has null DCB value, but dwCallBack=%08lX is not null !\n", uFlags, dwCallBack);
523         break;
524     case DCB_WINDOW:
525         TRACE("Window(%04lX) handle=%p!\n", dwCallBack, hDev);
526         PostMessageA((HWND)dwCallBack, wMsg, (WPARAM)hDev, dwParam1);
527         break;
528     case DCB_TASK: /* aka DCB_THREAD */
529         TRACE("Task(%04lx) !\n", dwCallBack);
530         PostThreadMessageA(dwCallBack, wMsg, (WPARAM)hDev, dwParam1);
531         break;
532     case DCB_FUNCTION:
533         TRACE("Function (32 bit) !\n");
534         ((LPDRVCALLBACK)dwCallBack)(hDev, wMsg, dwUser, dwParam1, dwParam2);
535         break;
536     case DCB_EVENT:
537         TRACE("Event(%08lx) !\n", dwCallBack);
538         SetEvent((HANDLE)dwCallBack);
539         break;
540     case 6: /* I would dub it DCB_MMTHREADSIGNAL */
541         /* this is an undocumented DCB_ value used for mmThreads
542          * loword of dwCallBack contains the handle of the lpMMThd block
543          * which dwSignalCount has to be incremented
544          */     
545         if (pFnGetMMThread16)
546         {
547             WINE_MMTHREAD*      lpMMThd = pFnGetMMThread16(LOWORD(dwCallBack));
548
549             TRACE("mmThread (%04x, %p) !\n", LOWORD(dwCallBack), lpMMThd);
550             /* same as mmThreadSignal16 */
551             InterlockedIncrement(&lpMMThd->dwSignalCount);
552             SetEvent(lpMMThd->hEvent);
553             /* some other stuff on lpMMThd->hVxD */
554         }
555         break;
556 #if 0
557     case 4:
558         /* this is an undocumented DCB_ value for... I don't know */
559         break;
560 #endif
561     default:
562         WARN("Unknown callback type %d\n", uFlags & DCB_TYPEMASK);
563         return FALSE;
564     }
565     TRACE("Done\n");
566     return TRUE;
567 }
568
569 /******************************************************************
570  *              DRIVER_UnloadAll
571  *
572  *
573  */
574 void    DRIVER_UnloadAll(void)
575 {
576     LPWINE_DRIVER       lpDrv;
577     LPWINE_DRIVER       lpNextDrv = NULL;
578     unsigned            count = 0;
579
580     for (lpDrv = lpDrvItemList; lpDrv != NULL; lpDrv = lpNextDrv)
581     {
582         lpNextDrv = lpDrv->lpNextItem;
583         CloseDriver((HDRVR)lpDrv, 0, 0);
584         count++;
585     }
586     TRACE("Unloaded %u drivers\n", count);
587 }