Subversion Repositories userdetect2

Rev

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