2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/vddsup.c
5 * PURPOSE: Virtual Device Drivers (VDD) Support
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 /* INCLUDES *******************************************************************/
20 typedef VOID (WINAPI
*VDD_PROC
)(VOID
);
22 typedef struct _VDD_MODULE
25 VDD_PROC DispatchRoutine
;
26 } VDD_MODULE
, *PVDD_MODULE
;
28 // WARNING: A structure with the same name exists in nt_vdd.h,
29 // however it is not declared because its inclusion was prevented
30 // with #define NO_NTVDD_COMPAT, see ntvdm.h
31 typedef struct _VDD_USER_HANDLERS
36 PFNVDD_UCREATE Ucr_Handler
;
37 PFNVDD_UTERMINATE Uterm_Handler
;
38 PFNVDD_UBLOCK Ublock_Handler
;
39 PFNVDD_URESUME Uresume_Handler
;
40 } VDD_USER_HANDLERS
, *PVDD_USER_HANDLERS
;
42 /* PRIVATE VARIABLES **********************************************************/
44 // TODO: Maybe use a linked list.
45 // But the number of elements must be <= MAXUSHORT (MAXWORD)
46 #define MAX_VDD_MODULES 0xFF + 1
47 static VDD_MODULE VDDList
[MAX_VDD_MODULES
] = {{NULL
}};
49 // Valid handles of VDD DLLs start at 1 and finish at MAX_VDD_MODULES
50 #define ENTRY_TO_HANDLE(Entry) ((Entry) + 1)
51 #define HANDLE_TO_ENTRY(Handle) ((Handle) - 1)
52 #define IS_VALID_HANDLE(Handle) ((Handle) > 0 && (Handle) <= MAX_VDD_MODULES)
54 static LIST_ENTRY VddUserHooksList
= {&VddUserHooksList
, &VddUserHooksList
};
56 /* PRIVATE FUNCTIONS **********************************************************/
58 static USHORT
GetNextFreeVDDEntry(VOID
)
60 USHORT Entry
= MAX_VDD_MODULES
;
61 for (Entry
= 0; Entry
< ARRAYSIZE(VDDList
); ++Entry
)
63 if (VDDList
[Entry
].hDll
== NULL
) break;
68 static VOID WINAPI
ThirdPartyVDDBop(LPWORD Stack
)
70 /* Get the Function Number and skip it */
71 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
82 LPCSTR DllName
= NULL
,
83 InitRoutineName
= NULL
,
84 DispatchRoutineName
= NULL
;
86 VDD_PROC InitRoutine
= NULL
,
87 DispatchRoutine
= NULL
;
89 DPRINT("RegisterModule() called\n");
91 /* Clear the Carry Flag (no error happened so far) */
94 /* Retrieve the next free entry in the table (used later on) */
95 Entry
= GetNextFreeVDDEntry();
96 if (Entry
>= MAX_VDD_MODULES
)
98 DPRINT1("Failed to create a new VDD module entry\n");
104 /* Retrieve the VDD name in DS:SI */
105 DllName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI());
107 /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */
108 if (TO_LINEAR(getES(), getDI()) != 0)
109 InitRoutineName
= (LPCSTR
)SEG_OFF_TO_PTR(getES(), getDI());
111 /* Retrieve the dispatch routine API name in DS:BX */
112 DispatchRoutineName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getBX());
114 DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n",
115 (DllName
? DllName
: "n/a"),
116 (InitRoutineName
? InitRoutineName
: "n/a"),
117 (DispatchRoutineName
? DispatchRoutineName
: "n/a"));
119 /* Load the VDD DLL */
120 hDll
= LoadLibraryA(DllName
);
123 DWORD LastError
= GetLastError();
126 if (LastError
== ERROR_NOT_ENOUGH_MEMORY
)
128 DPRINT1("Not enough memory to load DLL '%s'\n", DllName
);
134 DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName
, LastError
);
140 /* Load the initialization routine if needed */
143 InitRoutine
= (VDD_PROC
)GetProcAddress(hDll
, InitRoutineName
);
144 if (InitRoutine
== NULL
)
146 DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName
);
153 /* Load the dispatch routine */
154 DispatchRoutine
= (VDD_PROC
)GetProcAddress(hDll
, DispatchRoutineName
);
155 if (DispatchRoutine
== NULL
)
157 DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName
);
163 /* If we reached this point, that means everything is OK */
165 /* Register the VDD DLL */
166 VDDList
[Entry
].hDll
= hDll
;
167 VDDList
[Entry
].DispatchRoutine
= DispatchRoutine
;
169 /* Call the initialization routine if needed */
170 if (InitRoutine
) InitRoutine();
172 /* We succeeded. RetVal will contain a valid VDD DLL handle */
174 RetVal
= ENTRY_TO_HANDLE(Entry
); // Convert the entry to a valid handle
179 /* Unload the VDD DLL */
180 if (hDll
) FreeLibrary(hDll
);
182 /* Set the Carry Flag to indicate that an error happened */
187 // /* Clear the Carry Flag (success) */
194 /* UnRegisterModule */
197 WORD Handle
= getAX();
198 WORD Entry
= HANDLE_TO_ENTRY(Handle
); // Convert the handle to a valid entry
200 DPRINT("UnRegisterModule() called\n");
203 if (!IS_VALID_HANDLE(Handle
) || VDDList
[Entry
].hDll
== NULL
)
205 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry
);
211 /* Unregister the VDD DLL */
212 FreeLibrary(VDDList
[Entry
].hDll
);
213 VDDList
[Entry
].hDll
= NULL
;
214 VDDList
[Entry
].DispatchRoutine
= NULL
;
221 WORD Handle
= getAX();
222 WORD Entry
= HANDLE_TO_ENTRY(Handle
); // Convert the handle to a valid entry
224 DPRINT("DispatchCall() called\n");
227 if (!IS_VALID_HANDLE(Handle
) ||
228 VDDList
[Entry
].hDll
== NULL
||
229 VDDList
[Entry
].DispatchRoutine
== NULL
)
231 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry
);
237 /* Call the dispatch routine */
238 VDDList
[Entry
].DispatchRoutine();
244 DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum
);
251 static BOOL
LoadInstallableVDD(VOID
)
253 // FIXME: These strings should be localized.
254 #define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers."
255 #define ERROR_REGVDD L"Virtual Device Driver format in the registry is invalid."
256 #define ERROR_LOADVDD L"An installable Virtual Device Driver failed Dll initialization."
264 LPCWSTR VDDKeyName
= L
"SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers";
265 LPWSTR VDDValueName
= L
"VDD";
266 LPWSTR VDDList
= NULL
;
270 /* Try to open the VDD registry key */
271 Error
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
276 if (Error
== ERROR_FILE_NOT_FOUND
)
278 /* If the key just doesn't exist, don't do anything else */
281 else if (Error
!= ERROR_SUCCESS
)
283 /* The key exists but there was an access error: display an error and quit */
284 DisplayMessage(ERROR_REGVDD
);
289 * Retrieve the size of the VDD registry value
290 * and check that it's of REG_MULTI_SZ type.
292 Error
= RegQueryValueExW(hVDDKey
,
298 if (Error
== ERROR_FILE_NOT_FOUND
)
300 /* If the value just doesn't exist, don't do anything else */
304 else if (Error
!= ERROR_SUCCESS
|| Type
!= REG_MULTI_SZ
)
307 * The value exists but there was an access error or
308 * is of the wrong type: display an error and quit.
310 DisplayMessage(ERROR_REGVDD
);
315 /* Allocate the buffer */
316 BufSize
= (BufSize
< 2*sizeof(WCHAR
) ? 2*sizeof(WCHAR
) : BufSize
);
317 VDDList
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, BufSize
);
320 DisplayMessage(ERROR_MEMORYVDD
);
325 /* Retrieve the list of VDDs to load */
326 if (RegQueryValueExW(hVDDKey
,
331 &BufSize
) != ERROR_SUCCESS
)
333 DisplayMessage(ERROR_REGVDD
);
339 VDDValueName
= VDDList
;
342 DPRINT1("Loading VDD '%S'...", VDDList
);
343 hVDD
= LoadLibraryW(VDDList
);
346 DbgPrint("Failed\n");
347 DisplayMessage(ERROR_LOADVDD
);
351 DbgPrint("Succeeded\n");
353 /* Go to next string */
354 VDDList
+= wcslen(VDDList
) + 1;
356 VDDList
= VDDValueName
;
359 if (VDDList
) RtlFreeHeap(RtlGetProcessHeap(), 0, VDDList
);
360 RegCloseKey(hVDDKey
);
364 /* PUBLIC FUNCTIONS ***********************************************************/
367 * NOTE: This function can be called multiple times by the same VDD, if
368 * it wants to install different hooks for a same action. The most recent
369 * registered hooks are called first.
373 VDDInstallUserHook(IN HANDLE hVdd
,
374 IN PFNVDD_UCREATE Ucr_Handler
,
375 IN PFNVDD_UTERMINATE Uterm_Handler
,
376 IN PFNVDD_UBLOCK Ublock_Handler
,
377 IN PFNVDD_URESUME Uresume_Handler
)
379 PVDD_USER_HANDLERS UserHook
;
381 /* Check validity of the VDD handle */
382 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
384 SetLastError(ERROR_INVALID_PARAMETER
);
388 // NOTE: If we want that a VDD can install hooks only once, it's here
389 // that we need to check whether a hook entry is already registered.
391 /* Create and initialize a new hook entry... */
392 UserHook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*UserHook
));
393 if (UserHook
== NULL
)
395 SetLastError(ERROR_OUTOFMEMORY
);
399 UserHook
->hVdd
= hVdd
;
400 UserHook
->Ucr_Handler
= Ucr_Handler
;
401 UserHook
->Uterm_Handler
= Uterm_Handler
;
402 UserHook
->Ublock_Handler
= Ublock_Handler
;
403 UserHook
->Uresume_Handler
= Uresume_Handler
;
405 /* ... and add it at the top of the list of hooks */
406 InsertHeadList(&VddUserHooksList
, &UserHook
->Entry
);
412 * NOTE: This function uninstalls the latest installed hooks for a given VDD.
413 * It can be called multiple times by the same VDD to uninstall many hooks
414 * installed by multiple invocations of VDDInstallUserHook.
418 VDDDeInstallUserHook(IN HANDLE hVdd
)
421 PVDD_USER_HANDLERS UserHook
;
423 /* Check validity of the VDD handle */
424 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
426 SetLastError(ERROR_INVALID_PARAMETER
);
430 /* Uninstall the latest installed hooks */
431 for (Pointer
= VddUserHooksList
.Flink
; Pointer
!= &VddUserHooksList
; Pointer
= Pointer
->Flink
)
433 UserHook
= CONTAINING_RECORD(Pointer
, VDD_USER_HANDLERS
, Entry
);
434 if (UserHook
->hVdd
== hVdd
)
436 RemoveEntryList(&UserHook
->Entry
);
437 RtlFreeHeap(RtlGetProcessHeap(), 0, UserHook
);
442 SetLastError(ERROR_INVALID_PARAMETER
);
447 * Internal functions for calling the VDD user hooks.
448 * Their names come directly from the Windows 2kX DDK.
451 VOID
VDDCreateUserHook(USHORT DosPDB
)
454 PVDD_USER_HANDLERS UserHook
;
456 /* Call the hooks starting from the most recent ones */
457 for (Pointer
= VddUserHooksList
.Flink
; Pointer
!= &VddUserHooksList
; Pointer
= Pointer
->Flink
)
459 UserHook
= CONTAINING_RECORD(Pointer
, VDD_USER_HANDLERS
, Entry
);
460 if (UserHook
->Ucr_Handler
) UserHook
->Ucr_Handler(DosPDB
);
464 VOID
VDDTerminateUserHook(USHORT DosPDB
)
467 PVDD_USER_HANDLERS UserHook
;
469 /* Call the hooks starting from the most recent ones */
470 for (Pointer
= VddUserHooksList
.Flink
; Pointer
!= &VddUserHooksList
; Pointer
= Pointer
->Flink
)
472 UserHook
= CONTAINING_RECORD(Pointer
, VDD_USER_HANDLERS
, Entry
);
473 if (UserHook
->Uterm_Handler
) UserHook
->Uterm_Handler(DosPDB
);
477 VOID
VDDBlockUserHook(VOID
)
480 PVDD_USER_HANDLERS UserHook
;
482 /* Call the hooks starting from the most recent ones */
483 for (Pointer
= VddUserHooksList
.Flink
; Pointer
!= &VddUserHooksList
; Pointer
= Pointer
->Flink
)
485 UserHook
= CONTAINING_RECORD(Pointer
, VDD_USER_HANDLERS
, Entry
);
486 if (UserHook
->Ublock_Handler
) UserHook
->Ublock_Handler();
490 VOID
VDDResumeUserHook(VOID
)
493 PVDD_USER_HANDLERS UserHook
;
495 /* Call the hooks starting from the most recent ones */
496 for (Pointer
= VddUserHooksList
.Flink
; Pointer
!= &VddUserHooksList
; Pointer
= Pointer
->Flink
)
498 UserHook
= CONTAINING_RECORD(Pointer
, VDD_USER_HANDLERS
, Entry
);
499 if (UserHook
->Uresume_Handler
) UserHook
->Uresume_Handler();
505 VOID
VDDSupInitialize(VOID
)
507 /* Register the 3rd-party VDD BOP Handler */
508 RegisterBop(BOP_3RDPARTY
, ThirdPartyVDDBop
);
510 /* Load the installable VDDs from the registry */
511 LoadInstallableVDD();