Introduction
This article explains:
- How to identify third party “.inf” files related to a “.sys” file
- How to uninstall/force uninstall selected inf file
How the application works
During third party driver installation, related inf files get renamed to Oemxx.inf and get installed
in the “Windows Root\Inf” folder (e.g.: C:\Windows\Inf).
Windows also creates a .PNF file and stores it at the same location. To identify a driver for a device,
the OS goes through these inf files.
This application uses the above mentioned information to figure out the oem*.inf files that
are related to a driver and list them in the listview.
These files can be opened in Notepad by right clicking on their entry in listview.
A file in the list can be right clicked in the listview and uninstalled as well.
When a sys file is selected, the application iterates through the inf folder mentioned above and searches for files using
the following search wild card: Oem*.inf.
Each of the inf file obtained in the search is opened and the following section and key are searched for in the inf file:
[SourceDisksFile]
section is searched- Under the
[SourceDisksFile]
section, a key with the .sys file name selected is searched
For example, if the .sys file selected is “scvscv.sys”, then we open the first
Oemxx.inf file obtained in the search and looks for the [SourceDisksFiles]
section. Under that section, the application looks for a key with name “scvscv.sys”. If we are able to get a value for this key, then we know that this inf file is related to
the selected driver “scvscv.sys”.
The user can then right click on the inf file shown in the list and view the content in the richedit box on the right bottom of the screen or open it in
Notepad by right clicking on the particular entry in listview. Once the user is sure that the
inf can be uninstalled, they can right click and select Uninstall.
In some cases, it might not be possible to uninstall the inf file as it might be in use. In such cases, if the user is sure that the
inf file can be uninstalled, they can select the checkbox “Force Inf Uninstall”. This would ensure that the
inf file would get uninstalled forcefully. A screenshot of the UI is shown below:
Only Oemxx.inf files are searched as these are the ones that will be part of
the third party driver installation. Microsoft suggests leaving the driver file untouched even after uninstalling
the inf file as the driver files might be used by other applications. Due to this reason, this application will not delete the selected sys file.
Classes to accomplish the task
Entire logic to accomplish the task mentioned in this article is enveloped in these three classes;
CInfNDriverDelete
, CInfNDriverDeleteData
, and CInfNDriverDeleteView
.
The responsibility of each class is shown below:
CInfNDriverDelete
– This class contains member elements representing
the Data and View by means of the CInfNDriverDeleteData
and CInfNDriverDeleteView
classes, respectively.
A child window gets created in this class.CInfNDriverDeleteData
– This stores the data that should be shown in the view.CInfNDriverDeleteView
– This class is used to initialize the listview/other UI elements and fill in data that is available in
CInfNDriverDeleteData
into the listview/other required UI elements.
UI elements
Details of UI elements used and the calls to create those are detailed below.
On the UI, a static box is drawn first. Then the controls are drawn after that so that
a proper background color to the window is obtained. Otherwise, the groupbox and checkbox will have
their own color and the screen will look patchy.
I did try setting the background color in many different ways, but nothing worked and hence used the static control as a canvas.
All calls to create UI elements are present within the following function in the source code:
CInfNDriverDeleteView::Draw()
.
Creating a Groupbox
To create a groupbox, CreateWindowEx()
should be called with the style
BW_GROUPBOX
. The window class parameter should be “BUTTON”. The code snippet is shown below:
m_hwndSelectDriverGroup = CreateWindowEx(NULL,
_TEXT("BUTTON"),
_TEXT("Driver File"),
WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_GROUPBOX,
5,
5,
rect.right-10, rect.bottom / 4, m_hwndParent,
NULL,
(HINSTANCE) GetWindowLong(m_hwndParent, GWL_HINSTANCE),
NULL);
Creating a Listview
To create a listbox, CreateWindowEx()
should be called. The style can be
LVS_REPORT
.
The window class parameter should be WC_LISTVIEW
. The ID should be assigned to
the HMENU
parameter. This will be later used to get
control events. The code snippet is shown below:
m_hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE,
WC_LISTVIEW,
L"", WS_CHILD | WS_VISIBLE | LVS_REPORT,
0,
10 + rect.bottom / 4, rect.right/4, ((rect.bottom *3)/4) - 10, m_hwndParent,
(HMENU)ID_INFDELETE_LIST_VIEW,
(HINSTANCE) GetWindowLong(m_hwndParent, GWL_HINSTANCE),
NULL);
Creating a button
To create the Browse button, CreateWindowEx()
should be called with
a window class as “BUTTON”. ID
should be assigned
to the HMENU
parameter. This will be later used to get control events.
The code snippet is shown below:
m_hwndSelectDriverButton = CreateWindowEx(NULL,
_TEXT("BUTTON"),
_TEXT("Browse"),
WS_TABSTOP|WS_VISIBLE|WS_CHILD,
rect.right-180, 30, 100, rect.bottom/ 12, m_hwndParent,
(HMENU)ID_BROWSEFILE_BUTTON,
(HINSTANCE) GetWindowLong(m_hwndParent, GWL_HINSTANCE),
NULL);
Creating a checkbox
To create a checkbox button, CreateWindowEx()
should be called with
a window class as “BUTTON”. ID
should be assigned to the HMENU
parameter. This will be later used to get
the control events. The style flag should contain BS_
AUTOCHECKBOX
.
The code snippet is shown below:
m_hwndForceInfIninstall = CreateWindowEx(NULL,
_TEXT("BUTTON"),
_TEXT("Force Inf Uninstall"),
BS_AUTOCHECKBOX|WS_VISIBLE|WS_CHILD,
10, 57, 120, 25, m_hwndParent,
(HMENU)ID_FORCE_DRIVER_UNINSTALL_BUTTON,
(HINSTANCE) GetWindowLong(m_hwndParent, GWL_HINSTANCE),
NULL);
Creating a static control
To create the static control, CreateWindowEx()
should be called with
a window class as “STATIC”. The code snippet is shown below:
m_hwndStaticBackground = CreateWindowEx(NULL,
_TEXT("STATIC"),
_TEXT(""),
WS_VISIBLE|WS_CHILD,
0, 0, rect.right, rect.bottom / 4 + 10, m_hwndParent,
(HMENU)0,
(HINSTANCE) GetWindowLong(m_hwndParent, GWL_HINSTANCE),
NULL);
WM_NOTIFY message
The control elements will send a WM_NOTIFY
message on events.
The lParam
of WM_NOTIFY
would contain a pointer to
NMHDR
. The layout of the NMHDR
structure is as shown below:
typedef struct tagNMHDR
{
HWNDhwndFrom; UINT_PTR idFrom; UINTcode; } NMHDR;
The NMHDR
structure contains the details on the control ID and
the control hwnd
. This is where the control ID that we assigned earlier to
the Listview and Richedit is useful.
Creating the RichEdit control
To create a RichEdit control RichEd32.dll should be loaded into the memory.
A CreateWindowEx
call should follow. A call to load the library and create the control is shown:
m_hRichEdLib = LoadLibrary(_T("riched32.dll"));
m_hwndRichedit = CreateWindowEx(NULL,
RICHEDIT_CLASS,
_T(""),
WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOHSCROLL|
ES_AUTOVSCROLL|WS_HSCROLL|WS_VSCROLL|WS_BORDER,
rect.right/4 + 5,
10 + rect.bottom / 4, rect.right - rect.right/4 - 4, ((rect.bottom *3)/4) - 10, m_hwndParent,
(HMENU)ID_INF_FILE_CONTENT,
(HINSTANCE) GetWindowLong (m_hwndParent, GWL_HINSTANCE),
NULL);
#include
required for the Richedit control is: #include <RichEdit.h>
.
Note: Freeing the richedit library via
FreeLibrary()
was causing the application to crash.
I did not get time to spend on it further, but I will look into it when I get time. For now, I
have commented out the FreeLibrary()
call.
Creating Context menu over Listview
Once the listview gets populated, right clicking on it would bring up a context menu with
the option to view the inf in Notepad and to uninstall the inf.
Right click on the control was trapped via the NM_RCLICK
notification from the control.
The following calls were used to create the context menu:
POINT pt_CurPos;
GetCursorPos(&pt_CurPos);
HMENU hPopupMenu = CreatePopupMenu();
InsertMenu(hPopupMenu,
0,
MF_BYPOSITION | MF_STRING,
ID_POPUP_UNINSTALL,
_T("Uninstall"));
InsertMenu(hPopupMenu,
0,
MF_BYPOSITION | MF_STRING,
ID_POPUP_OPEN_INF_IN_NOTEPAD,
_T("Open inf file in Notepad..."));
SetForegroundWindow(hWnd);
TrackPopupMenu(hPopupMenu,
TPM_BOTTOMALIGN | TPM_LEFTALIGN,
pt_CurPos.x,
pt_CurPos.y,
0,
hWnd,
NULL);
Editbox subclassing
When a user copies and pastes a sys file path to the editbox and presses Enter, the inf file list should get populated. For this reason, the editbox
was sub classed. In the sub classed proc, Enter key was sensed and a user message sent to the parent window to look for
the relevant file and update
the listview based on the path pasted by the user. The user message sent from
the Editbox proc to the parent window is WM_SIGMA_SELECTFILE
.
To subclass the editbox, the default Winproc of the editbox will have to be replaced by calling
SetWindowLong()
. Following is the code to do that:
WNDPROC DefEditboxProc;
DefEditboxProc = (WNDPROC)SetWindowLong(m_hwndSelectDriverEditbox, GWL_WNDPROC,
(long)EditboxProc);
Selecting a driver file
To select a driver file, the Win32 API GetOpenFileName()
has been used. This will open the Select File dialog. This call is housed inside
the SelectDriverFile()
function in InfDriverDelete.cpp. _wsplitpath_s
is used to split and get
the .sys file name. This function was really helpful
as there was no need to write any code to do this parsing.
Getting the list of inf files
Once the sys file is selected, FindFirstFile()
is used to iterate through
the inf directory under the SYSTEMROOT directory. The wild card filter used during the
search is OEM*.inf files. From the list of OEM*.inf files that is obtained, each
inf file is searched using the INI file function GetPrivateProfileString
to
search for the [SourceDisksFiles]
section. Under the section the key equal to
the sys file name selected is searched for. If such a key is present, then this is
added to the data array. Finally, this data array will have the list of oem*.inf files for the particular “.sys” file that was selected.
GetEnvironmentVariable(_T("SYSTEMROOT"))
is used to get
the Windows root directory (e.g. C:\Windows).
Loading the selected inf file in a RichEdit control
The selected inf file from the listview will be loaded into the RichEdit control. To load a file into the RichEdit control, streaming will be used. The following
two functions in InfNDriverDeleteView.cpp do this work:
CInfNDriverDeleteView::FillRichEditFromInfFile(TCHAR*
szFileName)
and
DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
.
CInfNDriverDeleteView::FillRichEditFromInfFile()
opens the inf file and sends
the EM_STREAMIN
message to the RichEdit handle. The LPARAM
will contains
the address to the EDITSTREAM
structure. EDITSTREAM
will contain
a handle to the Inf file opened and a callback function (EditStreamCallback()
) which will actually read the inf
file and load it into the RichEdit control. Both the functions are shown below for clarity:
DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE lpBuff,
LONG cb, PLONG pcb)
{
HANDLE hFile = (HANDLE)dwCookie;
if(ReadFile(hFile, lpBuff, cb, (DWORD *)pcb, NULL))
{
return 0;
}
return -1;
}
BOOL CInfNDriverDeleteView::FillRichEditFromInfFile(TCHAR* szFileName)
{
BOOL fSuccess = FALSE;
HANDLE hFile = CreateFile(szFileName,
GENERIC_READ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
EDITSTREAM es = {0};
es.dwCookie = (DWORD_PTR)hFile;
es.pfnCallback = EditStreamCallback;
if (SendMessage(m_hwndRichedit, EM_STREAMIN, SF_TEXT, (LPARAM)&es)
&& es.dwError == 0)
{
fSuccess = TRUE;
}
DWORD dw_LastError = GetLastError();
CloseHandle(hFile);
}
return fSuccess;
}
The Richedit control is cleared by sending the following message:
SendMessage(m_hwndRichedit, WM_SETTEXT, 0,(LPARAM)_T""));
Uninstalling the Inf file
Finally, the main task of this application. This is done by calling the SetupUninstallOEMInf()
function available in
SetupApi.dll:
SetupUninstallOEMInf(sz_UnInstallInfFilename, dw_ForceInfUninstall, NULL);
Uninstall of inf can be forced via the “Force INF Uninstall” checkbox available on the UI.
The dw_ForceInfUninstall
variable value depends on the status
of this checkbox.
Environment
This article has been developed and tested under the following environment only: Lenovo T410 Laptop, VC++ 2005, Unicode, C++,
and Windows 7 Enterprise (English language). This article uses the code base from my earlier articles. Earlier I had
a VC++ 6.0 workspace, which has been upgraded to a VS2005 solution with this article.