Subversion Repositories userdetect2

Rev

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