Introduction
This article is written in honour of Matt Pietrek who wrote
the GREAT article on LIB file internals in the "Under The Hood" section of the
April 1998 edition of Microsoft Systems Journal. I urge all readers to read this
excellent article which includes full source codes to a sample
LibDump.exe demo program. The following is the link to the article :
LibDump.exe is a small console program this will list out the exported
symbols which are contained in an Import or Static Library file. The library
file must be in COFF format. In Matt's article, he describes how LibDump.exe
works. Inspired by LibDump, I endeavored to write a Visual Studio add-in
that gives LibDump a user interface. The picture displayed above shows a sample
listing of the symbols contained in a library file named c:\libs\netapi32.lib. I also gave
additional facilities like putting the listing report into a Visual Studio
window so that the user is able to do text searches and to save the report into
a text file.
Perhaps the most useful feature of this add-in is its facility to include the
current lib file under view into the library list of the current project so that
the lib file can be linked. I have further plans to expand the add-in in
upcoming versions. I have included full source codes to my add-in in this
article which includes the source files given by Matt Pietrek. I have made sure
that Matt's name remains in his source files.
Installation
- Build the zipped LibExplorer.dsw project and copy
LibExplorer.dll into Program Files\Microsoft visual
studio\common\msdev98\addins.
- Open Tools | Customize | Add-ins and Macro Files and tick
"LibExplorer.DSAddIn.1".
- A new toolbar with two icon buttons shown below will appear.
Usage
- Symbol Dump
- Click on the Symbol Dump Add-in Icon and the usual Open Dialog Box will
appear.
- Select a static or import library file.
- A modeless dialog box with a list control will be displayed. This list
control will display the all the exported symbols of the library file.
- The Ordinal number of the symbol, the symbol type (exported by Ordinal or by
Name) and the full symbol name are displayed row by row.
- If you click on the "Display Report In Window" button, the report will be
displayed in a Visual Studio window :
- From here, you can opt to save this report in an external text file via
"File|Save".
- You can also add the current library that you are viewing into your Project
by clicking on the "Add To Library List" button. The picture below show how
c:\libs\netapi32.lib has been added to the list of libraries in my
current project.
- Search Symbol
- Click on the Search Symbol Add-in Icon and the Search Symbol Specifications
Dialog Box will appear.
- This dialog box allows you to specify the symbol to search for, the
directory in which to scan library files, and whether the matching is done for
whole words and whether case sensitivity is enforced.
- After you have specified the conditions for the search, the Search Symbol
Dialog Box will appear :
- Various boxes will show the symbol that you are searching for, the directory
that you have selected for scanning, the conditions for the search and finally,
the search results.
- Via the "Add To Library List" button, you can also select a list entry and
have the add-in add the library file (that is associated with the found symbol)
to be added to the library list of the current Developer Studio Project.
- A "Stop Search" button is also supplied to terminate a search whenever you
want to.
Limitations
- Symbol Dump
- Note that clicking "Display Report In Window" multiple times will cause
multiple reports to be displayed in multiple windows even if one such report has
already been displayed.
- The report is first dumped by the add-in into a temporary file on your
system. The contents of this file (the report) is then displayed in a Visual
Studio window. Therefore the title of the window will be the name of the temp
file and not the name of the library file.
- The same goes for the "Add To Library List" button. Clicking this button
multiple times will cause the same lib file to be included in your project
linker settings multiple times.
- Symbol Search
- The Symbol Search Add-in is currently only able to scan files in one
directory. Sub-directories are not traversed further. I'm currently working on
this feature.
How Does It Work ?
Symbol Dump
The entire functionality of the add-in is encapsulated inside the
CDlg_LibraryExplorerSymbolsDump
dialog box and in the global
functions DoSymbolsCollection()
and LibInfoForSymbolAnalyse()
which are slight modifications of Matt Pietrek's functions. This dialog
box is designed to be a MODELESS dialog box. This dialog box will manage exactly
one lib file and will do so until it is closed. This dialog box will start life
when the add-in icon button is clicked at which time the following code will be
executed :
STDMETHODIMP CCommands::LibraryExplorer()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
if (m_pDlg_LibraryExplorerSymbolsDump == NULL)
{
LPTSTR lpszFileName = NULL;
long lRetTemp = 0;
lRetTemp = OpenAFile ((LPTSTR*)&lpszFileName);
if ((lRetTemp == 0) && (lpszFileName))
{
m_pDlg_LibraryExplorerSymbolsDump =
new CDlg_LibraryExplorerSymbolsDump();
m_pDlg_LibraryExplorerSymbolsDump -> SetFileToDumpSymbols(
(LPCTSTR)lpszFileName);
m_pDlg_LibraryExplorerSymbolsDump -> SetCommandsObject (this);
m_pDlg_LibraryExplorerSymbolsDump -> Display();
}
if (lpszFileName)
{
free (lpszFileName);
lpszFileName = NULL;
}
}
return S_OK;
}
- A new instance of
CDlg_LibraryExplorerSymbolsDump
is created,
the file to view is set by the member SetFileToDumpSymbols()
function.
- We also give this dialog box a pointer to the current
CCommands
object so that it can communicate with it. It is via the CCommands
pointer that the dialog box instructs CCommand
to display
the Symbols Listing Report.
- We then instruct the dialog box to display itself.
When the dialog box comes alive, the OnInitDialog()
member
function will be called. This is where the action starts :
BOOL CDlg_LibraryExplorerSymbolsDump::OnInitDialog()
{
long lRetTemp = 0;
CDialog::OnInitDialog();
InitSymbolsDumpListControl();
lRetTemp = DoSymbolsCollection ((LPCTSTR)m_szFileToDumpSymbols, this);
if (lRetTemp == 0)
{
DisplaySymbols();
}
return TRUE;
}
- We first call upon
InitSymbolsDumpListControl()
to initialize
the List Control. The common APIs to create columns for a list control are used.
- Next, we call on the global function
DoSymbolsCollection()
to
open up the LIB file and collect all the exported symbols from it.
Function DoSymbolsCollection()
will go through the entire LIB
file and pick up symbols, analyse them and store them into individual
CSymbol
objects. These CSymbol
objects will then be
stored inside the dialog class CDlg_LibraryExplorerSymbolsDump
in a
map object. DoSymbolsCollection()
is based on the main()
function supplied by Matt Pietrek.
DoSymbolsCollection()
also uses the MEMORY_MAPPED_FILE
class supplied by Matt. Note that the file must be a valid COFF file or
this function will reject it. I leave it to the reader to read through the
article by Matt Pietrek to understand the principles of the
DoSymbolsCollection()
function. Another function of interest is the
LibInfoForSymbolAnalyse()
function which is based on Matt's
DisplayLibInfoForSymbol()
function. LibInfoForSymbolAnalyse()
will analyse the current symbol and store the information in a
CSymbol
object which is returned. We will transfer the
CSymbol
object to the dialog class
CDlg_LibraryExplorerSymbolsDump
for long term keeping.
I defined and used a CSymbol
class (derived from
CObject
) which is stored in a CMapWordToOb
map in
CDlg_LibraryExplorerSymbolsDump
. Usage of such a CSymbol
object makes it much more convenient for reporting and sorting purposes
and also paves the way for future analysis of individual symbols. CSymbol
has also made the member function DisplaySymbols()
much more
clean. It encapsulates one symbol (which can be of several types). We use
central CSymbol
member functions to obtain the names and types of
these symbols.
2. Symbol Search
With version 2, the CSymbol
class has been extended with several
additional members :
m_cstrSymbol
- Stores the symbol string itself.
m_cstrExportedName
- Stores the exported name of the symbol.
m_cstrLibFilePath
- Stores the full path of the library file
that contains it.
m_dwArchiveMemberOffset
- Stores the library file offset of the
archive member that contains this symbol.
Although it may seem extravagant to include a full path to the lib file in
every CSymbol
object (generating a lot of repeated information in a
list of CSymbol
objects), this member helps in overall
organization. The constructor for CSymbol
has also been extended
with two additional parameters that takes in the lib file name and the archive
member offset. I have also created a new CSymbolContainerAbstractClass
pure abstract base class from which to derive our dialog box class
CDlg_LibraryExplorerSymbolsDump
.
class CSymbolContainerAbstractClass
{
public:
virtual long AddNewSymbol (CSymbol* pSymbolNewSet) = 0;
};
This base abstract class defines the public AddNewSymbol()
method which all derived classes must implement. The
CDlg_LibraryExplorerSymbolsDump
dialog box class now inherits from
CSymbolContainerAbstractClass
as well as CDialog
so
that it will inherit the AddNewSymbol()
method :
class CDlg_LibraryExplorerSymbolsDump : public CDialog,
public CSymbolContainerAbstractClass
{
public:
virtual long AddNewSymbol (CSymbol* pSymbolNewSet);
...
...
...
We have also modified the DoSymbolsCollection()
function so that
it takes a pointer to a CSymbolContainerAbstractClass
class instead
of the former CDlg_LibraryExplorerSymbolsDump
class. This is done
so that DoSymbolsCollection()
can call on any class that contains
symbols. AddNewSymbol()
serves as a callback into the Symbol
Container to instruct it to add a new symbol object. The
CDlg_LibraryExplorerSymbolsDump
dialog class is one of them. And
this new version of LibExplorer will introduce another dialog class that will
also contain symbols. I have avoided the use of interfaces and IDLs. They
unnecessarily complicate things for this project and will be hard for beginners
to understand.
The Symbol Search Add-in will start life when its associated icon button is
clicked at which time the CCommands::SymbolSearch()
function will
be executed. The CDlg_SearchSymbolSpecs
dialog class encapsulates
the functionality of the Symbol Search Specifications dialog box. This class is
straightforward and the usual methods are used to extract the various search
specifications. The CDlg_LibraryExplorerSymbolSearch
dialog class
encapsulates the functionality of symbol matching. The actual file iteration
through a directory is done via a thread in which the
ThreadFunc_FileScan()
function serves as the controlling function.
The DoSymbolsCollection()
function is used to collect symbols
found in a library file into
CDlg_LibraryExplorerSymbolSearch::m_MapSymbolsTemp
, a Map
object that holds pointers to CSymbol
objects that represent
symbols found in a lib file. The
CDlg_LibraryExplorerSymbolSearch::DoSearchSymbolInFile()
function
performs the symbol matching operation. The principle of the searching operation
is to scan through the temporary symbols list
CDlg_LibraryExplorerSymbolSearch::m_MapSymbolsTemp
and then storing
found symbols in a permanent symbols list
CDlg_LibraryExplorerSymbolSearch::m_MapSymbols
. Helper functions
CDlg_LibraryExplorerSymbolSearch::DoMatchCaseSensitive()
,
CDlg_LibraryExplorerSymbolSearch::DoMatchCaseInsensitive()
,
CDlg_LibraryExplorerSymbolSearch::DoMatchSubStringCaseSensitive()
and
CDlg_LibraryExplorerSymbolSearch::DoMatchSubStringCaseInsensitive()
all help in the symbol matching operation.
Further Enhancements
- I plan on adding more enhancements after this second version of LibExplorer.
- As stated in the "Limitations" section, I'm working on adding a
sub-directory traversing facility to the Symbol Search Add-in.
- I'm also studying the contents of OBJ files and a third version would
probably include listing the RAW contents of exported functions (from static
libs) into a Visual Studio binary view window.
- Hope I've done you proud, Matt.
Interim Enhancements and Bug Fixes
Monday December 17th 2001.
- A reader has kindly alerted me to a Visual Studio crash when a library file
is added to the library list. This crash occurs when Visual Studio currently has
no active workspace or project.
- I tested this case in a Windows 98 machine and noted that the crash does
indeed occur.
- The crash occurred in the member function
CCommands::GetActiveConfiguration()
.
- When there is no current active project, getting the Active Configuration
Object from the Application Object would raise an exception.
- In WinNT, the exception is caught when
IDispatch::Invoke()
is
called and CCommands::GetActiveConfiguration()
is able to exit
gracefully and indicate to the user that there is no current active project.
- Apparently, the exception handling routines in MSDEV in Win98 is not as
robust as that in WinNT. Hence the crash.
- To remedy the situation, I made the following code changes in
CCommands::IncludeLibFileInLibraryList()
:
- I created several more helper functions to obtain the current Projects List
Property from the Application Object. This property will exist even if there is
no current active project.
- Using the returned Projects List Property, I query it for its count of
projects. This will be returned in "
lProjectsCount
".
- If "
lProjectsCount
" is zero, it means that there is no current
active project and so we can gracefully exit.
- I added in further checks in
CCommands::IncludeLibFileInLibraryList()
to ensure no other surprise crashes.
long CCommands::IncludeLibFileInLibraryList(LPCTSTR lpszLibFileName)
{
LPDISPATCH pProjects = NULL;
LPDISPATCH pProject = NULL;
LPDISPATCH pConfig = NULL;
TCHAR szMessage[256];
HRESULT hrRetTemp = S_OK;
long lProjectsCount = 0;
long lRet = 0;
szMessage[0] = '\0';
GetProjects ((LPDISPATCH*)&pProjects);
if (pProjects == NULL)
{
lRet = -1;
wsprintf (szMessage, "ERROR:\r\nUnable to obtain Projects List.");
goto IncludeLibFileInLibraryList_0;
}
ProjectsGetCount (pProjects, &lProjectsCount);
if (lProjectsCount <= 0)
{
lRet = -2;
wsprintf (szMessage, "ERROR:\r\nProjects List indicate "
"that there are currently no projects.");
goto IncludeLibFileInLibraryList_0;
}
GetActiveProject((LPDISPATCH *)&pProject);
if (pProject == NULL)
{
lRet = -3;
wsprintf (szMessage, "ERROR:\r\nThere is no active project.");
goto IncludeLibFileInLibraryList_0;
}
GetActiveConfiguration((LPDISPATCH *)&pConfig);
if (pConfig)
{
hrRetTemp = ConfigAddToolSettings((LPDISPATCH)pConfig,
(LPCTSTR)"link.exe", (LPCTSTR)lpszLibFileName);
if (hrRetTemp == S_OK)
{
wsprintf (szMessage,
"SUCCESS:\r\nFile <%s> has successfully been "
"included into library list.", lpszLibFileName);
}
else
{
wsprintf (szMessage, "ERROR:\r\nUnable to include file <%s> "
"into library list.", lpszLibFileName);
}
}
else
{
wsprintf (szMessage, "ERROR:\r\nNo current configuration.\r\nUnable"
" to include file <%s> into library list.", lpszLibFileName);
}
IncludeLibFileInLibraryList_0:
AfxMessageBox(szMessage, MB_OK | MB_ICONINFORMATION);
if (pConfig)
{
pConfig -> Release();
pConfig = NULL;
}
if (pProject)
{
pProject -> Release();
pProject = NULL;
}
if (pProjects)
{
pProjects -> Release();
pProjects = NULL;
}
return lRet;
}
Wednesday December 19th 2001.
- Another reader (Bruno Leclerc) alerted me to a crash on his WinNT 4.0
machine with Visual C++ 6.0. Bruno has used a RELEASE version of the add-in. The
crash occurred after he has selected a lib file in the common File Open dialog
box.
- However, after 2 days of debugging and testing, including enlisting the help
of NuMega's Bounds Checker, I could not reproduce the problem on my machines.
- Hence I sincerely request readers who have faced the same problem, if you
have the time, to perhaps insert trace statements (or use plain old
MessageBoxes) into the supplied source codes to help me pinpoint the exact
source location where the crash occurred.
- If you can reproduce the problem in a DEBUG version of the add-in, and if
you can spare the time to step through the code, all the better :-)
- All these would help me a great deal to narrow down the problem.
- My sincere apologies for this and many thanks to all.
Latest Updates
- Monday December 24th 2001. LibExplorer Version 2.
- It's arrived ! I have updated this article with version 2 of the LibExplorer
Developer Studio Add-in.
- This new version includes a new Symbol Search Dialog Box that allows the
user to scan a directory for lib files that contain a particular symbol.
- The user is able to indicate full string search or sub-string search. Case
sensitivity can also be indicated.
- Just like the version 1 symbol dump facility, the user is able to add a
library that contains a found symbol to the library list of a Developer Studio
Project for linking.
- More on these in the "Usage" section below.