Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A GUI Front-End for Microsoft's Hotfix Checker Utility

0.00/5 (No votes)
6 Dec 2001 2  
This article demonstrates redirecting output of a child process, and displaying a web browser UI using DHTML.

 [WHotfixCheck dialog - 28K]

Introduction

Microsoft recently released a tool called Hfnetchk that checks NT 4 and Windows 2000 systems and generates a report of which service packs and hotfixes need to be installed. While this is a great idea, the tool is a console-mode program and only prints out a list of Knowledge Base article numbers; it doesn't list URLs or take you to the Knowledge Base articles about the hotfixes. Fortunately, Hfnetchk has a switch to produce tab-delimited output, which makes it rather simple to parse the output and show it in a more friendly UI. This article describes WHotfixCheck, a tool I wrote to provide just such a friendly UI, and touches on some interesting programming topics that I encountered while writing it.

Updates:

  • Sept. 23, 2001 - Added ability to scan remote computers. Added code to save and load last-used settings to/from the registry. Thanks to Uwe Keim for adding these new features!
  • Oct. 11, 2001 - Added Save Results button to save the scan results to an HTML file.
  • Oct 18, 2001 - Added support for more Hfnetchk switches: show missing or installed hotfixes, skip registry checks, verbose output. Made the HTML output a bit nicer-looking.
  • Nov. 7, 2001 - Improved handling of errors and messages from Hfnetchk. If Hfnetchk outputs a message (such as "no hotfixes available"), WHotfixCheck now shows that in a message box. Added XP theme support.

Using WHotfixCheck

To use WHotfixCheck, you must first download and install Hfnetchk from Microsoft. Browse to the Knowledge Base article on the tool and follow the download link in that article. Once it's installed, run WHotfixCheck and enter the path to hfnetchk.exe in the top edit box. If you have never run Hfnetchk before, leave the other edit box empty and Hfnetchk will download the necessary data file. If you have run it, there will be a file called mssecure.xml in the same directory as hfnetchk.exe; enter the path to that file in the second edit box.

In the Hotfixes to show combo box, select which hotfixes you want to see. The default is Necessary hotfixes, which shows hotfixes you have not installed. Select Installed hotfixes to see hotfixes you have already installed. Missing hotfixes is similar to Necessary hotfixes but also shows hotfixes that have been superseded by later hotfixes.

If you are viewing installed hotfixes, and WHotfixCheck reports that a hotfix that you have already installed is missing, check the Skip registry checks checkbox. Not all hotfixes are recorded in the registry, so they are always reported as not being installed. Checking Skip registry checks makes Hfnetchk ignore such hotfixes and not report them.

Check Verbose output if you want to see additional details in the hotfix list. This will show the status for each hotfix (found, not found, etc.) and an explanation of how Hfnetchk determined the status.

In the What to Scan combo box is a list of options relating to what computers should be scanned. Hfnetchk can remotely scan any computer on which you have administrator privileges. You can choose to scan just your local computer, a remote computer (identified by its name or IP address), a range of IP addresses, or an entire domain.

Once you have provided the locations of the needed files, click Run to begin the scan. If everything goes well, you will see a list of available hotfixes that are not installed on your computer. See the screen shot above for an idea of what the list looks like. Each line of the table lists the computer name, product name, Microsoft security bulletin number, and Knowledge Base article number. The latter two columns are linked to the corresponding web pages at Microsoft's site, so you can use those links to quickly get to Microsoft's descriptions of the hotfixes.

After running a scan, you can save the results to an HTML file by clicking the Save Results button.

Implementation Details

In this section I'll describe some of the more interesting parts of the code. You can safely skip this section if you aren't concerned with the internal workings of the program.

Running Hfnetchk and capturing its output

If WHotfixCheck were a console program, we could use the CRT function _popen() to run Hfnetchk and read its output. However, _popen() doesn't work when called from a GUI program, so we must create a pipe ourselves and attach it to Hfnetchk's standard output.

The first step is to set up security attributes for the pipe handles. The SECURITY_ATTRIBUTES struct contains a flag that determines whether the child process we'll launch can inherit handles from our process. This flag must be set to TRUE.

