Subversion Repositories userdetect2

Rev

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