560 lines
15 KiB
C
560 lines
15 KiB
C
|
/* Installable drivers for windows. Less common code.
|
||
|
*/
|
||
|
#include "user.h"
|
||
|
|
||
|
/*--------------------------------------------------------------------------*\
|
||
|
**
|
||
|
** NewSignalProc() -
|
||
|
**
|
||
|
\*--------------------------------------------------------------------------*/
|
||
|
#define SG_EXIT 0x0020
|
||
|
#define SG_LOAD_DLL 0x0040
|
||
|
#define SG_EXIT_DLL 0x0080
|
||
|
#define SG_GP_FAULT 0x0666
|
||
|
|
||
|
BOOL
|
||
|
CALLBACK NewSignalProc(
|
||
|
HTASK hTask,
|
||
|
WORD message,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
BOOL fRet;
|
||
|
|
||
|
// Notify installable drivers this app is going away.
|
||
|
if ( message == SG_EXIT || message == SG_GP_FAULT ) {
|
||
|
InternalBroadcastDriverMessage( NULL, DRV_EXITAPPLICATION,
|
||
|
(message == SG_GP_FAULT
|
||
|
? DRVEA_ABNORMALEXIT
|
||
|
: DRVEA_NORMALEXIT),
|
||
|
0L, IBDM_FIRSTINSTANCEONLY );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Pass notification on to WOW32 (which passes on to USER32)
|
||
|
//
|
||
|
|
||
|
fRet = SignalProc( hTask, message, wParam, lParam );
|
||
|
|
||
|
//
|
||
|
// After letting WOW32 and User32 cleanup, destroy the shadow
|
||
|
// message queue created by InitApp.
|
||
|
//
|
||
|
|
||
|
if ( message == SG_EXIT || message == SG_GP_FAULT ) {
|
||
|
DeleteQueue();
|
||
|
}
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
HINSTANCE LoadAliasedLibrary(LPCSTR szLibFileName,
|
||
|
LPCSTR szSection,
|
||
|
LPCSTR szIniFile,
|
||
|
LPSTR lpstrTail,
|
||
|
WORD cbTail)
|
||
|
{
|
||
|
char sz[128];
|
||
|
LPSTR pch;
|
||
|
OFSTRUCT os;
|
||
|
HFILE fd;
|
||
|
HINSTANCE h;
|
||
|
WORD errMode;
|
||
|
|
||
|
if (!szLibFileName || !*szLibFileName)
|
||
|
return((HINSTANCE)2); /* File not found */
|
||
|
|
||
|
/* read the filename and additional info. into sz
|
||
|
*/
|
||
|
GetPrivateProfileString(szSection, // ini section
|
||
|
szLibFileName, // key name
|
||
|
szLibFileName, // default if no match
|
||
|
sz, // return buffer
|
||
|
sizeof(sz), // return buffer size
|
||
|
szIniFile); // ini. file
|
||
|
|
||
|
sz[sizeof(sz)-1] = 0;
|
||
|
|
||
|
/* strip off the additional info. Remember, DS!=SS so we need to get a lpstr
|
||
|
* to our stack allocated sz.
|
||
|
*/
|
||
|
pch = (LPSTR)sz;
|
||
|
while (*pch)
|
||
|
{
|
||
|
if (*pch == ' ')
|
||
|
{
|
||
|
*pch++ = '\0';
|
||
|
break;
|
||
|
}
|
||
|
pch++;
|
||
|
}
|
||
|
|
||
|
// pch pts to ch after first space or null ch
|
||
|
|
||
|
fd = OpenFile(sz, &os, OF_EXIST|OF_SHARE_DENY_NONE);
|
||
|
if (HFILE_ERROR == fd)
|
||
|
return((HINSTANCE)2);
|
||
|
|
||
|
/* copy additional info. to lpstrTail
|
||
|
*/
|
||
|
|
||
|
if (lpstrTail && cbTail)
|
||
|
{
|
||
|
while (cbTail-- && (*lpstrTail++ = *pch++))
|
||
|
;
|
||
|
*(lpstrTail-1) = 0;
|
||
|
}
|
||
|
|
||
|
errMode = SetErrorMode(0x8001);
|
||
|
|
||
|
h = LoadLibrary(sz);
|
||
|
|
||
|
SetErrorMode(errMode);
|
||
|
|
||
|
return (h);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int GetDrvrUsage(HMODULE hModule)
|
||
|
/* effects: Runs through the driver list and figures out how many instances of
|
||
|
* this driver module handle we have. We use this instead of GetModuleUsage
|
||
|
* so that we can have drivers loaded as normal DLLs and as installable
|
||
|
* drivers.
|
||
|
*/
|
||
|
{
|
||
|
LPDRIVERTABLE lpdt;
|
||
|
int index;
|
||
|
int count;
|
||
|
|
||
|
if (!hInstalledDriverList || !cInstalledDrivers || !hModule)
|
||
|
return(0);
|
||
|
|
||
|
count = 0;
|
||
|
|
||
|
lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList,0);
|
||
|
|
||
|
for (index=0;index<cInstalledDrivers;index++)
|
||
|
{
|
||
|
if (lpdt->hModule==hModule)
|
||
|
count++;
|
||
|
|
||
|
lpdt++;
|
||
|
}
|
||
|
|
||
|
|
||
|
return(count);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL PASCAL CheckValidDriverProc(LPDRIVERTABLE lpdt, HDRVR hdrv)
|
||
|
/* effects: Some vendors shipped multimedia style installable drivers with
|
||
|
* bogus entry procs. This test checks for these bogus drivers and refuses to
|
||
|
* install them.
|
||
|
*/
|
||
|
{
|
||
|
WORD currentSP;
|
||
|
WORD saveSP;
|
||
|
|
||
|
_asm mov saveSP, sp
|
||
|
(void)(lpdt->lpDriverEntryPoint)(0, hdrv, 0, 0L, 0L);
|
||
|
_asm mov currentSP, sp
|
||
|
_asm mov sp, saveSP
|
||
|
|
||
|
if (saveSP != currentSP)
|
||
|
DebugErr(DBF_ERROR, "Invalid driver entry proc address");
|
||
|
|
||
|
return (saveSP == currentSP);
|
||
|
}
|
||
|
|
||
|
LRESULT FAR InternalLoadDriver(LPCSTR szDriverName,
|
||
|
LPCSTR szSectionName,
|
||
|
LPCSTR lpstrTail,
|
||
|
WORD cbTail,
|
||
|
BOOL fSendEnable)
|
||
|
{
|
||
|
int index;
|
||
|
int i;
|
||
|
LPDRIVERTABLE lpdt;
|
||
|
LPDRIVERTABLE lpdtBegin;
|
||
|
LRESULT result;
|
||
|
HGLOBAL h;
|
||
|
HINSTANCE hInstance;
|
||
|
char szDrivers[20];
|
||
|
char szSystemIni[20];
|
||
|
|
||
|
/* Drivers receive the following messages: if the driver was loaded,
|
||
|
* DRV_LOAD. If DRV_LOAD returns non-zero and fSendEnable, DRV_ENABLE.
|
||
|
*/
|
||
|
|
||
|
if (!hInstalledDriverList)
|
||
|
h = GlobalAlloc(GHND|GMEM_SHARE, (DWORD)((WORD)sizeof(DRIVERTABLE)));
|
||
|
else
|
||
|
/* Alloc space for the next driver we will install. We may not really
|
||
|
* install the driver in the last slot but rather in an intermediate
|
||
|
* slot which was freed.
|
||
|
*/
|
||
|
h = GlobalReAlloc(hInstalledDriverList,
|
||
|
(DWORD)((WORD)sizeof(DRIVERTABLE)*(cInstalledDrivers+1)),
|
||
|
GHND|GMEM_SHARE);
|
||
|
|
||
|
if (!h)
|
||
|
return(0L);
|
||
|
|
||
|
cInstalledDrivers++;
|
||
|
hInstalledDriverList = h;
|
||
|
|
||
|
if (!szSectionName)
|
||
|
LoadString(hInstanceWin, STR_DRIVERS, szDrivers, sizeof(szDrivers));
|
||
|
LoadString(hInstanceWin, STR_SYSTEMINI, szSystemIni, sizeof(szSystemIni));
|
||
|
|
||
|
lpdtBegin = lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList, NULL);
|
||
|
|
||
|
/* Find an empty driver entry */
|
||
|
for (i = 0; i < cInstalledDrivers; i++)
|
||
|
{
|
||
|
if (lpdt->hModule == NULL)
|
||
|
{
|
||
|
index = i;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
lpdt++;
|
||
|
}
|
||
|
|
||
|
if (index + 1 < cInstalledDrivers)
|
||
|
/* The driver went into an unused slot in the middle somewhere so
|
||
|
* decrement cInstalledDrivers count.
|
||
|
*/
|
||
|
cInstalledDrivers--;
|
||
|
|
||
|
/* Temporarly use an hModule to 1 to reserve this entry in case the driver
|
||
|
* loads another driver in its LibMain.
|
||
|
*/
|
||
|
lpdt->hModule = (HMODULE)1;
|
||
|
|
||
|
hInstance = LoadAliasedLibrary((LPSTR)szDriverName,
|
||
|
(LPSTR)(szSectionName ? szSectionName : szDrivers),
|
||
|
szSystemIni,
|
||
|
(LPSTR)lpstrTail,
|
||
|
cbTail);
|
||
|
if (hInstance < HINSTANCE_ERROR)
|
||
|
{
|
||
|
lpdt->hModule = NULL;
|
||
|
|
||
|
/* Load failed with an error. Return error code in highword.
|
||
|
*/
|
||
|
return(MAKELRESULT(0, hInstance));
|
||
|
}
|
||
|
|
||
|
(FARPROC)lpdt->lpDriverEntryPoint = GetProcAddress(hInstance, "DriverProc");
|
||
|
|
||
|
if (!lpdt->lpDriverEntryPoint)
|
||
|
{
|
||
|
FreeLibrary(hInstance);
|
||
|
lpdt->hModule = 0;
|
||
|
result = 0L;
|
||
|
goto LoadCleanUp;
|
||
|
}
|
||
|
|
||
|
lpdt->hModule = hInstance;
|
||
|
|
||
|
/* Save either the alias or filename of this driver. (depends on what the
|
||
|
* app passed to us to load it)
|
||
|
*/
|
||
|
lstrcpy(lpdt->szAliasName, szDriverName);
|
||
|
|
||
|
if (GetDrvrUsage(hInstance) == 1)
|
||
|
{
|
||
|
/* If this is the first instance, send the drv_load message. Don't use
|
||
|
* SendDriverMessage because we haven't initialized the linked list yet
|
||
|
*/
|
||
|
if (!CheckValidDriverProc(lpdt, (HDRVR)(index+1)) ||
|
||
|
!(lpdt->lpDriverEntryPoint)(lpdt->dwDriverIdentifier,
|
||
|
(HDRVR)(index+1),
|
||
|
DRV_LOAD,
|
||
|
0L, 0L))
|
||
|
{
|
||
|
/* Driver failed load call.
|
||
|
*/
|
||
|
lpdt->lpDriverEntryPoint = NULL;
|
||
|
lpdt->hModule = NULL;
|
||
|
FreeLibrary(hInstance);
|
||
|
result = 0L;
|
||
|
goto LoadCleanUp;
|
||
|
}
|
||
|
|
||
|
lpdt->fFirstEntry = 1;
|
||
|
}
|
||
|
|
||
|
/* Put driver in the load order linked list
|
||
|
*/
|
||
|
if (idFirstDriver == -1)
|
||
|
{
|
||
|
/* Initialize everything when first driver is loaded.
|
||
|
*/
|
||
|
idFirstDriver = index;
|
||
|
idLastDriver = index;
|
||
|
lpdt->idNextDriver = -1;
|
||
|
lpdt->idPrevDriver = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Insert this driver at the end of the load chain.
|
||
|
*/
|
||
|
lpdtBegin[idLastDriver].idNextDriver = index;
|
||
|
lpdt->idPrevDriver = idLastDriver;
|
||
|
lpdt->idNextDriver = -1;
|
||
|
idLastDriver = index;
|
||
|
}
|
||
|
|
||
|
if (fSendEnable && lpdt->fFirstEntry)
|
||
|
SendDriverMessage((HDRVR)(index+1), DRV_ENABLE, 0L, 0L);
|
||
|
|
||
|
result = MAKELRESULT(index+1, hInstance);
|
||
|
|
||
|
LoadCleanUp:
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
WORD FAR InternalFreeDriver(HDRVR hDriver, BOOL fSendDisable)
|
||
|
{
|
||
|
LPDRIVERTABLE lpdt;
|
||
|
WORD w;
|
||
|
int id;
|
||
|
|
||
|
/* The driver will receive the following message sequence:
|
||
|
*
|
||
|
* if usage count of driver is 1
|
||
|
* DRV_DISABLE (normally)
|
||
|
* DRV_FREE
|
||
|
*/
|
||
|
|
||
|
if ((int)hDriver > cInstalledDrivers || !hDriver)
|
||
|
return(0);
|
||
|
|
||
|
lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList,0);
|
||
|
|
||
|
if (!lpdt[(int)hDriver-1].hModule)
|
||
|
return(0);
|
||
|
|
||
|
/* If the driver usage count is 1, then send free and disable messages.
|
||
|
*/
|
||
|
|
||
|
/* Clear dwDriverIdentifier so that the sendmessage for DRV_OPEN and
|
||
|
* DRV_ENABLE have dwDriverIdentifier = 0 if an entry gets reused and so
|
||
|
* that the DRV_DISABLE and DRV_FREE messages below also get
|
||
|
* dwDriverIdentifier = 0.
|
||
|
*/
|
||
|
|
||
|
lpdt[(int)hDriver-1].dwDriverIdentifier = 0;
|
||
|
|
||
|
w = GetDrvrUsage(lpdt[(int)hDriver-1].hModule);
|
||
|
if (w == 1)
|
||
|
{
|
||
|
if (fSendDisable)
|
||
|
SendDriverMessage(hDriver, DRV_DISABLE, 0L, 0L);
|
||
|
SendDriverMessage(hDriver, DRV_FREE, 0L, 0L);
|
||
|
}
|
||
|
FreeLibrary(lpdt[(int)hDriver-1].hModule);
|
||
|
|
||
|
// Clear the rest of the table entry
|
||
|
|
||
|
lpdt[(int)hDriver-1].hModule = 0; // this indicates free entry
|
||
|
lpdt[(int)hDriver-1].fFirstEntry = 0; // this is also just to be tidy
|
||
|
lpdt[(int)hDriver-1].lpDriverEntryPoint = 0; // this is also just to be tidy
|
||
|
|
||
|
/* Fix up the driver load linked list */
|
||
|
if (idFirstDriver == (int)hDriver-1)
|
||
|
{
|
||
|
idFirstDriver = lpdt[(int)hDriver-1].idNextDriver;
|
||
|
if (idFirstDriver == -1)
|
||
|
{
|
||
|
/* No more drivers in the chain */
|
||
|
idFirstDriver = -1;
|
||
|
idLastDriver = -1;
|
||
|
cInstalledDrivers= 0;
|
||
|
goto Done;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Make prev entry of new first driver -1 */
|
||
|
lpdt[idFirstDriver].idPrevDriver = -1;
|
||
|
}
|
||
|
}
|
||
|
else if (idLastDriver == (int)hDriver-1)
|
||
|
{
|
||
|
/* We are freeing the last driver. So find a new last driver. */
|
||
|
idLastDriver = lpdt[(int)hDriver-1].idPrevDriver;
|
||
|
lpdt[idLastDriver].idNextDriver = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* We are freeing a driver in the middle of the list somewhere. */
|
||
|
id = lpdt[(int)hDriver-1].idPrevDriver;
|
||
|
lpdt[id].idNextDriver = lpdt[(int)hDriver-1].idNextDriver;
|
||
|
|
||
|
id = lpdt[(int)hDriver-1].idNextDriver;
|
||
|
lpdt[id].idPrevDriver = lpdt[(int)hDriver-1].idPrevDriver;
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
return(w-1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
LRESULT InternalOpenDriver(LPCSTR szDriverName,
|
||
|
LPCSTR szSectionName,
|
||
|
LPARAM lParam2,
|
||
|
BOOL fSendEnable)
|
||
|
{
|
||
|
HDRVR hDriver;
|
||
|
LPDRIVERTABLE lpdt;
|
||
|
LRESULT result;
|
||
|
char sz[128];
|
||
|
|
||
|
if (hDriver = (HDRVR)LOWORD(InternalLoadDriver(szDriverName, szSectionName,
|
||
|
sz, sizeof(sz), fSendEnable)))
|
||
|
{
|
||
|
/* Set the driver identifier to the DRV_OPEN call to the driver
|
||
|
* handle. This will let people build helper functions that the driver
|
||
|
* can call with a unique identifier if they want to.
|
||
|
*/
|
||
|
|
||
|
lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList,0);
|
||
|
|
||
|
lpdt[(int)hDriver-1].dwDriverIdentifier = (DWORD)(WORD)hDriver;
|
||
|
|
||
|
result = SendDriverMessage(hDriver,
|
||
|
DRV_OPEN,
|
||
|
(LPARAM)(LPSTR)sz,
|
||
|
lParam2);
|
||
|
if (!result)
|
||
|
InternalFreeDriver(hDriver, fSendEnable);
|
||
|
else
|
||
|
{
|
||
|
lpdt = (LPDRIVERTABLE)MAKELONG(0,hInstalledDriverList);
|
||
|
|
||
|
lpdt[(int)hDriver-1].dwDriverIdentifier = (DWORD)result;
|
||
|
|
||
|
result = (LRESULT)(DWORD)(WORD)hDriver;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
result = 0L;
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
|
||
|
LRESULT InternalCloseDriver(HDRVR hDriver, LPARAM lParam1, LPARAM lParam2, BOOL fSendDisable)
|
||
|
{
|
||
|
LPDRIVERTABLE lpdt;
|
||
|
LRESULT result;
|
||
|
int index;
|
||
|
BOOL f;
|
||
|
HMODULE hm;
|
||
|
|
||
|
// check handle in valid range.
|
||
|
|
||
|
if ((int)hDriver > cInstalledDrivers)
|
||
|
return(FALSE);
|
||
|
|
||
|
lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList,0);
|
||
|
|
||
|
if (!lpdt[(int)hDriver-1].hModule)
|
||
|
return(FALSE);
|
||
|
|
||
|
result = SendDriverMessage(hDriver, DRV_CLOSE, lParam1, lParam2);
|
||
|
|
||
|
if (result)
|
||
|
{
|
||
|
// Driver didn't abort close
|
||
|
|
||
|
f = lpdt[(int)hDriver-1].fFirstEntry;
|
||
|
hm = lpdt[(int)hDriver-1].hModule;
|
||
|
|
||
|
if (InternalFreeDriver(hDriver, fSendDisable) && f)
|
||
|
{
|
||
|
lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList,0);
|
||
|
|
||
|
/* Only one entry for the driver in the driver list has the first
|
||
|
* instance flag set. This is to make it easier to handle system
|
||
|
* messages that only need to be sent to a driver once.
|
||
|
*
|
||
|
* To maintain the flag, we must set the flag in one of the other
|
||
|
* entries if we remove the driver entry with the flag set.
|
||
|
*
|
||
|
* Note that InternalFreeDriver returns the new usage count of
|
||
|
* the driver so if it is zero, we know that there are no other
|
||
|
* entries for the driver in the list and so we don't have to
|
||
|
* do this loop.
|
||
|
*/
|
||
|
|
||
|
for (index=0;index<cInstalledDrivers;index++)
|
||
|
if (lpdt[index].hModule == hm && !lpdt[index].fFirstEntry)
|
||
|
{
|
||
|
lpdt[index].fFirstEntry = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
|
||
|
HDRVR API IOpenDriver(LPCSTR szDriverName, LPCSTR szSectionName, LPARAM lParam)
|
||
|
{
|
||
|
LRESULT result;
|
||
|
|
||
|
/* The driver receives the following messages when it is opened. If it isn't
|
||
|
* loaded, the library is loaded and the DRV_LOAD message is sent. If
|
||
|
* DRV_LOAD returns nonzero, the DRV_ENABLE message is sent. Once the
|
||
|
* driver is loaded or if it was previously loaded, the DRV_OPEN message is
|
||
|
* sent.
|
||
|
*/
|
||
|
result = InternalOpenDriver(szDriverName, szSectionName, lParam, TRUE);
|
||
|
|
||
|
return((HDRVR)LOWORD(result));
|
||
|
}
|
||
|
|
||
|
|
||
|
LRESULT API ICloseDriver(HDRVR hDriver, LPARAM lParam1, LPARAM lParam2)
|
||
|
{
|
||
|
/* The driver will receive the following message sequence:
|
||
|
*
|
||
|
* DRV_CLOSE
|
||
|
* if DRV_CLOSE returns non-zero
|
||
|
* if driver usage count = 1
|
||
|
* DRV_DISABLE
|
||
|
* DRV_FREE
|
||
|
*/
|
||
|
|
||
|
return(InternalCloseDriver(hDriver, lParam1, lParam2, TRUE));
|
||
|
}
|
||
|
|
||
|
|
||
|
HINSTANCE API IGetDriverModuleHandle(HDRVR hDriver)
|
||
|
/* effects: Returns the module handle associated with the given driver ID.
|
||
|
*/
|
||
|
{
|
||
|
LPDRIVERTABLE lpdt;
|
||
|
HINSTANCE hModule = NULL;
|
||
|
|
||
|
if (hDriver && ((int)hDriver <= cInstalledDrivers))
|
||
|
{
|
||
|
lpdt = (LPDRIVERTABLE)MAKELP(hInstalledDriverList,0);
|
||
|
|
||
|
return lpdt[(int)hDriver-1].hModule;
|
||
|
}
|
||
|
else
|
||
|
return NULL;
|
||
|
}
|