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

Import and Static Library Symbols Explorer Add-In for Visual Studio : Version 2.

0.00/5 (No votes)
23 Dec 2001 3  
Visual Studio add-in that allows you to list down all the exported symbols inside an Import or Static Library. This add-in also allows you to save the exported symbols listing and to include a library file into your current Project. This add-in also includes a facility to scan through library files.

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

  1. Symbol Dump
    1. Click on the Symbol Dump Add-in Icon and the usual Open Dialog Box will appear.

    2. Select a static or import library file.
    3. 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.
    4. 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.
    5. If you click on the "Display Report In Window" button, the report will be displayed in a Visual Studio window :

       

    6. From here, you can opt to save this report in an external text file via "File|Save".
    7. 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.

    8. Search Symbol
      1. Click on the Search Symbol Add-in Icon and the Search Symbol Specifications Dialog Box will appear.

      2. 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.
      3. After you have specified the conditions for the search, the Search Symbol Dialog Box will appear :

      4. 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.
      5. 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.
      6. 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())
    
        // TODO: Add your implementation code here
    
        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();
        
        // TODO: Add extra initialization here
    
        InitSymbolsDumpListControl();
    
        // Get modified Matt Pietrek routines to collect all symbols.
    
        lRetTemp = DoSymbolsCollection ((LPCTSTR)m_szFileToDumpSymbols, this);
    
        // If all went well in DoSymbolsCollection, we now display all symbols 
    
        // collected in m_MapSymbols in our List Control.
    
        if (lRetTemp == 0)
        {
          DisplaySymbols();
        }
    
        return TRUE;  // return TRUE unless you set the focus to a control
    
                  // EXCEPTION: OCX Property Pages should return FALSE
    
    }
    
    • 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
    {
    // Construction
    
    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

    1. I plan on adding more enhancements after this second version of LibExplorer.
    2. As stated in the "Limitations" section, I'm working on adding a sub-directory traversing facility to the Symbol Search Add-in.
    3. 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.
    4. 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 whenIDispatch::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;
    
      // Initialise message string.
    
      szMessage[0] = '\0';
    
      GetProjects ((LPDISPATCH*)&pProjects);
      if (pProjects == NULL)
      {
        lRet = -1; // No projects list.
    
        wsprintf (szMessage, "ERROR:\r\nUnable to obtain Projects List.");
        goto IncludeLibFileInLibraryList_0;
      }
    
      // The Projects List property is not NULL.
    
      // We now obtain the count of projects in the Projects List.
    
      ProjectsGetCount (pProjects, &lProjectsCount);
      if (lProjectsCount <= 0)
      {
        lRet = -2; // Projects List indicate that there 
    
                   // are currently no projects.
    
        wsprintf (szMessage, "ERROR:\r\nProjects List indicate "
            "that there are currently no projects.");
        goto IncludeLibFileInLibraryList_0;
      }
    
      // Check to see if we have an active project.
    
      GetActiveProject((LPDISPATCH *)&pProject);
      if (pProject == NULL)
      {
        lRet = -3; // No active project.
    
        wsprintf (szMessage, "ERROR:\r\nThere is no active project.");
        goto IncludeLibFileInLibraryList_0;
      }
    
      // Finally, we have an active project.
    
      // We now get the currently active configuration.
    
      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.

    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