Subversion Repositories userdetect2

Rev

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