Subversion Repositories userdetect2

Rev

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