LRESULT CMainDlg::OnRun(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
// ...

SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
 
    sa.bInheritHandle = TRUE;

Next, we create the pipe. We get back two handles, one to the read end and one to the write end.

HANDLE hStdoutRd, hStdoutWr;
 
    if ( !CreatePipe ( &hStdoutRd, &hStdoutWr, &sa, 0 ))
        return 0;

Next, we set up the structures used by CreateProcess(). The important part here is that we set the child process's standard output to be the write end of the pipe.

STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
 
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdOutput = hStdoutWr;

Next, we launch Hfnetchk.

    if ( !CreateProcess ( NULL, szCommandLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS,
                          NULL, NULL, &si, &pi ))
        {
        // error handling omitted

        return 0;
        }

The fifth parameter is a flag that indicates whether the child process inherits our handles. This must be TRUE for Hfnetchk to be able to use the pipe handle we're passing it.

Once Hfnetchk has been launched, we can start reading its output. To make parsing easier, we dump the output to a temp file first. The first step is to close our handle to the write end of the pipe. If we don't do this, the write end will not close when Hfnetchk is done with it, because the pipe won't close when there are open handles to it.

    CloseHandle ( hStdoutWr );

Next, we create a temp file to store Hfnetchk's output.

HANDLE hTempFile;
 
    hTempFile = CreateFile ( szTempFile, GENERIC_WRITE, 0, NULL, 
                             CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
 
    if ( INVALID_HANDLE_VALUE == hTempFile )
        return 0;

Now we can enter a loop and read the data from the pipe.

BYTE buff[1024];
DWORD dwRead, dwWritten;
 
    while ( ReadFile ( hStdoutRd, buff, countof(buff), &dwRead, NULL ) && dwRead != 0 )
        {
        WriteFile ( hTempFile, buff, dwRead, &dwWritten, NULL );
        }

ReadFile() will return FALSE when Hfnetchk has written all its data into the pipe and terminated, thus closing the write end of the pipe.

After that, it's a simple matter of parsing the lines with strtok(). Each line contains details about one hotfix. We pull out the computer name, product name, security bulletin number, and Knowledge Base article number and store them in a struct:

struct CPatchInfo
{
    CString sComputer, sProduct, sBulletin, sKBNumber;
};

OnRun() builds an array of structs and passes it to ShowPatchList(), which handles the UI.

Displaying the results

WHotfixCheck hosts a WebBrowser control and uses the IE DHTML object model to display HTML in the browser. The starting point it to get an IWebBrowser2 pointer on the control.

void CMainDlg::ShowPatchList ( CSimpleArray<CPatchInfo>& aPatchInfo )
{
CComPtr<IUnknown>       punkIE;
CComQIPtr<IWebBrowser2> pWB2;
 
    // Get an IWebBrowser2 interface to control the browser.

    AtlAxGetControl ( GetDlgItem(IDC_IE), &punkIE );
    pWB2 = punkIE;

Now, we can get a pointer to the HTML document, and then the <body> element. Once we have access to the body, we can insert any HTML we want. (There's actually a lot more code involved, but I've omitted it here for clarity.)

CComPtr<IDispatch>        pdispDoc;
CComQIPtr<IHTMLDocument2> pDoc;
CComPtr<IHTMLElement>     peltBody;
 
    // Get a pointer to the <body> element so we can insert a table.

    pWB2->get_Document ( &pdispDoc );
    pDoc = pdispDoc;
    pDoc->get_body ( &peltBody );

Now we can insert HTML into the body. The first step is to create a <table> with one row that contains the column headings.

    // Replace the body with the beginnings of our table.

    peltBody->put_innerHTML ( CComBSTR("<table width=100% id=\"patches\" cols=3><tr>...</tr></table>"));

The <table> has an id field so we can modify it with the DHTML object model. The next step is to get a IHTMLTable interface on the table. There are two ways to do this. To maintain compatibility with IE 4, WHotfixCheck accesses the all collection of the document and gets the table from that collection. A simpler way is to use IHTMLDocument3::getElementById() to get an interface on the table by passing its id, but this method requires IE 5.

CComPtr<IHTMLElementCollection> pColl;
CComPtr<IDispatch>    pdispTable;
CComQIPtr<IHTMLTable> pTable;
 
    pDoc->get_all ( &pColl );
 
    pColl->item ( CComVariant("patches"), CComVariant(0), &pdispTable );
    pTable = pdispTable;

Now we can start adding rows to the table. We loop through the array of CPatchInfo structs and for each one, assemble either a string or an HTML fragment to go in the table. Adding to the table involves first adding a row, then adding cells to the row. Here's how we add a new row:

    for ( int i = 0; i < aPatchInfo.GetSize(); i++ )
        {
        CComPtr<IDispatch> pdispRow;
        CComQIPtr<IHTMLTableRow> pRow;
 
        // Add a new row to the end of the table.

        pTable->insertRow ( -1, &pdispRow );
        pRow = pdispRow;

Passing a row index of -1 puts the new row at the end of the table. With the IHTMLTableRow interface, we can add cells to the row. Here is the code to add column 1, a plain string listing the computer name:

CComPtr<IDispatch> pdispCell;
CComQIPtr<IHTMLElement> peltCell;
 
        // Col 1 - computer name

        pRow->insertCell ( -1, &pdispCell );
        peltCell = pdispCell;
 
        peltCell->put_innerText ( CComBSTR(aPatchInfo[i].sComputer) );
 
        pdispCell.Release();
        peltCell.Release();

IHTMLElement::put_innerText() sets the text of the element. The word "inner" indicates that the text being changed is inside the HTML tags (<td> and </td> in this case). Column 2 is created similarly, and shows the product name.

For column 3, we create an HTML fragment containing an <a> tag, which makes a hyperlink.

        // Col 3 - security bulletin name/URL

CString sText;
 
        pRow->insertCell ( -1, &pdispCell );
        peltCell = pdispCell;
 
        sText.Format ( _T("<a href=\"http://www.microsoft.com/technet/security/bulletin/%s.asp\" target=_blank>%s</a>"),
                       (LPCTSTR) aPatchInfo[i].sBulletin, (LPCTSTR) aPatchInfo[i].sBulletin );

        peltCell->put_innerHTML ( CComBSTR(sText) );
 
        pdispCell.Release();
        peltCell.Release();

This time we use IHTMLElement::put_innerHTML() to insert an HTML fragment into the cell. Column 4 is created similarly to column 3, and links to the Knowledge Base article.

As you can see, accessing the DHTML object model from C++ is a bit cumbersome, since many methods return IDispatch interfaces, which you then have to query for the interface you really need. The documentation is also lacking in some areas, most notably the fact that the Web Workshop pages that have the object model reference do not appear in the Contents pane, so you can't use the Locate button in the help viewer. I would normally use the Locate button to jump right to the list of methods in an interface.

Room for Improvement

The code in ShowPatchList() that gets a pointer to the <body> is rather ugly, since it has to handle the special case of the first call, where there is no document or body in the WebBrowser yet. I threw together something that creates a dummy body, since I found no IHTMLDocument method that looked like it could do the job.

Links

Find out more about Hfnetchk in these Knowledge Base articles:

WHotfixCheck and Hfnetchk require Microsoft's XML parser. Download version 3 here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here