Subversion Repositories userdetect2

Rev

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