Subversion Repositories userdetect2

Rev

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