|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionMicrosoft 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:
Using WHotfixCheckTo 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 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 DetailsIn 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 outputIf WHotfixCheck were a console program, we could use the CRT function The first step is to set up security attributes for the pipe handles. The 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 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 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 ); }
After that, it's a simple matter of parsing the lines with struct CPatchInfo
{
CString sComputer, sProduct, sBulletin, sKBNumber;
};
Displaying the resultsWHotfixCheck hosts a WebBrowser control and uses the IE DHTML object model to display HTML in the browser. The
starting point it to get an 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 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 // Replace the body with the beginnings of our table. peltBody->put_innerHTML ( CComBSTR("<table width=100% id=\"patches\" cols=3><tr>...</tr></table>")); The 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 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 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();
For column 3, we create an HTML fragment containing an // 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 As you can see, accessing the DHTML object model from C++ is a bit cumbersome, since many methods return Room for ImprovementThe code in LinksFind out more about Hfnetchk in these Knowledge Base articles:
WHotfixCheck and Hfnetchk require Microsoft's XML parser. Download version 3 here.
| ||||||||||||||||||||