Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Uninstalling inf files related to a driver file

4.71/5 (10 votes)
7 Feb 2013CPOL8 min read 54.3K   2.3K  
This article explains how to get a list of inf files related to a driver and uninstall the inf files.

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:

Image 1

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:

C++
// Create a groupbox to house the driver file selection button
m_hwndSelectDriverGroup = CreateWindowEx(NULL, 
                                        _TEXT("BUTTON"),
                                        _TEXT("Driver File"),
                                        WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_GROUPBOX,
                                        5,
                                        5,
                                        rect.right-10, //width
                                        rect.bottom / 4, //height
                                        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:

C++
m_hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE, 
                             WC_LISTVIEW,
                             L"", //caption not required
                             WS_CHILD | WS_VISIBLE | LVS_REPORT,
                             0,
                             10 + rect.bottom / 4, // start after the group box,
                             rect.right/4, // width
                             ((rect.bottom *3)/4) - 10,// height
                             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:

C++
// Create a button which will launch driver file selection dialog
m_hwndSelectDriverButton = CreateWindowEx(NULL, 
                             _TEXT("BUTTON"),
                             _TEXT("Browse"),
                             WS_TABSTOP|WS_VISIBLE|WS_CHILD,
                             rect.right-180,//x,
                             30,//y
                             100,//width
                             rect.bottom/ 12, //height
                             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:

C++
// Create checkbox
m_hwndForceInfIninstall = CreateWindowEx(NULL, 
                             _TEXT("BUTTON"),
                             _TEXT("Force Inf Uninstall"),
                             BS_AUTOCHECKBOX|WS_VISIBLE|WS_CHILD,
                             10,//x,
                             57,//y
                             120,//width
                             25, //height
                             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:

C++
// create static control for background
// all remaining controls can be written on top of it
m_hwndStaticBackground = CreateWindowEx(NULL, 
                             _TEXT("STATIC"),
                             _TEXT(""),
                             WS_VISIBLE|WS_CHILD,
                             0,//x,
                             0,//y
                             rect.right,//width
                             rect.bottom / 4 + 10, //height
                             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:

C++
typedef struct tagNMHDR
{
    HWNDhwndFrom; // handle of the control sending the message
    UINT_PTR idFrom; // id of the control sending the message
    UINTcode; // notification code used to identify the event. 
              // This is used to idenfity if it is a right click 
              // or a click in the control
} 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:

C++
// Load richedit library
m_hRichEdLib = LoadLibrary(_T("riched32.dll"));
 
// Create Richedit control to show selected inf file
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, // start after the group box
                          rect.right - rect.right/4 - 4, // width
                          ((rect.bottom *3)/4) - 10, // height
                          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:

C++
//Create a popup 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:

C++
WNDPROC DefEditboxProc;

//subclass editbox to capture enter button
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:

C++
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:

C++
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:

C++
SetupUninstallOEMInf(sz_UnInstallInfFilename, // inf file name
                     dw_ForceInfUninstall, // force removal or not
                     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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)