HOWTO: Determine If an Excel Workbook Contains Macros

ID: Q224518


The information in this article applies to:


SUMMARY

Microsoft Excel workbooks are OLE-structured storage files. You can detect the presence of macros by doing the following:


MORE INFORMATION

The following Microsoft Visual C++ function demonstrates how to do this:


   // Returns TRUE if Excel file contains macros.
   BOOL ExcelFileContainsMacros(char *pszFilename) {
      // Translate filename to UNICODE.
      WCHAR wcFilename[1024];
      int i = mbstowcs(wcFilename, pszFilename, strlen(pszFilename));
      wcFilename[i] = 0;

      // Open workbook using structured storage.
      IStorage *pStg;
      HRESULT hr = StgOpenStorage(wcFilename, 0, 
         STGM_READ | STGM_SHARE_EXCLUSIVE, 0, 0, &pStg);
      if(FAILED(hr)) return FALSE;

      // Test presence of _VBA_PROJECT_CUR substorage (XL8+).
      IStorage *pSubStg;
      hr = pStg->OpenStorage(L"_VBA_PROJECT_CUR", 0, 
         STGM_READ | STGM_SHARE_EXCLUSIVE, 0, 0, &pSubStg);
      if(!FAILED(hr)) {
         // VBA substorage exists.
         pSubStg->Release();
         pStg->Release();
         return TRUE;
      }
      // Test presence of _VBA_PROJECT substorage (XL7+).
      hr = pStg->OpenStorage(L"_VBA_PROJECT", 0, 
         STGM_READ | STGM_SHARE_EXCLUSIVE, 0, 0, &pSubStg);
      if(!FAILED(hr)) {
         // VBA substorage exists.
         pSubStg->Release();
         pStg->Release();
         return TRUE;
      }

      // Now, begin test for Excel 4.0 macro sheets.

      // Open the data stream where Microsoft Excel stores the data.
      IStream *pStm;
      hr = pStg->OpenStream(L"Workbook", 0, 
         STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStm);
      // If "Workbook" fails, try older "Book" name...
      if(FAILED(hr)) {
         hr = pStg->OpenStream(L"Book", 0, 
            STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStm);
         if(FAILED(hr)) {
            pStg->Release();
            return FALSE;
         }
      }

      // Now iterate the records in the stream,
      // looking for BOUNDSHEET record.
      BOOL hasXl4Macros = false;
      for(;;) {
         // Read id and len first.
         DWORD dwRead;
         short id, len;
         hr = pStm->Read(&id, 2, &dwRead);
         // Check exit condition (end of BIFF, or read-error).
         if( (FAILED(hr)) || (dwRead != 2) || (id==0x0a)) break;
         pStm->Read(&len, 2, &dwRead);

         // Is this a BOUNDSHEET record?
         if(id == 0x85) {
            // Read other fields.
            int dummy, grbit;
            pStm->Read(&dummy, 4, &dwRead);
            pStm->Read(&grbit, 4, &dwRead);
            // Adjust length for garbage run.
            len -= 8;

            // Check grbit to determine if it is a macro sheet.
            hasXl4Macros = (grbit & 0x100) ? true : false;

            // Exit, if you found what you are looking for.
            if(hasXl4Macros)
               break;
         }

         // Clear out the rest of this record.
         if(len > 32766) len = 32766;
         static char garbage[32766];
         pStm->Read(garbage, len, &dwRead);
      }
      // Clean up.
      pStm->Release();
      pStg->Release();

      return hasXl4Macros;
   } 

Additional query words: macro virus


Keywords          : kbExcel kbVC kbSDKExcel kbGrpDSO kbOffice2000 kbexcel2000 
Version           : :; WINDOWS:2000,5.0,7.0,97
Platform          : WINDOWS 
Issue type        : kbhowto 

Last Reviewed: June 8, 1999