Add Microsoft AntiVirus API


Microsoft Antivirus API enables software developers to develop applications that scan Microsoft Office documents before opening them.
The Antivirus API also supports scanning Microsoft IE code downloads, such as ActiveX controls.
The primary purpose of this API is to give a software developers the ability to design and implement antivirus software that can be used by all applications.

The antivirus component is a standard ActiveX component you register as an in-process server that supports the MSOfficeAntiVirus component category
(CATID_MSOfficeAntiVirus : TGUID = '{56FFCC30-D398-11d0-B2AE-00A0C908FA49}').
IE and MS Office implement the antivirus component as follows:

Obtain the list of all the installed antivirus components registered as supporting the MSOfficeAntiVirus component category.
Launch the installed components.
Query for the IOfficeAntiVirus interface.
Call IOfficeAntiVirus.Scan method to obtain all the installed components.
Continue to open the file after the virus scan, regardless of the HRESULT value. The antivirus software warns a user if a file has a known virus but opens the file after the warning. It is up to the user to take action concerning the warning.


  1. {The Antivirus API enables software vendors to develop applications that scan
  2.  Microsoft Office documents before opening them. The API also supports
  3.  scanning Microsoft® Internet Explorer 5 code downloads, such as ActiveXControls
  4.  or installs. However, be aware that the Internet Explorer scanning support
  5.  applies only to automatic code downloads and not to file downloads or
  6.  HTML documents.
  7.  The primary purpose of this API is to give independent software vendors (ISVs)
  8.  the ability to design and implement scanning software that can be used by
  9.  all applications.}
  10.  
  11.  
  12. unit msoav;
  13.  
  14. interface
  15.  
  16. uses Windows, SysUtils, ActiveX, ComObj, Classes;
  17.  
  18. const
  19.  
  20. IID_IOfficeAntiVirus : TGUID =    '{56FFCC30-D398-11d0-B2AE-00A0C908FA49}';
  21. //DEFINE_GUID(IID_IOfficeAntiVirus,
  22. //0x56ffcc30, 0xd398, 0x11d0, 0xb2, 0xae, 0x0, 0xa0, 0xc9, 0x8, 0xfa, 0x49);
  23.  
  24. CATID_MSOfficeAntiVirus : TGUID = '{56FFCC30-D398-11d0-B2AE-00A0C908FA49}';
  25. //DEFINE_GUID(CATID_MSOfficeAntiVirus,
  26. //0x56ffcc30, 0xd398, 0x11d0, 0xb2, 0xae, 0x0, 0xa0, 0xc9, 0x8, 0xfa, 0x49);
  27.  
  28.  
  29. type
  30.  
  31.  TInfoStruct = record
  32.   fIsFile : boolean;
  33.   fIsReadOnly : boolean;
  34.   fIsInstalled : boolean;
  35.   fIsHTTPDownload : boolean;
  36.  end;
  37.  
  38.  //Contains information about the file to be scanned.
  39. {
  40.  * cbSize      
  41. - Integer value that specifies the size of an MSOAVINFO structure.
  42.  * hWnd        
  43. - Handle to the parent window of the Microsoft® Office 2000 application.
  44.  * pwzFullPath
  45. - Address of a wide character string that contains the full
  46. path of the file about to be opened.
  47.  * lpStg      
  48. - Address of the OLE storage location of the file about to be opened.
  49.  * pwzHostName
  50. - Address of a wide character string that contains the host
  51. application name for the antivirus scanner user interface.
  52.  * pwzOrigURL  
  53. - Address of a wide character string that contains the URL of the origin of a
  54. downloaded file.
  55. }
  56.  
  57.  TMsoavinfo = record
  58.   cbSize : integer;
  59.   info   : ULONG;
  60.   wnd : HWND;
  61.   FullPath : Pointer;
  62.   pwzHostName : PWChar;
  63.   pwzOrigURL  : PWChar;
  64.  end;
  65.  
  66.  //This is the interface an antivirus scanner
  67. //uses to interact with a host application.
  68.  IOfficeAntiVirus = interface(IUnknown)
  69.  ['{56FFCC30-D398-11d0-B2AE-00A0C908FA49}']
  70.   function Scan(pmsoavinfo : PChar) : HResult; stdcall;
  71.  end;
  72.  
  73. function TestBit(const Value: Cardinal; const Bit: byte): Boolean;
  74. procedure GetRegisteredAntiviruses(ProgIDs: TStrings);
  75.  
  76.  
  77. implementation
  78.  
  79. function TestBit(const Value: Cardinal; const Bit: byte): Boolean;
  80. begin
  81.   Result := (Value and (1 shl (Bit mod 32)))  0;
  82. end;
  83.  
  84.  
  85. procedure GetRegisteredAntiviruses(ProgIDs: TStrings);
  86. var
  87.   CatInformation: ICatInformation;
  88.   Enum: IEnumGUID;
  89.   CLSID: TGUID;
  90.   nFetched: Cardinal;
  91.   CatId: TGUID;
  92. begin
  93.   CatInformation := CreateComObject(CLSID_StdComponentCategoryMgr)
  94. as ICatInformation;
  95.   CatId := CATID_MSOfficeAntiVirus;
  96.   OleCheck(CatInformation.EnumClassesOfCategories(1, @CatId, 0, nil, Enum));
  97.   ProgIDs.BeginUpdate;
  98.   try
  99.     ProgIDs.Clear;
  100.     while (Enum.Next(1, CLSID, nFetched) = S_OK) do begin
  101.       ProgIDs.Add(GuidToString(clsid));
  102.     end;
  103.   finally
  104.     ProgIDs.EndUpdate;
  105.   end;
  106. end;
  107.  
  108. end.


