Subversion Repositories userdetect2

Rev

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