/* 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;indexhModule==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