Now I will show a small example how to use IOfficeAntiVirus interface to implement own antivirus program for Microsoft Office.

  1. library msoavtest;
  2.  
  3.  
  4. uses
  5.  
  6.   ComServ,
  7.   msoav,
  8.   umsoavtest;
  9.  
  10.  
  11. exports
  12.  
  13.   DllGetClassObject,
  14.   DllCanUnloadNow,
  15.   DllRegisterServer,
  16.   DllUnregisterServer;
  17.  
  18.  
  19. begin
  20. end.  
  21.  
  22. unit umsoavtest;
  23.  
  24. interface
  25.  
  26. uses
  27.   Windows, ActiveX, ComObj, ShlObj, Dialogs, msoav;
  28.  
  29. type
  30.   TMSOTest = class(TComObject, IOfficeAntiVirus)
  31.   protected
  32.    function Scan(pmsoavinfo : PChar) : HResult; stdcall;
  33.   end;
  34.  
  35.  
  36. const
  37.   Class_MsoTest: TGUID = '{F56BE781-C8BE-11D7-8601-00E0184D1E9D}';
  38.  
  39. implementation
  40.  
  41. uses ComServ, SysUtils, ShellApi, Registry;
  42.  
  43.  
  44. procedure UpdateCat(Register: Boolean;  const ClassID:  string);
  45. const
  46.   SCatImplBaseKey = 'CLSID\%s\Implemented Categories';
  47.   SCatImplKey = SCatImplBaseKey + '\%s';
  48.  
  49. var
  50.   CatReg: ICatRegister;
  51.   Rslt: HResult;
  52.   CatInfo: TCATEGORYINFO;
  53.   Description: string;
  54. begin
  55.   Rslt := CoCreateInstance(CLSID_StdComponentCategoryMgr, nil,
  56.     CLSCTX_INPROC_SERVER, ICatRegister, CatReg);
  57.   if Succeeded(Rslt) then
  58.   begin
  59.     if Register then
  60.     begin
  61.       CatInfo.catid := CATID_MSOfficeAntiVirus;
  62.       CatInfo.lcid := $0409;
  63.       StringToWideChar('', CatInfo.szDescription,
  64.         Length('') + 1);
  65.       OleCheck(CatReg.RegisterCategories(1, @CatInfo));
  66.       OleCheck(CatReg.RegisterClassImplCategories
  67. (StringToGUID(ClassID),
  68. 1,
  69. @CATID_MSOfficeAntiVirus));
  70.     end else
  71.     begin
  72.       OleCheck(CatReg.UnRegisterClassImplCategories(StringToGUID(ClassID),
  73. 1,
  74. @CATID_MSOfficeAntiVirus));
  75.  
  76.       DeleteRegKey(Format(SCatImplBaseKey, [ClassID]));
  77.     end;
  78.   end else
  79.   begin
  80.     if Register then
  81.     begin
  82.       CreateRegKey('Component Categories\' +
  83.  GUIDToString(CATID_MSOfficeAntiVirus),
  84. '409',
  85. '');
  86.       CreateRegKey(Format(SCatImplKey,
  87. [ClassID, GUIDToString(CATID_MSOfficeAntiVirus)]),
  88. '',
  89. '');
  90.     end else
  91.     begin
  92.       DeleteRegKey(Format(SCatImplKey,
  93. [ClassID,
  94. GUIDToString(CATID_MSOfficeAntiVirus)]));
  95.       DeleteRegKey(Format(SCatImplBaseKey, [ClassID]));
  96.     end;
  97.   end;
  98.   if Register then
  99.   begin
  100.     Description := GetRegStringValue('CLSID\' + ClassID, '');
  101.     CreateRegKey('AppID\' + ClassID, '', Description);
  102.     CreateRegKey('CLSID\' + ClassID, 'AppID', ClassID);
  103.   end else
  104.     DeleteRegKey('AppID\' + ClassID);
  105. end;
  106.  
  107. { TMSOTest }
  108.  
  109. function TMSOTest.Scan(pmsoavinfo: PChar): HResult;
  110. var
  111.  Info   : TMsoavinfo;
  112.  Struct : TInfoStruct;
  113.  p : pointer;
  114. begin
  115.   p := pointer(pmsoavinfo);
  116.   if not Assigned(p) then
  117.    begin
  118.      //no information available
  119.      Result := S_OK;
  120.      Exit;
  121.    end;
  122.  
  123.   Move(P^, Info, SizeOf(Tmsoavinfo));
  124.   if Info.cbSize  SizeOf(Tmsoavinfo) then
  125.    begin
  126.      //wrong size of the structure
  127.      Result := S_OK;
  128.      Exit;
  129.    end;
  130.   Struct.fIsFile := TestBit(Info.Info, 0);
  131.   Struct.fIsReadOnly := TestBit(Info.Info, 1);
  132.   Struct.fIsInstalled := TestBit(Info.Info, 2);
  133.   Struct.fIsHTTPDownload :=  TestBit(Info.Info, 3);
  134.   if struct.fIsFile then
  135.    begin
  136.      MessageDlg(PWChar(Info.FullPath), mtWarning, [mbOK], 0);
  137.    end;
  138.   Result := S_OK;
  139. end;
  140.  
  141.  
  142. type
  143.   TMSOAvFactory = class(TComObjectFactory)
  144.   public
  145.     procedure UpdateRegistry(Register: Boolean); override;
  146.   end;
  147.  
  148.  
  149. procedure TMSOAVFactory.UpdateRegistry(Register: Boolean);
  150. var
  151.   ClassID: string;
  152. begin
  153.   ClassID := GUIDToString(Class_MsoTest);
  154.   if Register then
  155.   begin
  156.     inherited UpdateRegistry(Register);
  157.     UpdateCat(true, ClassID);
  158.   end
  159.   else
  160.   begin
  161.     UpdateCat(false, ClassID);
  162.     inherited UpdateRegistry(Register);
  163.   end;
  164. end;
  165.  
  166. initialization
  167.   TComObjectFactory.Create(ComServer, TMsoTest, Class_MsoTest,
  168.     'MsoTest', '', ciMultiInstance, tmApartment);
  169. end.

Related Discussions
  • USING DLLS (2001-01-04 01:47:50)
    Hi again Goober ... :o) Let's take NETAPI32.DLL as an example. This DLL is created by Microsoft and the explanation of its use is described in...
  • DOT ON SCREEN (2001-01-03 05:42:55)
    Delphi does not support a Pixel function with a HDC, you have to use the Windows-API-function COLORREF SetPixel( HDC hdc, // handle...
  • HELP WITH CHDIR !!!! TURBO PASCAL VS DELPHI (2001-01-03 08:48:49)
    This is because the ChDir Procedure, the SetCurrentDir and SetCurrentDirectory Functions are changing the directory in the active process. Try...
  • SENDING ICQ MSGS FROM DELPHI (2001-01-03 16:54:03)
    I tried the ICQ Api, but never used it. I've added a simple ICQPager component to delphipages (Winsock/TCPIP).. it might help... Q
  • REPEATING FUNCTIONS (2001-01-04 11:46:34)
    The OnIdle event is not hardware dependent. It is called when the program has idle time. The OnIdle event is in TApplication and is a warpper...
  • MANIPULATING WINDOWS OF OTHER PROGRAMS (2001-01-04 21:11:41)
    You need to use the EnumWindows API function to figure out the handle of each window that you want to manipulate (you may also need to use...
  • PRINTING A PDF FORM (2001-01-07 05:57:11)
    Hello Tom, If you find a delphi solution to bypass the acrobat Printer i am very interrested. Actually i am printing a lot of invoice to the...
  • HOW CAN I "FORCE" WINDOWS TO CREATE A LINK TO A SPECIFIED FILE? (2001-01-06 11:12:17)
    Hi, This should help: uses ShlObj, ActiveX, ComObj, Registry; var MyObject: IUnknown; MySLink: IShellLink; MyPFile: IPersistFile;...
  • DETECTING ACTIVE DESKTOP -- SYSTEMPARAMETERSINFO? (2001-01-07 08:06:54)
    I'll check this out and then accept as answer, cheers :)
  • SHARE API (2001-01-06 15:53:18)
    Thanks for your reply, but I'm not totally stupid :-) I've already looked for this function in Delphi files !! No more problem : I've found a very...
Latest News
Submit News Form Past News
Latest Forum Entries