Subversion Repositories userdetect2

Rev

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