Subversion Repositories userdetect2

Rev

Rev 87 | Rev 91 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit UD2_Obj;
  2.  
  3. interface
  4.  
  5. {$IF CompilerVersion >= 25.0}
  6. {$LEGACYIFEND ON}
  7. {$IFEND}
  8.  
  9. {$INCLUDE 'UserDetect2.inc'}
  10.  
  11. uses
  12.   Windows, SysUtils, Classes, IniFiles, Contnrs, Dialogs, UD2_PluginIntf,
  13.   UD2_PluginStatus, UD2_Utils, UD2_Parsing;
  14.  
  15. const
  16.   UD2_TagDescription = 'Description';
  17.  
  18. type
  19.   TUD2IdentificationEntry = class;
  20.  
  21.   TUD2Plugin = class(TObject)
  22.   protected
  23.     FDetectedIdentifications: TObjectList{<TUD2IdentificationEntry>};
  24.     FOSNotSupportedEnforced: boolean;
  25.     FPluginDLL: string;
  26.     FPluginGUIDSet: boolean;
  27.     FPluginGUID: TGUID;
  28.     FPluginName: WideString;
  29.     FPluginVendor: WideString;
  30.     FPluginVersion: WideString;
  31.     FIdentificationMethodName: WideString;
  32.     FAcceptsDynamicRequests: boolean;
  33.     FIdentificationProcedureStatusCode: UD2_STATUS;
  34.     FIdentificationProcedureStatusCodeDescribed: WideString;
  35.     FLoadingTime: Cardinal;
  36.   public
  37.     // This flag will be set if "AutoOSNotSupportedCompatibility" of the INI manifest had to be enforced/used
  38.     property OSNotSupportedEnforced: boolean read FOSNotSupportedEnforced;
  39.  
  40.     // Data read from the DLL
  41.     property PluginDLL: string read FPluginDLL;
  42.     property PluginGUIDSet: boolean read FPluginGUIDSet;
  43.     property PluginGUID: TGUID read FPluginGUID;
  44.     property PluginName: WideString read FPluginName;
  45.     property PluginVendor: WideString read FPluginVendor;
  46.     property PluginVersion: WideString read FPluginVersion;
  47.     property IdentificationMethodName: WideString read FIdentificationMethodName;
  48.     property AcceptsDynamicRequests: boolean read FAcceptsDynamicRequests;
  49.  
  50.     // ONLY contains the non-failure status code of IdentificationStringW
  51.     property IdentificationProcedureStatusCode: UD2_STATUS read FIdentificationProcedureStatusCode;
  52.     property IdentificationProcedureStatusCodeDescribed: WideString read FIdentificationProcedureStatusCodeDescribed;
  53.  
  54.     // How long did the plugin to load?
  55.     property LoadingTime: Cardinal read FLoadingTime;
  56.  
  57.     function PluginGUIDString: string;
  58.     property DetectedIdentifications: TObjectList{<TUD2IdentificationEntry>} read FDetectedIdentifications;
  59.     destructor Destroy; override;
  60.     constructor Create;
  61.     function AddIdentification(IdStr: WideString): TUD2IdentificationEntry;
  62.  
  63.     function InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings; var outIDs: TArrayOfString): boolean; overload;
  64.     function InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings): boolean; overload;
  65.     function GetDynamicRequestResult(dynamicData: WideString; AErrorOut: TStrings=nil): TArrayOfString;
  66.  
  67.     function EqualsMethodNameOrGuid(idMethodNameOrGUID: string): boolean;
  68.   end;
  69.  
  70.   TUD2IdentificationEntry = class(TObject)
  71.   private
  72.     FIdentificationString: WideString;
  73.     FPlugin: TUD2Plugin;
  74.     FDynamicDataUsed: boolean;
  75.     FDynamicData: WideString;
  76.   public
  77.     property DynamicDataUsed: boolean read FDynamicDataUsed write FDynamicDataUsed;
  78.     property DynamicData: WideString read FDynamicData write FDynamicData;
  79.     property IdentificationString: WideString read FIdentificationString;
  80.     property Plugin: TUD2Plugin read FPlugin;
  81.     procedure GetIdNames(sl: TStrings);
  82.     constructor Create(AIdentificationString: WideString; APlugin: TUD2Plugin);
  83.   end;
  84.  
  85.   TUD2 = class(TObject)
  86.   private
  87.     {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
  88.     FGUIDLookup: TStrings;
  89.     {$ENDIF}
  90.   protected
  91.     FLoadedPlugins: TObjectList{<TUD2Plugin>};
  92.     FIniFile: TMemIniFile;
  93.     FErrors: TStrings;
  94.     FIniFileName: string;
  95.   public
  96.     property IniFileName: string read FIniFileName;
  97.     property Errors: TStrings read FErrors;
  98.     property LoadedPlugins: TObjectList{<TUD2Plugin>} read FLoadedPlugins;
  99.     property IniFile: TMemIniFile read FIniFile;
  100.     procedure GetAllDetectedIDs(outSL: TStrings);
  101.     function FulfilsEverySubterm(conds: TUD2TDFConditionArray; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean; overload;
  102.     function FulfilsEverySubterm(idTerm: WideString; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean; overload;
  103.     function CheckTerm(idTermAndCmd: string; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): TUD2CommandArray;
  104.     function FindPluginByMethodNameOrGuid(idMethodName: string): TUD2Plugin;
  105.     function GetCommandList(ShortTaskName: string; AErrorOut: TStrings=nil): TUD2CommandArray;
  106.     procedure HandlePluginDir(APluginDir, AFileMask: string);
  107.     procedure GetTaskListing(outSL: TStrings);
  108.     constructor Create(AIniFileName: string);
  109.     destructor Destroy; override;
  110.     function TaskExists(ShortTaskName: string): boolean;
  111.     function ReadMetatagString(ShortTaskName, MetatagName: string; DefaultVal: string): string;
  112.     function ReadMetatagBool(ShortTaskName, MetatagName: string; DefaultVal: string): boolean;
  113.     function GetTaskName(AShortTaskName: string): string;
  114.     class function GenericErrorLookup(grStatus: UD2_STATUS): string;
  115.   end;
  116.  
  117. implementation
  118.  
  119. uses
  120.   Math;
  121.  
  122. const
  123.   cchBufferSize = 32768;
  124.  
  125. type
  126.   TUD2PluginLoader = class(TThread)
  127.   protected
  128.     dllFile: string;
  129.     lngID: LANGID;
  130.     useDynamicData: boolean;
  131.     dynamicData: WideString;
  132.     procedure Execute; override;
  133.     function HandleDLL: boolean;
  134.   public
  135.     Plugin: TUD2Plugin;
  136.     Errors: TStringList;
  137.     ResultIdentifiers: TArrayOfString;
  138.     constructor Create(Suspended: boolean; DLL: string; alngid: LANGID; useDynamicData: boolean; dynamicData: WideString);
  139.     destructor Destroy; override;
  140.   end;
  141.  
  142. class function TUD2.GenericErrorLookup(grStatus: UD2_STATUS): string;
  143. resourcestring
  144.   LNG_STATUS_OK_UNSPECIFIED               = 'Success (Unspecified)';
  145.   LNG_STATUS_OK_SINGLELINE                = 'Success (One identifier returned)';
  146.   LNG_STATUS_OK_MULTILINE                 = 'Success (Multiple identifiers returned)';
  147.   LNG_UNKNOWN_SUCCESS                     = 'Success (Unknown status code %s)';
  148.  
  149.   LNG_STATUS_NOTAVAIL_UNSPECIFIED         = 'Not available (Unspecified)';
  150.   LNG_STATUS_NOTAVAIL_OS_NOT_SUPPORTED    = 'Not available (Operating system not supported)';
  151.   LNG_STATUS_NOTAVAIL_HW_NOT_SUPPORTED    = 'Not available (Hardware not supported)';
  152.   LNG_STATUS_NOTAVAIL_NO_ENTITIES         = 'Not available (No entities to identify)';
  153.   LNG_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE = 'Not available (A Windows API call failed. Message: %s)';
  154.   LNG_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC = 'Not available (Arguments required)';
  155.   LNG_STATUS_NOTAVAIL_INVALID_INPUT       = 'Not available (Plugin received invalid input)';
  156.   LNG_UNKNOWN_NOTAVAIL                    = 'Not available (Unknown status code %s)';
  157.  
  158.   LNG_STATUS_FAILURE_UNSPECIFIED          = 'Error (Unspecified)';
  159.   LNG_STATUS_FAILURE_BUFFER_TOO_SMALL     = 'Error (The provided buffer is too small!)';
  160.   LNG_STATUS_FAILURE_INVALID_ARGS         = 'Error (An internal function received invalid arguments!)';
  161.   LNG_STATUS_FAILURE_PLUGIN_NOT_LICENSED  = 'Error (The plugin is not licensed)';
  162.   LNG_STATUS_FAILURE_NO_RETURNED_VALUE    = 'Error (Plugin did not return a status)';
  163.   LNG_STATUS_FAILURE_CATCHED_EXCEPTION    = 'Error (Catched unexpected Exception)';
  164.   LNG_UNKNOWN_FAILED                      = 'Error (Unknown status code %s)';
  165.  
  166.   LNG_UNKNOWN_STATUS                      = 'Unknown status code with unexpected category: %s';
  167. begin
  168.        if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_UNSPECIFIED, false)               then result := LNG_STATUS_OK_UNSPECIFIED
  169.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_SINGLELINE, false)                then result := LNG_STATUS_OK_SINGLELINE
  170.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_MULTILINE, false)                 then result := LNG_STATUS_OK_MULTILINE
  171.  
  172.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_UNSPECIFIED, false)         then result := LNG_STATUS_NOTAVAIL_UNSPECIFIED
  173.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_OS_NOT_SUPPORTED, false)    then result := LNG_STATUS_NOTAVAIL_OS_NOT_SUPPORTED
  174.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_HW_NOT_SUPPORTED, false)    then result := LNG_STATUS_NOTAVAIL_HW_NOT_SUPPORTED
  175.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_NO_ENTITIES, false)         then result := LNG_STATUS_NOTAVAIL_NO_ENTITIES
  176.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE, false) then result := Format(LNG_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE, [FormatOSError(grStatus.dwExtraInfo)])
  177.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC, false) then result := LNG_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC
  178.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_INVALID_INPUT, false)       then result := LNG_STATUS_NOTAVAIL_INVALID_INPUT
  179.  
  180.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_UNSPECIFIED, false)          then result := LNG_STATUS_FAILURE_UNSPECIFIED
  181.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_BUFFER_TOO_SMALL, false)     then result := LNG_STATUS_FAILURE_BUFFER_TOO_SMALL
  182.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_INVALID_ARGS, false)         then result := LNG_STATUS_FAILURE_INVALID_ARGS
  183.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_PLUGIN_NOT_LICENSED, false)  then result := LNG_STATUS_FAILURE_PLUGIN_NOT_LICENSED
  184.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_NO_RETURNED_VALUE, false)    then result := LNG_STATUS_FAILURE_NO_RETURNED_VALUE
  185.   else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_CATCHED_EXCEPTION, false)    then result := LNG_STATUS_FAILURE_CATCHED_EXCEPTION
  186.  
  187.   else if grStatus.wCategory = UD2_STATUSCAT_SUCCESS   then result := Format(LNG_UNKNOWN_SUCCESS,  [UD2_STATUS_FormatStatusCode(grStatus)])
  188.   else if grStatus.wCategory = UD2_STATUSCAT_NOT_AVAIL then result := Format(LNG_UNKNOWN_NOTAVAIL, [UD2_STATUS_FormatStatusCode(grStatus)])
  189.   else if grStatus.wCategory = UD2_STATUSCAT_FAILED    then result := Format(LNG_UNKNOWN_FAILED,   [UD2_STATUS_FormatStatusCode(grStatus)])
  190.   else                                                      result := Format(LNG_UNKNOWN_STATUS,   [UD2_STATUS_FormatStatusCode(grStatus)]);
  191. end;
  192.  
  193. { TUD2Plugin }
  194.  
  195. function TUD2Plugin.PluginGUIDString: string;
  196. begin
  197.   if PluginGUIDSet then
  198.     result := UpperCase(GUIDToString(PluginGUID))
  199.   else
  200.     result := '';
  201. end;
  202.  
  203. function TUD2Plugin.AddIdentification(IdStr: WideString): TUD2IdentificationEntry;
  204. begin
  205.   result := TUD2IdentificationEntry.Create(IdStr, Self);
  206.   DetectedIdentifications.Add(result);
  207. end;
  208.  
  209. destructor TUD2Plugin.Destroy;
  210. begin
  211.   DetectedIdentifications.Free;
  212.   inherited;
  213. end;
  214.  
  215. constructor TUD2Plugin.Create;
  216. begin
  217.   inherited Create;
  218.   FDetectedIdentifications := TObjectList{<TUD2IdentificationEntry>}.Create(true);
  219. end;
  220.  
  221. function TUD2Plugin.InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings; var outIDs: TArrayOfString): boolean;
  222. var
  223.   ude: TUD2IdentificationEntry;
  224.   i: integer;
  225.   id: string;
  226.   l: integer;
  227. begin
  228.   result := false;
  229.  
  230.   SetLength(outIDs, 0);
  231.  
  232.   for i := 0 to FDetectedIdentifications.Count-1 do
  233.   begin
  234.     ude := FDetectedIdentifications.Items[i] as TUD2IdentificationEntry;
  235.     if ude.dynamicDataUsed and (ude.dynamicData = dynamicData) then
  236.     begin
  237.       l := Length(outIDs);
  238.       SetLength(outIDs, l+1);
  239.       outIDs[l] := ude.FIdentificationString;
  240.     end;
  241.   end;
  242.  
  243.   // The dynamic content was already evaluated (and therefore is already added in FDetectedIdentifications).
  244.   if Length(outIDs) > 0 then exit;
  245.  
  246.   outIDs := GetDynamicRequestResult(dynamicData, AErrorOut);
  247.  
  248.   for i := 0 to Length(outIDs)-1 do
  249.   begin
  250.     id := outIDs[i];
  251.  
  252.     ude := AddIdentification(id);
  253.     ude.dynamicDataUsed := true;
  254.     ude.dynamicData := dynamicData;
  255.  
  256.     result := true;
  257.   end;
  258. end;
  259.  
  260. function TUD2Plugin.GetDynamicRequestResult(dynamicData: WideString; AErrorOut: TStrings=nil): TArrayOfString;
  261. var
  262.   lngID: LANGID;
  263.   loader: TUD2PluginLoader;
  264. begin
  265.   lngID := GetSystemDefaultLangID;
  266.  
  267.   loader := TUD2PluginLoader.Create(false, PluginDLL, lngid, true, dynamicData);
  268.   try
  269.     loader.WaitFor;
  270.     result := loader.ResultIdentifiers;
  271.     if Assigned(AErrorOut) then
  272.     begin
  273.       AErrorOut.AddStrings(loader.Errors);
  274.     end;
  275.  
  276.     // TODO: Use assign() instead? or allow TUD2PluginLoader to write the TPlugin object directly?
  277.     //       Should we even overwrite the current plugin data, or return the new plugin?
  278.     FIdentificationProcedureStatusCode := loader.plugin.IdentificationProcedureStatusCode;
  279.     FIdentificationProcedureStatusCodeDescribed := loader.plugin.IdentificationProcedureStatusCodeDescribed;
  280.     FOSNotSupportedEnforced := loader.plugin.OSNotSupportedEnforced;
  281.     FLoadingTime := loader.Plugin.LoadingTime;
  282.  
  283.   finally
  284.     if Assigned(loader.Plugin) then FreeAndNil(loader.Plugin);
  285.     loader.Free;
  286.   end;
  287. end;
  288.  
  289. function TUD2Plugin.EqualsMethodNameOrGuid(idMethodNameOrGUID: string): boolean;
  290. begin
  291.   result := SameText(IdentificationMethodName, idMethodNameOrGUID) or
  292.             SameText(GUIDToString(PluginGUID), idMethodNameOrGUID)
  293. end;
  294.  
  295. function TUD2Plugin.InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings): boolean;
  296. var
  297.   dummy: TArrayOfString;
  298. begin
  299.   result := InvokeDynamicCheck(dynamicData, AErrorOut, dummy)
  300. end;
  301.  
  302. { TUD2IdentificationEntry }
  303.  
  304. procedure TUD2IdentificationEntry.GetIdNames(sl: TStrings);
  305. var
  306.   cond: TUD2TDFCondition;
  307. begin
  308.   cond.idMethodName := Plugin.IdentificationMethodName;
  309.   cond.idStr := IdentificationString;
  310.   cond.dynamicDataUsed := DynamicDataUsed;
  311.   cond.dynamicData := DynamicData;
  312.   sl.Add(UD2_CondToStr(cond));
  313.  
  314.   cond.idMethodName := Plugin.PluginGUIDString;
  315.   sl.Add(UD2_CondToStr(cond));
  316. end;
  317.  
  318. constructor TUD2IdentificationEntry.Create(AIdentificationString: WideString;
  319.   APlugin: TUD2Plugin);
  320. begin
  321.   inherited Create;
  322.  
  323.   // TODO: We need to do this, because ReadSectionValues strips the names of the name-value pairs...
  324.   //       We should correct ReadSectionValues...
  325.   // Example: DriveSerial(c:):2SHSWNHA010807 X    =calc.exe
  326.   // ReadSectionValues will return "DriveSerial(c:):2SHSWNHA010807 X=calc.exe"
  327.   AIdentificationString := Trim(AIdentificationString);
  328.  
  329.   FIdentificationString := AIdentificationString;
  330.   FPlugin := APlugin;
  331. end;
  332.  
  333. { TUD2 }
  334.  
  335. procedure TUD2.HandlePluginDir(APluginDir, AFileMask: string);
  336. Var
  337.   SR: TSearchRec;
  338.   path: string;
  339.   pluginLoader: TUD2PluginLoader;
  340.   tob: TObjectList{<TUD2PluginLoader>};
  341.   i: integer;
  342.   {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
  343.   sPluginID, prevDLL: string;
  344.   {$ENDIF}
  345.   lngid: LANGID;
  346. resourcestring
  347.   LNG_PLUGINS_SAME_GUID = 'Attention: The plugin "%s" and the plugin "%s" have the same identification GUID. The latter will not be loaded.';
  348. begin
  349.   tob := TObjectList{<TUD2PluginLoader>}.Create;
  350.   try
  351.     tob.OwnsObjects := false;
  352.  
  353.     lngID := GetSystemDefaultLangID;
  354.  
  355.     path := APluginDir;
  356.     if path <> '' then path := IncludeTrailingPathDelimiter(path);
  357.  
  358.     if FindFirst(path + AFileMask, 0, SR) = 0 then
  359.     begin
  360.       try
  361.         repeat
  362.           try
  363.             tob.Add(TUD2PluginLoader.Create(false, path + sr.Name, lngid, false, ''));
  364.           except
  365.             on E: Exception do
  366.             begin
  367.               MessageDlg(E.Message, mtError, [mbOK], 0);
  368.             end;
  369.           end;
  370.         until FindNext(SR) <> 0;
  371.       finally
  372.         FindClose(SR);
  373.       end;
  374.     end;
  375.  
  376.     for i := 0 to tob.count-1 do
  377.     begin
  378.       pluginLoader := tob.items[i] as TUD2PluginLoader;
  379.       pluginLoader.WaitFor;
  380.       Errors.AddStrings(pluginLoader.Errors);
  381.       if Assigned(pluginLoader.Plugin) then
  382.       begin
  383.         {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
  384.         if pluginLoader.Plugin.PluginGUIDSet then
  385.         begin
  386.           sPluginID := GUIDToString(pluginLoader.Plugin.PluginGUID);
  387.           prevDLL := FGUIDLookup.Values[sPluginID];
  388.           if (prevDLL <> '') and (prevDLL <> pluginLoader.Plugin.PluginDLL) then
  389.           begin
  390.             Errors.Add(Format(LNG_PLUGINS_SAME_GUID, [prevDLL, pluginLoader.Plugin.PluginDLL]));
  391.             pluginLoader.Plugin.Free;
  392.           end
  393.           else
  394.           begin
  395.             FGUIDLookup.Values[sPluginID] := pluginLoader.Plugin.PluginDLL;
  396.             LoadedPlugins.Add(pluginLoader.Plugin);
  397.           end;
  398.         end
  399.         else
  400.         begin
  401.           LoadedPlugins.Add(pluginLoader.Plugin);
  402.         end;
  403.         {$ELSE}
  404.         LoadedPlugins.Add(pluginLoader.Plugin);
  405.         {$ENDIF}
  406.       end;
  407.       pluginLoader.Free;
  408.     end;
  409.   finally
  410.     tob.free;
  411.   end;
  412. end;
  413.  
  414. destructor TUD2.Destroy;
  415. begin
  416.   FIniFile.Free;
  417.   FLoadedPlugins.Free;
  418.   {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
  419.   FGUIDLookup.Free;
  420.   {$ENDIF}
  421.   FErrors.Free;
  422. end;
  423.  
  424. constructor TUD2.Create(AIniFileName: string);
  425. begin
  426.   FIniFileName := AIniFileName;
  427.   FLoadedPlugins := TObjectList{<TUD2Plugin>}.Create(true);
  428.   FIniFile := TMemIniFile.Create(IniFileName);
  429.   {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
  430.   FGUIDLookup := TStringList.Create;
  431.   {$ENDIF}
  432.   FErrors := TStringList.Create;
  433. end;
  434.  
  435. function TUD2.GetTaskName(AShortTaskName: string): string;
  436. resourcestring
  437.   LNG_NO_DESCRIPTION = '(%s)';
  438. begin
  439.   result := FIniFile.ReadString(AShortTaskName, UD2_TagDescription, Format(LNG_NO_DESCRIPTION, [AShortTaskName]));
  440. end;
  441.  
  442. procedure TUD2.GetTaskListing(outSL: TStrings);
  443. var
  444.   sl: TStringList;
  445.   i: integer;
  446.   desc: string;
  447. begin
  448.   sl := TStringList.Create;
  449.   try
  450.     FIniFile.ReadSections(sl);
  451.     for i := 0 to sl.Count-1 do
  452.     begin
  453.       desc := GetTaskName(sl.Strings[i]);
  454.       outSL.Values[sl.Strings[i]] := desc;
  455.     end;
  456.   finally
  457.     sl.Free;
  458.   end;
  459. end;
  460.  
  461. function TUD2.TaskExists(ShortTaskName: string): boolean;
  462. begin
  463.   result := FIniFile.SectionExists(ShortTaskName);
  464. end;
  465.  
  466. function TUD2.ReadMetatagString(ShortTaskName, MetatagName: string; DefaultVal: string): string;
  467. begin
  468.   result := IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal);
  469. end;
  470.  
  471. function TUD2.ReadMetatagBool(ShortTaskName, MetatagName: string; DefaultVal: string): boolean;
  472. begin
  473.   // DefaultVal is a string, because we want to allow an empty string, in case the
  474.   // user wishes an Exception in case the string is not a valid boolean string
  475.   result := BetterInterpreteBool(IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal));
  476. end;
  477.  
  478. (*
  479.  
  480. NAMING EXAMPLE: $CASESENSITIVE$ComputerName(dynXYZ):ABC&&User:John=calc.exe$RIOD$
  481.  
  482.         idTerm:       ComputerName(dynXYZ):ABC&&User:John
  483.         idName:       ComputerName:ABC
  484.         IdMethodName: ComputerName
  485.         IdStr         ABC
  486.         cmd:          calc.exe
  487.         dynamicData:  dynXYZ
  488.  
  489. *)
  490.  
  491. procedure TUD2.GetAllDetectedIDs(outSL: TStrings);
  492. var
  493.   i, j: integer;
  494.   pl: TUD2Plugin;
  495.   ude: TUD2IdentificationEntry;
  496. begin
  497.   for i := 0 to LoadedPlugins.Count-1 do
  498.   begin
  499.     pl := LoadedPlugins.Items[i] as TUD2Plugin;
  500.     for j := 0 to pl.DetectedIdentifications.Count-1 do
  501.     begin
  502.       ude := pl.DetectedIdentifications.Items[j] as TUD2IdentificationEntry;
  503.       ude.GetIdNames(outSL);
  504.     end;
  505.   end;
  506. end;
  507.  
  508. function TUD2.FulfilsEverySubterm(conds: TUD2TDFConditionArray; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean;
  509. begin
  510.   result := FulfilsEverySubterm(UD2_CondsToStr(conds), slIdNames, AErrorOut);
  511. end;
  512.  
  513. function TUD2.FulfilsEverySubterm(idTerm: WideString; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean;
  514. var
  515.   i: integer;
  516.   p: TUD2Plugin;
  517.   cleanUpStringList: boolean;
  518.   conds: TUD2TDFConditionArray;
  519.   cond: TUD2TDFCondition;
  520.   idName: string;
  521. begin
  522.   {$IFDEF NO_CONDITIONS_IS_FAILURE}
  523.   if idTerm = '' then
  524.   begin
  525.     SetLength(conds, 0);
  526.     result := false;
  527.     Exit;
  528.   end;
  529.   {$ENDIF}
  530.  
  531.   cleanUpStringList := slIdNames = nil;
  532.   try
  533.     if cleanUpStringList then
  534.     begin
  535.       slIdNames := TStringList.Create;
  536.       GetAllDetectedIDs(slIdNames);
  537.     end;
  538.  
  539.     conds := UD2P_ParseConditions(idTerm);
  540.  
  541.     result := true;
  542.     for i := Low(conds) to High(conds) do
  543.     begin
  544.       cond := conds[i];
  545.  
  546.       if cond.dynamicDataUsed then
  547.       begin
  548.         p := FindPluginByMethodNameOrGuid(cond.idMethodName);
  549.         if Assigned(p) then
  550.         begin
  551.           if p.InvokeDynamicCheck(cond.dynamicData, AErrorOut) then
  552.           begin
  553.             // Reload the identifications
  554.             slIdNames.Clear;
  555.             GetAllDetectedIDs(slIdNames);
  556.           end;
  557.         end;
  558.       end;
  559.  
  560.       idName := UD2_CondToStr(cond);
  561.  
  562.       if (not cond.caseSensitive and (slIdNames.IndexOf(idName) = -1)) or
  563.          (cond.caseSensitive and (IndexOf_CS(slIdNames, idName) = -1)) then
  564.       begin
  565.         result := false;
  566.         break;
  567.       end;
  568.     end;
  569.   finally
  570.     if cleanUpStringList and Assigned(slIdNames) then
  571.       slIdNames.Free;
  572.   end;
  573. end;
  574.  
  575. function TUD2.FindPluginByMethodNameOrGuid(idMethodName: string): TUD2Plugin;
  576. var
  577.   i: integer;
  578.   p: TUD2Plugin;
  579. begin
  580.   result := nil;
  581.   for i := 0 to LoadedPlugins.Count-1 do
  582.   begin
  583.     p := LoadedPlugins.Items[i] as TUD2Plugin;
  584.  
  585.     if p.EqualsMethodNameOrGuid(idMethodName) then
  586.     begin
  587.       result := p;
  588.       Exit;
  589.     end;
  590.   end;
  591. end;
  592.  
  593. function TUD2.GetCommandList(ShortTaskName: string; AErrorOut: TStrings=nil): TUD2CommandArray;
  594. var
  595.   i, j, l: integer;
  596.   slSV, slIdNames: TStrings;
  597.   tmpCmds: TUD2CommandArray;
  598. begin
  599.   SetLength(result, 0);
  600.   SetLength(tmpCmds, 0);
  601.  
  602.   slIdNames := TStringList.Create;
  603.   try
  604.     GetAllDetectedIDs(slIdNames);
  605.  
  606.     slSV := TStringList.Create;
  607.     try
  608.       FIniFile.ReadSectionValues(ShortTaskName, slSV);
  609.       for i := 0 to slSV.Count-1 do
  610.       begin
  611.         tmpCmds := CheckTerm(slSV.Strings[i], slIdNames, AErrorOut);
  612.         for j := Low(tmpCmds) to High(tmpCmds) do
  613.         begin
  614.           l := Length(result);
  615.           SetLength(result, l+1);
  616.           result[l] := tmpCmds[j];
  617.         end;
  618.       end;
  619.     finally
  620.       slSV.Free;
  621.     end;
  622.   finally
  623.     slIdNames.Free;
  624.   end;
  625. end;
  626.  
  627. function TUD2.CheckTerm(idTermAndCmd: string; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): TUD2CommandArray;
  628. var
  629.   slIdNamesCreated: boolean;
  630.   ent: TUD2TDFEntry;
  631. begin
  632.   SetLength(result, 0);
  633.  
  634.   slIdNamesCreated := false;
  635.   try
  636.     if not Assigned(slIdNames) then
  637.     begin
  638.       slIdNamesCreated := true;
  639.       slIdNames := TStringList.Create;
  640.       GetAllDetectedIDs(slIdNames);
  641.     end;
  642.  
  643.     if not UD2P_ParseTdfLine(idTermAndCmd, ent) then Exit;
  644.     if FulfilsEverySubterm(ent.ids, slIdNames, AErrorOut) then
  645.     begin
  646.       result := ent.commands;
  647.     end;
  648.   finally
  649.     if slIdNamesCreated then slIdNames.Free;
  650.   end;
  651. end;
  652.  
  653. { TUD2PluginLoader }
  654.  
  655. procedure TUD2PluginLoader.Execute;
  656. begin
  657.   inherited;
  658.  
  659.   HandleDLL;
  660. end;
  661.  
  662. constructor TUD2PluginLoader.Create(Suspended: boolean; DLL: string; alngid: LANGID; useDynamicData: boolean; dynamicData: WideString);
  663. begin
  664.   inherited Create(Suspended);
  665.   dllfile := dll;
  666.   Plugin := nil;
  667.   Errors := TStringList.Create;
  668.   lngid := alngid;
  669.   self.useDynamicData := useDynamicData;
  670.   Self.dynamicData := dynamicData;
  671. end;
  672.  
  673. destructor TUD2PluginLoader.Destroy;
  674. begin
  675.   Errors.Free;
  676.   inherited;
  677. end;
  678.  
  679. function TUD2PluginLoader.HandleDLL: boolean;
  680. var
  681.   sIdentifier: WideString;
  682.   buf: array[0..cchBufferSize-1] of WideChar;
  683.   pluginInterfaceID: TGUID;
  684.   dllHandle: Cardinal;
  685.   fPluginInterfaceID: TFuncPluginInterfaceID;
  686.   fPluginIdentifier: TFuncPluginIdentifier;
  687.   fPluginNameW: TFuncPluginNameW;
  688.   fPluginVendorW: TFuncPluginVendorW;
  689.   fPluginVersionW: TFuncPluginVersionW;
  690.   fIdentificationMethodNameW: TFuncIdentificationMethodNameW;
  691.   fIdentificationStringW: TFuncIdentificationStringW;
  692.   fDynamicIdentificationStringW: TFuncDynamicIdentificationStringW;
  693.   fCheckLicense: TFuncCheckLicense;
  694.   fDescribeOwnStatusCodeW: TFuncDescribeOwnStatusCodeW;
  695.   statusCode: UD2_STATUS;
  696.   i: integer;
  697.   starttime, endtime, time: cardinal;
  698.   bakErrorMode: DWORD;
  699.   err: DWORD;
  700.  
  701.   function _ErrorLookup(statusCode: UD2_STATUS): WideString;
  702.   var
  703.     ret: BOOL;
  704.     buf: array[0..cchBufferSize-1] of WideChar;
  705.   begin
  706.     if Assigned(fDescribeOwnStatusCodeW) then
  707.     begin
  708.       ZeroMemory(@buf, cchBufferSize);
  709.       ret := fDescribeOwnStatusCodeW(@buf, cchBufferSize, statusCode, lngID);
  710.       if ret then
  711.       begin
  712.         result := PWideChar(@buf);
  713.         Exit;
  714.       end;
  715.     end;
  716.     result := TUD2.GenericErrorLookup(statusCode);
  717.   end;
  718.  
  719.   function _ApplyCompatibilityGUID: boolean;
  720.   var
  721.     iniConfig: TIniFile;
  722.     sOverrideGUID: string;
  723.     sPluginConfigFile: string;
  724.   begin
  725.     result := false;
  726.     sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
  727.     if FileExists(sPluginConfigFile) then
  728.     begin
  729.       iniConfig := TIniFile.Create(sPluginConfigFile);
  730.       try
  731.         sOverrideGUID := iniConfig.ReadString('Compatibility', 'OverrideGUID', '');
  732.         if sOverrideGUID <> '' then
  733.         begin
  734.           Plugin.FPluginGUIDSet := true;
  735.           Plugin.FPluginGUID := StringToGUID(sOverrideGUID);
  736.           result := true;
  737.         end;
  738.       finally
  739.         iniConfig.Free;
  740.       end;
  741.     end;
  742.   end;
  743.  
  744.   function _AutoOSNotSupportedMode: integer;
  745.   var
  746.     iniConfig: TIniFile;
  747.     sPluginConfigFile: string;
  748.   begin
  749.     result := 0;
  750.     sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
  751.     if FileExists(sPluginConfigFile) then
  752.     begin
  753.       iniConfig := TIniFile.Create(sPluginConfigFile);
  754.       try
  755.         result := iniConfig.ReadInteger('Compatibility', 'AutoOSNotSupported', 0);
  756.       finally
  757.         iniConfig.Free;
  758.       end;
  759.     end;
  760.   end;
  761.  
  762.   procedure _OverwriteStatusToOSNotSupported;
  763.   begin
  764.     Plugin := TUD2Plugin.Create;
  765.     Plugin.FPluginDLL := dllFile;
  766.     statusCode := UD2_STATUS_NOTAVAIL_OS_NOT_SUPPORTED;
  767.     Plugin.FIdentificationProcedureStatusCode := statusCode;
  768.     Plugin.FIdentificationProcedureStatusCodeDescribed := _ErrorLookup(statusCode);
  769.     Plugin.FOSNotSupportedEnforced := true;
  770.     result := true;
  771.   end;
  772.  
  773. resourcestring
  774.   LNG_DLL_NOT_LOADED = 'Plugin DLL "%s" could not be loaded: %s';
  775.   LNG_METHOD_NOT_FOUND = 'Method "%s" not found in plugin "%s". The DLL is probably not a valid plugin DLL.';
  776.   LNG_INVALID_PLUGIN = 'The plugin "%s" is not a valid plugin for this application.';
  777.   LNG_METHOD_FAILURE = 'Error "%s" at method "%s" of plugin "%s".';
  778.   LNG_EXCEPTION = 'Fatal error while loading "%s" (%s: %s)';
  779. begin
  780.   result := false;
  781.   startTime := GetTickCount;
  782.  
  783.   try
  784.     bakErrorMode := 0;
  785.     UD2_SetThreadErrorMode(SEM_FAILCRITICALERRORS, Pointer(bakErrorMode));
  786.     try
  787.       dllHandle := LoadLibrary(PChar(dllFile));
  788.       if dllHandle = 0 then
  789.       begin
  790.         err := GetLastError;
  791.  
  792.         if ((_AutoOSNotSupportedMode = 1) and ((err = ERROR_DLL_NOT_FOUND) or (err = ERROR_PROC_NOT_FOUND))) or
  793.            (_AutoOSNotSupportedMode >= 2) then
  794.         begin
  795.           _OverwriteStatusToOSNotSupported;
  796.           Exit;
  797.         end;
  798.  
  799.         Errors.Add(Format(LNG_DLL_NOT_LOADED, [dllFile, SysErrorMessage(err)]));
  800.         Exit;
  801.       end;
  802.       try
  803.         @fPluginInterfaceID := GetProcAddress(dllHandle, mnPluginInterfaceID);
  804.         if not Assigned(fPluginInterfaceID) then
  805.         begin
  806.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginInterfaceID, dllFile]));
  807.           Exit;
  808.         end;
  809.         pluginInterfaceID := fPluginInterfaceID();
  810.         if not IsEqualGUID(pluginInterfaceID, GUID_USERDETECT2_IDPLUGIN_V1) then
  811.         begin
  812.           Errors.Add(Format(LNG_INVALID_PLUGIN, [dllFile]));
  813.           Exit;
  814.         end;
  815.  
  816.         Plugin := TUD2Plugin.Create;
  817.         Plugin.FPluginDLL := dllFile;
  818.  
  819.         @fDynamicIdentificationStringW := GetProcAddress(dllHandle, mnDynamicIdentificationStringW);
  820.         Plugin.FAcceptsDynamicRequests := Assigned(fDynamicIdentificationStringW);
  821.  
  822.         fIdentificationStringW := nil;
  823.         if useDynamicData then
  824.         begin
  825.           if not Plugin.AcceptsDynamicRequests then
  826.           begin
  827.             // TODO xxx: Darf hier ein fataler Fehler entstehen, obwohl dieses Szenario nur durch die INI file auftreten kann?
  828.             // TODO (allgemein): doku
  829.             Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnDynamicIdentificationStringW, dllFile]));
  830.             Exit;
  831.           end;
  832.         end
  833.         else
  834.         begin
  835.           @fIdentificationStringW := GetProcAddress(dllHandle, mnIdentificationStringW);
  836.           if not Assigned(fIdentificationStringW) then
  837.           begin
  838.             Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationStringW, dllFile]));
  839.             Exit;
  840.           end;
  841.         end;
  842.  
  843.         @fPluginNameW := GetProcAddress(dllHandle, mnPluginNameW);
  844.         if not Assigned(fPluginNameW) then
  845.         begin
  846.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginNameW, dllFile]));
  847.           Exit;
  848.         end;
  849.  
  850.         @fPluginVendorW := GetProcAddress(dllHandle, mnPluginVendorW);
  851.         if not Assigned(fPluginVendorW) then
  852.         begin
  853.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginVendorW, dllFile]));
  854.           Exit;
  855.         end;
  856.  
  857.         @fPluginVersionW := GetProcAddress(dllHandle, mnPluginVersionW);
  858.         if not Assigned(fPluginVersionW) then
  859.         begin
  860.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginVersionW, dllFile]));
  861.           Exit;
  862.         end;
  863.  
  864.         @fCheckLicense := GetProcAddress(dllHandle, mnCheckLicense);
  865.         if not Assigned(fCheckLicense) then
  866.         begin
  867.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnCheckLicense, dllFile]));
  868.           Exit;
  869.         end;
  870.  
  871.         @fIdentificationMethodNameW := GetProcAddress(dllHandle, mnIdentificationMethodNameW);
  872.         if not Assigned(fIdentificationMethodNameW) then
  873.         begin
  874.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationMethodNameW, dllFile]));
  875.           Exit;
  876.         end;
  877.  
  878.         @fDescribeOwnStatusCodeW := GetProcAddress(dllHandle, mnDescribeOwnStatusCodeW);
  879.         if not Assigned(fDescribeOwnStatusCodeW) then
  880.         begin
  881.           Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnDescribeOwnStatusCodeW, dllFile]));
  882.           Exit;
  883.         end;
  884.  
  885.         if not _ApplyCompatibilityGUID then
  886.         begin
  887.           @fPluginIdentifier := GetProcAddress(dllHandle, mnPluginIdentifier);
  888.           if not Assigned(fPluginIdentifier) then
  889.           begin
  890.             Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginIdentifier, dllFile]));
  891.             Exit;
  892.           end;
  893.           Plugin.FPluginGUIDSet := true;
  894.           Plugin.FPluginGUID := fPluginIdentifier();
  895.         end;
  896.  
  897.         statusCode := fCheckLicense(nil);
  898.         if statusCode.wCategory = UD2_STATUSCAT_FAILED then
  899.         begin
  900.           Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnCheckLicense, dllFile]));
  901.           Exit;
  902.         end;
  903.  
  904.         ZeroMemory(@buf, cchBufferSize);
  905.         statusCode := fPluginNameW(@buf, cchBufferSize, lngID);
  906.              if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FPluginName := PWideChar(@buf)
  907.         else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FPluginName := ''
  908.         else
  909.         begin
  910.           Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginNameW, dllFile]));
  911.           Exit;
  912.         end;
  913.  
  914.         ZeroMemory(@buf, cchBufferSize);
  915.         statusCode := fPluginVendorW(@buf, cchBufferSize, lngID);
  916.              if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FPluginVendor := PWideChar(@buf)
  917.         else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FPluginVendor := ''
  918.         else
  919.         begin
  920.           Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginVendorW, dllFile]));
  921.           Exit;
  922.         end;
  923.  
  924.         ZeroMemory(@buf, cchBufferSize);
  925.         statusCode := fPluginVersionW(@buf, cchBufferSize, lngID);
  926.              if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FPluginVersion := PWideChar(@buf)
  927.         else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FPluginVersion := ''
  928.         else
  929.         begin
  930.           Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginVersionW, dllFile]));
  931.           Exit;
  932.         end;
  933.  
  934.         ZeroMemory(@buf, cchBufferSize);
  935.         statusCode := fIdentificationMethodNameW(@buf, cchBufferSize);
  936.              if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FIdentificationMethodName := PWideChar(@buf)
  937.         else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FIdentificationMethodName := ''
  938.         else
  939.         begin
  940.           Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnIdentificationMethodNameW, dllFile]));
  941.           Exit;
  942.         end;
  943.  
  944.         ZeroMemory(@buf, cchBufferSize);
  945.         statusCode := UD2_STATUS_FAILURE_NO_RETURNED_VALUE; // This status will be used when the DLL does not return anything (which is an error by the developer)
  946.         if useDynamicData then
  947.         begin
  948.           statusCode := fDynamicIdentificationStringW(@buf, cchBufferSize, PWideChar(dynamicData));
  949.         end
  950.         else
  951.         begin
  952.           statusCode := fIdentificationStringW(@buf, cchBufferSize);
  953.         end;
  954.         Plugin.FIdentificationProcedureStatusCode := statusCode;
  955.         Plugin.FIdentificationProcedureStatusCodeDescribed := _ErrorLookup(statusCode);
  956.         if statusCode.wCategory = UD2_STATUSCAT_SUCCESS then
  957.         begin
  958.           sIdentifier := PWideChar(@buf);
  959.           if UD2_STATUS_Equal(statusCode, UD2_STATUS_OK_MULTILINE, false) then
  960.           begin
  961.             // Multiple identifiers (e.g. multiple MAC addresses are delimited via UD2_MULTIPLE_ITEMS_DELIMITER)
  962.             SetLength(ResultIdentifiers, 0);
  963.             ResultIdentifiers := SplitString(UD2_MULTIPLE_ITEMS_DELIMITER, sIdentifier);
  964.             for i := Low(ResultIdentifiers) to High(ResultIdentifiers) do
  965.             begin
  966.               Plugin.AddIdentification(ResultIdentifiers[i]);
  967.             end;
  968.           end
  969.           else
  970.           begin
  971.             Plugin.AddIdentification(sIdentifier);
  972.  
  973.             SetLength(ResultIdentifiers, 1);
  974.             ResultIdentifiers[0] := sIdentifier;
  975.           end;
  976.         end
  977.         else if statusCode.wCategory <> UD2_STATUSCAT_NOT_AVAIL then
  978.         begin
  979.           if _AutoOSNotSupportedMode >= 3 then
  980.           begin
  981.             _OverwriteStatusToOSNotSupported;
  982.             Exit;
  983.           end;
  984.  
  985.           // Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnIdentificationStringW, dllFile]));
  986.           Errors.Add(Format(LNG_METHOD_FAILURE, [Plugin.IdentificationProcedureStatusCodeDescribed, mnIdentificationStringW, dllFile]));
  987.           Exit;
  988.         end;
  989.  
  990.         result := true;
  991.       finally
  992.         if not result and Assigned(Plugin) then FreeAndNil(Plugin);
  993.         FreeLibrary(dllHandle);
  994.       end;
  995.     finally
  996.       UD2_SetThreadErrorMode(bakErrorMode, nil);
  997.  
  998.       if result then
  999.       begin
  1000.         endtime := GetTickCount;
  1001.         time := endtime - starttime;
  1002.         if endtime < starttime then time := High(Cardinal) - time;
  1003.         Plugin.FLoadingTime := time;
  1004.       end;
  1005.     end;
  1006.   except
  1007.     // TODO: when an exception happens in a cdecl DLL, then this code is somehow not
  1008.     // executed. Probably the memory is corrupted. Anyway, a cdecl DLL shall NEVER
  1009.     // raise an Exception.
  1010.     on E: Exception do
  1011.     begin
  1012.       Errors.Add(Format(LNG_EXCEPTION, [dllFile, E.ClassName, E.Message]));
  1013.       Exit;
  1014.     end;
  1015.   end;
  1016. end;
  1017.  
  1018. end.
  1019.