Subversion Repositories delphiutils

Rev

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