Subversion Repositories userdetect2

Rev

Rev 87 | Rev 91 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
68 daniel-mar 1
unit UD2_Obj;
2
 
3
interface
4
 
5
{$IF CompilerVersion >= 25.0}
6
{$LEGACYIFEND ON}
7
{$IFEND}
8
 
70 daniel-mar 9
{$INCLUDE 'UserDetect2.inc'}
10
 
68 daniel-mar 11
uses
71 daniel-mar 12
  Windows, SysUtils, Classes, IniFiles, Contnrs, Dialogs, UD2_PluginIntf,
85 daniel-mar 13
  UD2_PluginStatus, UD2_Utils, UD2_Parsing;
68 daniel-mar 14
 
90 daniel-mar 15
const
16
  UD2_TagDescription = 'Description';
17
 
68 daniel-mar 18
type
83 daniel-mar 19
  TUD2IdentificationEntry = class;
20
 
68 daniel-mar 21
  TUD2Plugin = class(TObject)
22
  protected
23
    FDetectedIdentifications: TObjectList{<TUD2IdentificationEntry>};
87 daniel-mar 24
    FOSNotSupportedEnforced: boolean;
25
    FPluginDLL: string;
26
    FPluginGUIDSet: boolean;
27
    FPluginGUID: TGUID;
28
    FPluginName: WideString;
29
    FPluginVendor: WideString;
30
    FPluginVersion: WideString;
31
    FIdentificationMethodName: WideString;
32
    FAcceptsDynamicRequests: boolean;
33
    FIdentificationProcedureStatusCode: UD2_STATUS;
34
    FIdentificationProcedureStatusCodeDescribed: WideString;
35
    FLoadingTime: Cardinal;
68 daniel-mar 36
  public
82 daniel-mar 37
    // This flag will be set if "AutoOSNotSupportedCompatibility" of the INI manifest had to be enforced/used
87 daniel-mar 38
    property OSNotSupportedEnforced: boolean read FOSNotSupportedEnforced;
86 daniel-mar 39
 
87 daniel-mar 40
    // Data read from the DLL
41
    property PluginDLL: string read FPluginDLL;
42
    property PluginGUIDSet: boolean read FPluginGUIDSet;
43
    property PluginGUID: TGUID read FPluginGUID;
44
    property PluginName: WideString read FPluginName;
45
    property PluginVendor: WideString read FPluginVendor;
46
    property PluginVersion: WideString read FPluginVersion;
47
    property IdentificationMethodName: WideString read FIdentificationMethodName;
48
    property AcceptsDynamicRequests: boolean read FAcceptsDynamicRequests;
70 daniel-mar 49
 
50
    // ONLY contains the non-failure status code of IdentificationStringW
87 daniel-mar 51
    property IdentificationProcedureStatusCode: UD2_STATUS read FIdentificationProcedureStatusCode;
52
    property IdentificationProcedureStatusCodeDescribed: WideString read FIdentificationProcedureStatusCodeDescribed;
83 daniel-mar 53
 
87 daniel-mar 54
    // How long did the plugin to load?
55
    property LoadingTime: Cardinal read FLoadingTime;
56
 
68 daniel-mar 57
    function PluginGUIDString: string;
83 daniel-mar 58
    property DetectedIdentifications: TObjectList{<TUD2IdentificationEntry>} read FDetectedIdentifications;
68 daniel-mar 59
    destructor Destroy; override;
60
    constructor Create;
83 daniel-mar 61
    function AddIdentification(IdStr: WideString): TUD2IdentificationEntry;
62
 
90 daniel-mar 63
    function InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings; var outIDs: TArrayOfString): boolean; overload;
64
    function InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings): boolean; overload;
65
    function GetDynamicRequestResult(dynamicData: WideString; AErrorOut: TStrings=nil): TArrayOfString;
83 daniel-mar 66
 
67
    function EqualsMethodNameOrGuid(idMethodNameOrGUID: string): boolean;
68 daniel-mar 68
  end;
69
 
70
  TUD2IdentificationEntry = class(TObject)
71
  private
72
    FIdentificationString: WideString;
73
    FPlugin: TUD2Plugin;
83 daniel-mar 74
    FDynamicDataUsed: boolean;
87 daniel-mar 75
    FDynamicData: WideString;
68 daniel-mar 76
  public
83 daniel-mar 77
    property DynamicDataUsed: boolean read FDynamicDataUsed write FDynamicDataUsed;
87 daniel-mar 78
    property DynamicData: WideString read FDynamicData write FDynamicData;
68 daniel-mar 79
    property IdentificationString: WideString read FIdentificationString;
80
    property Plugin: TUD2Plugin read FPlugin;
81
    procedure GetIdNames(sl: TStrings);
82
    constructor Create(AIdentificationString: WideString; APlugin: TUD2Plugin);
83
  end;
84
 
85
  TUD2 = class(TObject)
86
  private
70 daniel-mar 87
    {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
68 daniel-mar 88
    FGUIDLookup: TStrings;
70 daniel-mar 89
    {$ENDIF}
68 daniel-mar 90
  protected
91
    FLoadedPlugins: TObjectList{<TUD2Plugin>};
92
    FIniFile: TMemIniFile;
93
    FErrors: TStrings;
94
    FIniFileName: string;
95
  public
96
    property IniFileName: string read FIniFileName;
97
    property Errors: TStrings read FErrors;
98
    property LoadedPlugins: TObjectList{<TUD2Plugin>} read FLoadedPlugins;
99
    property IniFile: TMemIniFile read FIniFile;
84 daniel-mar 100
    procedure GetAllDetectedIDs(outSL: TStrings);
90 daniel-mar 101
    function FulfilsEverySubterm(conds: TUD2TDFConditionArray; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean; overload;
102
    function FulfilsEverySubterm(idTerm: WideString; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean; overload;
103
    function CheckTerm(idTermAndCmd: string; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): TUD2CommandArray;
83 daniel-mar 104
    function FindPluginByMethodNameOrGuid(idMethodName: string): TUD2Plugin;
90 daniel-mar 105
    function GetCommandList(ShortTaskName: string; AErrorOut: TStrings=nil): TUD2CommandArray;
80 daniel-mar 106
    procedure HandlePluginDir(APluginDir, AFileMask: string);
68 daniel-mar 107
    procedure GetTaskListing(outSL: TStrings);
108
    constructor Create(AIniFileName: string);
109
    destructor Destroy; override;
110
    function TaskExists(ShortTaskName: string): boolean;
80 daniel-mar 111
    function ReadMetatagString(ShortTaskName, MetatagName: string; DefaultVal: string): string;
112
    function ReadMetatagBool(ShortTaskName, MetatagName: string; DefaultVal: string): boolean;
68 daniel-mar 113
    function GetTaskName(AShortTaskName: string): string;
71 daniel-mar 114
    class function GenericErrorLookup(grStatus: UD2_STATUS): string;
68 daniel-mar 115
  end;
116
 
117
implementation
118
 
119
uses
83 daniel-mar 120
  Math;
68 daniel-mar 121
 
84 daniel-mar 122
const
123
  cchBufferSize = 32768;
124
 
69 daniel-mar 125
type
126
  TUD2PluginLoader = class(TThread)
127
  protected
128
    dllFile: string;
129
    lngID: LANGID;
83 daniel-mar 130
    useDynamicData: boolean;
131
    dynamicData: WideString;
69 daniel-mar 132
    procedure Execute; override;
70 daniel-mar 133
    function HandleDLL: boolean;
69 daniel-mar 134
  public
90 daniel-mar 135
    Plugin: TUD2Plugin;
69 daniel-mar 136
    Errors: TStringList;
83 daniel-mar 137
    ResultIdentifiers: TArrayOfString;
138
    constructor Create(Suspended: boolean; DLL: string; alngid: LANGID; useDynamicData: boolean; dynamicData: WideString);
69 daniel-mar 139
    destructor Destroy; override;
140
  end;
141
 
71 daniel-mar 142
class function TUD2.GenericErrorLookup(grStatus: UD2_STATUS): string;
68 daniel-mar 143
resourcestring
80 daniel-mar 144
  LNG_STATUS_OK_UNSPECIFIED               = 'Success (Unspecified)';
145
  LNG_STATUS_OK_SINGLELINE                = 'Success (One identifier returned)';
146
  LNG_STATUS_OK_MULTILINE                 = 'Success (Multiple identifiers returned)';
147
  LNG_UNKNOWN_SUCCESS                     = 'Success (Unknown status code %s)';
69 daniel-mar 148
 
80 daniel-mar 149
  LNG_STATUS_NOTAVAIL_UNSPECIFIED         = 'Not available (Unspecified)';
150
  LNG_STATUS_NOTAVAIL_OS_NOT_SUPPORTED    = 'Not available (Operating system not supported)';
151
  LNG_STATUS_NOTAVAIL_HW_NOT_SUPPORTED    = 'Not available (Hardware not supported)';
152
  LNG_STATUS_NOTAVAIL_NO_ENTITIES         = 'Not available (No entities to identify)';
153
  LNG_STATUS_NOTAVAIL_WINAPI_CALL_FAILURE = 'Not available (A Windows API call failed. Message: %s)';
83 daniel-mar 154
  LNG_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC = 'Not available (Arguments required)';
90 daniel-mar 155
  LNG_STATUS_NOTAVAIL_INVALID_INPUT       = 'Not available (Plugin received invalid input)';
80 daniel-mar 156
  LNG_UNKNOWN_NOTAVAIL                    = 'Not available (Unknown status code %s)';
69 daniel-mar 157
 
82 daniel-mar 158
  LNG_STATUS_FAILURE_UNSPECIFIED          = 'Error (Unspecified)';
159
  LNG_STATUS_FAILURE_BUFFER_TOO_SMALL     = 'Error (The provided buffer is too small!)';
90 daniel-mar 160
  LNG_STATUS_FAILURE_INVALID_ARGS         = 'Error (An internal function received invalid arguments!)';
82 daniel-mar 161
  LNG_STATUS_FAILURE_PLUGIN_NOT_LICENSED  = 'Error (The plugin is not licensed)';
162
  LNG_STATUS_FAILURE_NO_RETURNED_VALUE    = 'Error (Plugin did not return a status)';
163
  LNG_STATUS_FAILURE_CATCHED_EXCEPTION    = 'Error (Catched unexpected Exception)';
80 daniel-mar 164
  LNG_UNKNOWN_FAILED                      = 'Error (Unknown status code %s)';
69 daniel-mar 165
 
71 daniel-mar 166
  LNG_UNKNOWN_STATUS                      = 'Unknown status code with unexpected category: %s';
68 daniel-mar 167
begin
71 daniel-mar 168
       if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_UNSPECIFIED, false)               then result := LNG_STATUS_OK_UNSPECIFIED
169
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_SINGLELINE, false)                then result := LNG_STATUS_OK_SINGLELINE
170
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_OK_MULTILINE, false)                 then result := LNG_STATUS_OK_MULTILINE
69 daniel-mar 171
 
71 daniel-mar 172
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_UNSPECIFIED, false)         then result := LNG_STATUS_NOTAVAIL_UNSPECIFIED
173
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_OS_NOT_SUPPORTED, false)    then result := LNG_STATUS_NOTAVAIL_OS_NOT_SUPPORTED
174
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_HW_NOT_SUPPORTED, false)    then result := LNG_STATUS_NOTAVAIL_HW_NOT_SUPPORTED
175
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_NO_ENTITIES, false)         then result := LNG_STATUS_NOTAVAIL_NO_ENTITIES
176
  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)])
83 daniel-mar 177
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC, false) then result := LNG_STATUS_NOTAVAIL_ONLY_ACCEPT_DYNAMIC
90 daniel-mar 178
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_NOTAVAIL_INVALID_INPUT, false)       then result := LNG_STATUS_NOTAVAIL_INVALID_INPUT
69 daniel-mar 179
 
82 daniel-mar 180
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_UNSPECIFIED, false)          then result := LNG_STATUS_FAILURE_UNSPECIFIED
181
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_BUFFER_TOO_SMALL, false)     then result := LNG_STATUS_FAILURE_BUFFER_TOO_SMALL
182
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_INVALID_ARGS, false)         then result := LNG_STATUS_FAILURE_INVALID_ARGS
183
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_PLUGIN_NOT_LICENSED, false)  then result := LNG_STATUS_FAILURE_PLUGIN_NOT_LICENSED
184
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_NO_RETURNED_VALUE, false)    then result := LNG_STATUS_FAILURE_NO_RETURNED_VALUE
185
  else if UD2_STATUS_Equal(grStatus, UD2_STATUS_FAILURE_CATCHED_EXCEPTION, false)    then result := LNG_STATUS_FAILURE_CATCHED_EXCEPTION
69 daniel-mar 186
 
71 daniel-mar 187
  else if grStatus.wCategory = UD2_STATUSCAT_SUCCESS   then result := Format(LNG_UNKNOWN_SUCCESS,  [UD2_STATUS_FormatStatusCode(grStatus)])
188
  else if grStatus.wCategory = UD2_STATUSCAT_NOT_AVAIL then result := Format(LNG_UNKNOWN_NOTAVAIL, [UD2_STATUS_FormatStatusCode(grStatus)])
189
  else if grStatus.wCategory = UD2_STATUSCAT_FAILED    then result := Format(LNG_UNKNOWN_FAILED,   [UD2_STATUS_FormatStatusCode(grStatus)])
190
  else                                                      result := Format(LNG_UNKNOWN_STATUS,   [UD2_STATUS_FormatStatusCode(grStatus)]);
68 daniel-mar 191
end;
192
 
193
{ TUD2Plugin }
194
 
195
function TUD2Plugin.PluginGUIDString: string;
196
begin
87 daniel-mar 197
  if PluginGUIDSet then
198
    result := UpperCase(GUIDToString(PluginGUID))
199
  else
200
    result := '';
68 daniel-mar 201
end;
202
 
83 daniel-mar 203
function TUD2Plugin.AddIdentification(IdStr: WideString): TUD2IdentificationEntry;
68 daniel-mar 204
begin
83 daniel-mar 205
  result := TUD2IdentificationEntry.Create(IdStr, Self);
206
  DetectedIdentifications.Add(result);
68 daniel-mar 207
end;
208
 
209
destructor TUD2Plugin.Destroy;
210
begin
211
  DetectedIdentifications.Free;
212
  inherited;
213
end;
214
 
215
constructor TUD2Plugin.Create;
216
begin
217
  inherited Create;
218
  FDetectedIdentifications := TObjectList{<TUD2IdentificationEntry>}.Create(true);
219
end;
220
 
90 daniel-mar 221
function TUD2Plugin.InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings; var outIDs: TArrayOfString): boolean;
83 daniel-mar 222
var
223
  ude: TUD2IdentificationEntry;
224
  i: integer;
225
  id: string;
86 daniel-mar 226
  l: integer;
83 daniel-mar 227
begin
228
  result := false;
68 daniel-mar 229
 
86 daniel-mar 230
  SetLength(outIDs, 0);
231
 
83 daniel-mar 232
  for i := 0 to FDetectedIdentifications.Count-1 do
233
  begin
234
    ude := FDetectedIdentifications.Items[i] as TUD2IdentificationEntry;
235
    if ude.dynamicDataUsed and (ude.dynamicData = dynamicData) then
236
    begin
86 daniel-mar 237
      l := Length(outIDs);
238
      SetLength(outIDs, l+1);
239
      outIDs[l] := ude.FIdentificationString;
83 daniel-mar 240
    end;
241
  end;
242
 
86 daniel-mar 243
  // The dynamic content was already evaluated (and therefore is already added in FDetectedIdentifications).
244
  if Length(outIDs) > 0 then exit;
83 daniel-mar 245
 
90 daniel-mar 246
  outIDs := GetDynamicRequestResult(dynamicData, AErrorOut);
86 daniel-mar 247
 
248
  for i := 0 to Length(outIDs)-1 do
83 daniel-mar 249
  begin
86 daniel-mar 250
    id := outIDs[i];
83 daniel-mar 251
 
252
    ude := AddIdentification(id);
253
    ude.dynamicDataUsed := true;
254
    ude.dynamicData := dynamicData;
255
 
256
    result := true;
257
  end;
258
end;
259
 
90 daniel-mar 260
function TUD2Plugin.GetDynamicRequestResult(dynamicData: WideString; AErrorOut: TStrings=nil): TArrayOfString;
83 daniel-mar 261
var
262
  lngID: LANGID;
90 daniel-mar 263
  loader: TUD2PluginLoader;
68 daniel-mar 264
begin
83 daniel-mar 265
  lngID := GetSystemDefaultLangID;
266
 
90 daniel-mar 267
  loader := TUD2PluginLoader.Create(false, PluginDLL, lngid, true, dynamicData);
83 daniel-mar 268
  try
90 daniel-mar 269
    loader.WaitFor;
270
    result := loader.ResultIdentifiers;
271
    if Assigned(AErrorOut) then
272
    begin
273
      AErrorOut.AddStrings(loader.Errors);
274
    end;
275
 
276
    // TODO: Use assign() instead? or allow TUD2PluginLoader to write the TPlugin object directly?
277
    //       Should we even overwrite the current plugin data, or return the new plugin?
278
    FIdentificationProcedureStatusCode := loader.plugin.IdentificationProcedureStatusCode;
279
    FIdentificationProcedureStatusCodeDescribed := loader.plugin.IdentificationProcedureStatusCodeDescribed;
280
    FOSNotSupportedEnforced := loader.plugin.OSNotSupportedEnforced;
281
    FLoadingTime := loader.Plugin.LoadingTime;
282
 
83 daniel-mar 283
  finally
90 daniel-mar 284
    if Assigned(loader.Plugin) then FreeAndNil(loader.Plugin);
285
    loader.Free;
83 daniel-mar 286
  end;
68 daniel-mar 287
end;
288
 
83 daniel-mar 289
function TUD2Plugin.EqualsMethodNameOrGuid(idMethodNameOrGUID: string): boolean;
290
begin
291
  result := SameText(IdentificationMethodName, idMethodNameOrGUID) or
292
            SameText(GUIDToString(PluginGUID), idMethodNameOrGUID)
293
end;
294
 
90 daniel-mar 295
function TUD2Plugin.InvokeDynamicCheck(dynamicData: WideString; AErrorOut: TStrings): boolean;
86 daniel-mar 296
var
297
  dummy: TArrayOfString;
298
begin
90 daniel-mar 299
  result := InvokeDynamicCheck(dynamicData, AErrorOut, dummy)
86 daniel-mar 300
end;
301
 
83 daniel-mar 302
{ TUD2IdentificationEntry }
303
 
68 daniel-mar 304
procedure TUD2IdentificationEntry.GetIdNames(sl: TStrings);
85 daniel-mar 305
var
306
  cond: TUD2TDFCondition;
68 daniel-mar 307
begin
85 daniel-mar 308
  cond.idMethodName := Plugin.IdentificationMethodName;
309
  cond.idStr := IdentificationString;
310
  cond.dynamicDataUsed := DynamicDataUsed;
311
  cond.dynamicData := DynamicData;
312
  sl.Add(UD2_CondToStr(cond));
313
 
314
  cond.idMethodName := Plugin.PluginGUIDString;
315
  sl.Add(UD2_CondToStr(cond));
68 daniel-mar 316
end;
317
 
318
constructor TUD2IdentificationEntry.Create(AIdentificationString: WideString;
319
  APlugin: TUD2Plugin);
320
begin
321
  inherited Create;
90 daniel-mar 322
 
323
  // TODO: We need to do this, because ReadSectionValues strips the names of the name-value pairs...
324
  //       We should correct ReadSectionValues...
325
  // Example: DriveSerial(c:):2SHSWNHA010807 X    =calc.exe
326
  // ReadSectionValues will return "DriveSerial(c:):2SHSWNHA010807 X=calc.exe"
327
  AIdentificationString := Trim(AIdentificationString);
328
 
68 daniel-mar 329
  FIdentificationString := AIdentificationString;
330
  FPlugin := APlugin;
331
end;
332
 
333
{ TUD2 }
334
 
80 daniel-mar 335
procedure TUD2.HandlePluginDir(APluginDir, AFileMask: string);
69 daniel-mar 336
Var
337
  SR: TSearchRec;
338
  path: string;
70 daniel-mar 339
  pluginLoader: TUD2PluginLoader;
87 daniel-mar 340
  tob: TObjectList{<TUD2PluginLoader>};
68 daniel-mar 341
  i: integer;
70 daniel-mar 342
  {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
343
  sPluginID, prevDLL: string;
344
  {$ENDIF}
69 daniel-mar 345
  lngid: LANGID;
68 daniel-mar 346
resourcestring
347
  LNG_PLUGINS_SAME_GUID = 'Attention: The plugin "%s" and the plugin "%s" have the same identification GUID. The latter will not be loaded.';
348
begin
87 daniel-mar 349
  tob := TObjectList{<TUD2PluginLoader>}.Create;
68 daniel-mar 350
  try
69 daniel-mar 351
    tob.OwnsObjects := false;
68 daniel-mar 352
 
69 daniel-mar 353
    lngID := GetSystemDefaultLangID;
68 daniel-mar 354
 
80 daniel-mar 355
    path := APluginDir;
356
    if path <> '' then path := IncludeTrailingPathDelimiter(path);
357
 
358
    if FindFirst(path + AFileMask, 0, SR) = 0 then
68 daniel-mar 359
    begin
360
      try
69 daniel-mar 361
        repeat
362
          try
83 daniel-mar 363
            tob.Add(TUD2PluginLoader.Create(false, path + sr.Name, lngid, false, ''));
69 daniel-mar 364
          except
365
            on E: Exception do
366
            begin
367
              MessageDlg(E.Message, mtError, [mbOK], 0);
368
            end;
369
          end;
370
        until FindNext(SR) <> 0;
68 daniel-mar 371
      finally
69 daniel-mar 372
        FindClose(SR);
68 daniel-mar 373
      end;
374
    end;
375
 
69 daniel-mar 376
    for i := 0 to tob.count-1 do
68 daniel-mar 377
    begin
70 daniel-mar 378
      pluginLoader := tob.items[i] as TUD2PluginLoader;
379
      pluginLoader.WaitFor;
380
      Errors.AddStrings(pluginLoader.Errors);
90 daniel-mar 381
      if Assigned(pluginLoader.Plugin) then
68 daniel-mar 382
      begin
87 daniel-mar 383
        {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
90 daniel-mar 384
        if pluginLoader.Plugin.PluginGUIDSet then
69 daniel-mar 385
        begin
90 daniel-mar 386
          sPluginID := GUIDToString(pluginLoader.Plugin.PluginGUID);
82 daniel-mar 387
          prevDLL := FGUIDLookup.Values[sPluginID];
90 daniel-mar 388
          if (prevDLL <> '') and (prevDLL <> pluginLoader.Plugin.PluginDLL) then
82 daniel-mar 389
          begin
90 daniel-mar 390
            Errors.Add(Format(LNG_PLUGINS_SAME_GUID, [prevDLL, pluginLoader.Plugin.PluginDLL]));
391
            pluginLoader.Plugin.Free;
82 daniel-mar 392
          end
393
          else
394
          begin
90 daniel-mar 395
            FGUIDLookup.Values[sPluginID] := pluginLoader.Plugin.PluginDLL;
396
            LoadedPlugins.Add(pluginLoader.Plugin);
82 daniel-mar 397
          end;
87 daniel-mar 398
        end
399
        else
400
        begin
90 daniel-mar 401
          LoadedPlugins.Add(pluginLoader.Plugin);
69 daniel-mar 402
        end;
87 daniel-mar 403
        {$ELSE}
90 daniel-mar 404
        LoadedPlugins.Add(pluginLoader.Plugin);
87 daniel-mar 405
        {$ENDIF}
68 daniel-mar 406
      end;
70 daniel-mar 407
      pluginLoader.Free;
68 daniel-mar 408
    end;
409
  finally
69 daniel-mar 410
    tob.free;
68 daniel-mar 411
  end;
412
end;
413
 
414
destructor TUD2.Destroy;
415
begin
416
  FIniFile.Free;
417
  FLoadedPlugins.Free;
70 daniel-mar 418
  {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
68 daniel-mar 419
  FGUIDLookup.Free;
70 daniel-mar 420
  {$ENDIF}
68 daniel-mar 421
  FErrors.Free;
422
end;
423
 
424
constructor TUD2.Create(AIniFileName: string);
425
begin
426
  FIniFileName := AIniFileName;
427
  FLoadedPlugins := TObjectList{<TUD2Plugin>}.Create(true);
428
  FIniFile := TMemIniFile.Create(IniFileName);
70 daniel-mar 429
  {$IFDEF CHECK_FOR_SAME_PLUGIN_GUID}
68 daniel-mar 430
  FGUIDLookup := TStringList.Create;
70 daniel-mar 431
  {$ENDIF}
68 daniel-mar 432
  FErrors := TStringList.Create;
433
end;
434
 
435
function TUD2.GetTaskName(AShortTaskName: string): string;
69 daniel-mar 436
resourcestring
437
  LNG_NO_DESCRIPTION = '(%s)';
68 daniel-mar 438
begin
90 daniel-mar 439
  result := FIniFile.ReadString(AShortTaskName, UD2_TagDescription, Format(LNG_NO_DESCRIPTION, [AShortTaskName]));
68 daniel-mar 440
end;
441
 
442
procedure TUD2.GetTaskListing(outSL: TStrings);
443
var
444
  sl: TStringList;
445
  i: integer;
446
  desc: string;
447
begin
448
  sl := TStringList.Create;
449
  try
450
    FIniFile.ReadSections(sl);
451
    for i := 0 to sl.Count-1 do
452
    begin
453
      desc := GetTaskName(sl.Strings[i]);
454
      outSL.Values[sl.Strings[i]] := desc;
455
    end;
456
  finally
457
    sl.Free;
458
  end;
459
end;
460
 
461
function TUD2.TaskExists(ShortTaskName: string): boolean;
462
begin
463
  result := FIniFile.SectionExists(ShortTaskName);
464
end;
465
 
87 daniel-mar 466
function TUD2.ReadMetatagString(ShortTaskName, MetatagName: string; DefaultVal: string): string;
68 daniel-mar 467
begin
468
  result := IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal);
469
end;
470
 
87 daniel-mar 471
function TUD2.ReadMetatagBool(ShortTaskName, MetatagName: string; DefaultVal: string): boolean;
68 daniel-mar 472
begin
473
  // DefaultVal is a string, because we want to allow an empty string, in case the
474
  // user wishes an Exception in case the string is not a valid boolean string
475
  result := BetterInterpreteBool(IniFile.ReadString(ShortTaskName, MetatagName, DefaultVal));
476
end;
477
 
478
(*
479
 
84 daniel-mar 480
NAMING EXAMPLE: $CASESENSITIVE$ComputerName(dynXYZ):ABC&&User:John=calc.exe$RIOD$
68 daniel-mar 481
 
84 daniel-mar 482
        idTerm:       ComputerName(dynXYZ):ABC&&User:John
68 daniel-mar 483
        idName:       ComputerName:ABC
484
        IdMethodName: ComputerName
485
        IdStr         ABC
486
        cmd:          calc.exe
83 daniel-mar 487
        dynamicData:  dynXYZ
68 daniel-mar 488
 
489
*)
490
 
84 daniel-mar 491
procedure TUD2.GetAllDetectedIDs(outSL: TStrings);
68 daniel-mar 492
var
493
  i, j: integer;
494
  pl: TUD2Plugin;
495
  ude: TUD2IdentificationEntry;
496
begin
81 daniel-mar 497
  for i := 0 to LoadedPlugins.Count-1 do
498
  begin
499
    pl := LoadedPlugins.Items[i] as TUD2Plugin;
500
    for j := 0 to pl.DetectedIdentifications.Count-1 do
501
    begin
502
      ude := pl.DetectedIdentifications.Items[j] as TUD2IdentificationEntry;
503
      ude.GetIdNames(outSL);
504
    end;
505
  end;
506
end;
68 daniel-mar 507
 
90 daniel-mar 508
function TUD2.FulfilsEverySubterm(conds: TUD2TDFConditionArray; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean;
85 daniel-mar 509
begin
90 daniel-mar 510
  result := FulfilsEverySubterm(UD2_CondsToStr(conds), slIdNames, AErrorOut);
85 daniel-mar 511
end;
512
 
90 daniel-mar 513
function TUD2.FulfilsEverySubterm(idTerm: WideString; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): boolean;
81 daniel-mar 514
var
515
  i: integer;
83 daniel-mar 516
  p: TUD2Plugin;
81 daniel-mar 517
  cleanUpStringList: boolean;
85 daniel-mar 518
  conds: TUD2TDFConditionArray;
519
  cond: TUD2TDFCondition;
520
  idName: string;
81 daniel-mar 521
begin
90 daniel-mar 522
  {$IFDEF NO_CONDITIONS_IS_FAILURE}
523
  if idTerm = '' then
524
  begin
525
    SetLength(conds, 0);
526
    result := false;
527
    Exit;
528
  end;
529
  {$ENDIF}
530
 
81 daniel-mar 531
  cleanUpStringList := slIdNames = nil;
68 daniel-mar 532
  try
81 daniel-mar 533
    if cleanUpStringList then
68 daniel-mar 534
    begin
81 daniel-mar 535
      slIdNames := TStringList.Create;
84 daniel-mar 536
      GetAllDetectedIDs(slIdNames);
81 daniel-mar 537
    end;
538
 
85 daniel-mar 539
    conds := UD2P_ParseConditions(idTerm);
540
 
81 daniel-mar 541
    result := true;
85 daniel-mar 542
    for i := Low(conds) to High(conds) do
81 daniel-mar 543
    begin
85 daniel-mar 544
      cond := conds[i];
81 daniel-mar 545
 
85 daniel-mar 546
      if cond.dynamicDataUsed then
83 daniel-mar 547
      begin
85 daniel-mar 548
        p := FindPluginByMethodNameOrGuid(cond.idMethodName);
549
        if Assigned(p) then
84 daniel-mar 550
        begin
90 daniel-mar 551
          if p.InvokeDynamicCheck(cond.dynamicData, AErrorOut) then
83 daniel-mar 552
          begin
85 daniel-mar 553
            // Reload the identifications
554
            slIdNames.Clear;
555
            GetAllDetectedIDs(slIdNames);
83 daniel-mar 556
          end;
557
        end;
558
      end;
559
 
85 daniel-mar 560
      idName := UD2_CondToStr(cond);
83 daniel-mar 561
 
85 daniel-mar 562
      if (not cond.caseSensitive and (slIdNames.IndexOf(idName) = -1)) or
563
         (cond.caseSensitive and (IndexOf_CS(slIdNames, idName) = -1)) then
68 daniel-mar 564
      begin
81 daniel-mar 565
        result := false;
566
        break;
68 daniel-mar 567
      end;
568
    end;
81 daniel-mar 569
  finally
570
    if cleanUpStringList and Assigned(slIdNames) then
571
      slIdNames.Free;
572
  end;
573
end;
68 daniel-mar 574
 
83 daniel-mar 575
function TUD2.FindPluginByMethodNameOrGuid(idMethodName: string): TUD2Plugin;
576
var
577
  i: integer;
578
  p: TUD2Plugin;
579
begin
580
  result := nil;
581
  for i := 0 to LoadedPlugins.Count-1 do
582
  begin
583
    p := LoadedPlugins.Items[i] as TUD2Plugin;
584
 
585
    if p.EqualsMethodNameOrGuid(idMethodName) then
586
    begin
587
      result := p;
588
      Exit;
589
    end;
590
  end;
591
end;
592
 
90 daniel-mar 593
function TUD2.GetCommandList(ShortTaskName: string; AErrorOut: TStrings=nil): TUD2CommandArray;
81 daniel-mar 594
var
85 daniel-mar 595
  i, j, l: integer;
81 daniel-mar 596
  slSV, slIdNames: TStrings;
85 daniel-mar 597
  tmpCmds: TUD2CommandArray;
81 daniel-mar 598
begin
85 daniel-mar 599
  SetLength(result, 0);
600
  SetLength(tmpCmds, 0);
601
 
81 daniel-mar 602
  slIdNames := TStringList.Create;
603
  try
84 daniel-mar 604
    GetAllDetectedIDs(slIdNames);
81 daniel-mar 605
 
68 daniel-mar 606
    slSV := TStringList.Create;
607
    try
608
      FIniFile.ReadSectionValues(ShortTaskName, slSV);
81 daniel-mar 609
      for i := 0 to slSV.Count-1 do
68 daniel-mar 610
      begin
90 daniel-mar 611
        tmpCmds := CheckTerm(slSV.Strings[i], slIdNames, AErrorOut);
85 daniel-mar 612
        for j := Low(tmpCmds) to High(tmpCmds) do
613
        begin
614
          l := Length(result);
615
          SetLength(result, l+1);
616
          result[l] := tmpCmds[j];
617
        end;
68 daniel-mar 618
      end;
619
    finally
620
      slSV.Free;
621
    end;
622
  finally
623
    slIdNames.Free;
624
  end;
625
end;
626
 
90 daniel-mar 627
function TUD2.CheckTerm(idTermAndCmd: string; slIdNames: TStrings=nil; AErrorOut: TStrings=nil): TUD2CommandArray;
83 daniel-mar 628
var
629
  slIdNamesCreated: boolean;
85 daniel-mar 630
  ent: TUD2TDFEntry;
83 daniel-mar 631
begin
85 daniel-mar 632
  SetLength(result, 0);
633
 
83 daniel-mar 634
  slIdNamesCreated := false;
635
  try
636
    if not Assigned(slIdNames) then
637
    begin
638
      slIdNamesCreated := true;
639
      slIdNames := TStringList.Create;
84 daniel-mar 640
      GetAllDetectedIDs(slIdNames);
83 daniel-mar 641
    end;
642
 
85 daniel-mar 643
    if not UD2P_ParseTdfLine(idTermAndCmd, ent) then Exit;
90 daniel-mar 644
    if FulfilsEverySubterm(ent.ids, slIdNames, AErrorOut) then
85 daniel-mar 645
    begin
646
      result := ent.commands;
647
    end;
83 daniel-mar 648
  finally
649
    if slIdNamesCreated then slIdNames.Free;
650
  end;
651
end;
652
 
69 daniel-mar 653
{ TUD2PluginLoader }
654
 
655
procedure TUD2PluginLoader.Execute;
656
begin
657
  inherited;
658
 
659
  HandleDLL;
660
end;
661
 
83 daniel-mar 662
constructor TUD2PluginLoader.Create(Suspended: boolean; DLL: string; alngid: LANGID; useDynamicData: boolean; dynamicData: WideString);
69 daniel-mar 663
begin
664
  inherited Create(Suspended);
665
  dllfile := dll;
90 daniel-mar 666
  Plugin := nil;
69 daniel-mar 667
  Errors := TStringList.Create;
668
  lngid := alngid;
83 daniel-mar 669
  self.useDynamicData := useDynamicData;
670
  Self.dynamicData := dynamicData;
69 daniel-mar 671
end;
672
 
673
destructor TUD2PluginLoader.Destroy;
674
begin
675
  Errors.Free;
676
  inherited;
677
end;
678
 
70 daniel-mar 679
function TUD2PluginLoader.HandleDLL: boolean;
69 daniel-mar 680
var
681
  sIdentifier: WideString;
682
  buf: array[0..cchBufferSize-1] of WideChar;
683
  pluginInterfaceID: TGUID;
70 daniel-mar 684
  dllHandle: Cardinal;
69 daniel-mar 685
  fPluginInterfaceID: TFuncPluginInterfaceID;
686
  fPluginIdentifier: TFuncPluginIdentifier;
687
  fPluginNameW: TFuncPluginNameW;
688
  fPluginVendorW: TFuncPluginVendorW;
689
  fPluginVersionW: TFuncPluginVersionW;
690
  fIdentificationMethodNameW: TFuncIdentificationMethodNameW;
691
  fIdentificationStringW: TFuncIdentificationStringW;
83 daniel-mar 692
  fDynamicIdentificationStringW: TFuncDynamicIdentificationStringW;
69 daniel-mar 693
  fCheckLicense: TFuncCheckLicense;
70 daniel-mar 694
  fDescribeOwnStatusCodeW: TFuncDescribeOwnStatusCodeW;
69 daniel-mar 695
  statusCode: UD2_STATUS;
696
  i: integer;
697
  starttime, endtime, time: cardinal;
82 daniel-mar 698
  bakErrorMode: DWORD;
699
  err: DWORD;
70 daniel-mar 700
 
701
  function _ErrorLookup(statusCode: UD2_STATUS): WideString;
702
  var
703
    ret: BOOL;
83 daniel-mar 704
    buf: array[0..cchBufferSize-1] of WideChar;
70 daniel-mar 705
  begin
82 daniel-mar 706
    if Assigned(fDescribeOwnStatusCodeW) then
70 daniel-mar 707
    begin
82 daniel-mar 708
      ZeroMemory(@buf, cchBufferSize);
709
      ret := fDescribeOwnStatusCodeW(@buf, cchBufferSize, statusCode, lngID);
710
      if ret then
711
      begin
712
        result := PWideChar(@buf);
713
        Exit;
714
      end;
70 daniel-mar 715
    end;
716
    result := TUD2.GenericErrorLookup(statusCode);
717
  end;
718
 
82 daniel-mar 719
  function _ApplyCompatibilityGUID: boolean;
720
  var
721
    iniConfig: TIniFile;
722
    sOverrideGUID: string;
723
    sPluginConfigFile: string;
69 daniel-mar 724
  begin
82 daniel-mar 725
    result := false;
69 daniel-mar 726
    sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
727
    if FileExists(sPluginConfigFile) then
728
    begin
729
      iniConfig := TIniFile.Create(sPluginConfigFile);
730
      try
731
        sOverrideGUID := iniConfig.ReadString('Compatibility', 'OverrideGUID', '');
732
        if sOverrideGUID <> '' then
733
        begin
90 daniel-mar 734
          Plugin.FPluginGUIDSet := true;
735
          Plugin.FPluginGUID := StringToGUID(sOverrideGUID);
82 daniel-mar 736
          result := true;
69 daniel-mar 737
        end;
738
      finally
739
        iniConfig.Free;
740
      end;
741
    end;
82 daniel-mar 742
  end;
69 daniel-mar 743
 
82 daniel-mar 744
  function _AutoOSNotSupportedMode: integer;
745
  var
746
    iniConfig: TIniFile;
747
    sPluginConfigFile: string;
748
  begin
749
    result := 0;
750
    sPluginConfigFile := ChangeFileExt(dllFile, '.ini');
751
    if FileExists(sPluginConfigFile) then
69 daniel-mar 752
    begin
82 daniel-mar 753
      iniConfig := TIniFile.Create(sPluginConfigFile);
754
      try
755
        result := iniConfig.ReadInteger('Compatibility', 'AutoOSNotSupported', 0);
756
      finally
757
        iniConfig.Free;
69 daniel-mar 758
      end;
759
    end;
82 daniel-mar 760
  end;
69 daniel-mar 761
 
82 daniel-mar 762
  procedure _OverwriteStatusToOSNotSupported;
763
  begin
90 daniel-mar 764
    Plugin := TUD2Plugin.Create;
765
    Plugin.FPluginDLL := dllFile;
82 daniel-mar 766
    statusCode := UD2_STATUS_NOTAVAIL_OS_NOT_SUPPORTED;
90 daniel-mar 767
    Plugin.FIdentificationProcedureStatusCode := statusCode;
768
    Plugin.FIdentificationProcedureStatusCodeDescribed := _ErrorLookup(statusCode);
769
    Plugin.FOSNotSupportedEnforced := true;
82 daniel-mar 770
    result := true;
771
  end;
69 daniel-mar 772
 
82 daniel-mar 773
resourcestring
774
  LNG_DLL_NOT_LOADED = 'Plugin DLL "%s" could not be loaded: %s';
775
  LNG_METHOD_NOT_FOUND = 'Method "%s" not found in plugin "%s". The DLL is probably not a valid plugin DLL.';
776
  LNG_INVALID_PLUGIN = 'The plugin "%s" is not a valid plugin for this application.';
777
  LNG_METHOD_FAILURE = 'Error "%s" at method "%s" of plugin "%s".';
778
  LNG_EXCEPTION = 'Fatal error while loading "%s" (%s: %s)';
779
begin
780
  result := false;
781
  startTime := GetTickCount;
69 daniel-mar 782
 
82 daniel-mar 783
  try
784
    bakErrorMode := 0;
785
    UD2_SetThreadErrorMode(SEM_FAILCRITICALERRORS, Pointer(bakErrorMode));
786
    try
787
      dllHandle := LoadLibrary(PChar(dllFile));
788
      if dllHandle = 0 then
789
      begin
790
        err := GetLastError;
69 daniel-mar 791
 
82 daniel-mar 792
        if ((_AutoOSNotSupportedMode = 1) and ((err = ERROR_DLL_NOT_FOUND) or (err = ERROR_PROC_NOT_FOUND))) or
793
           (_AutoOSNotSupportedMode >= 2) then
794
        begin
795
          _OverwriteStatusToOSNotSupported;
796
          Exit;
797
        end;
69 daniel-mar 798
 
82 daniel-mar 799
        Errors.Add(Format(LNG_DLL_NOT_LOADED, [dllFile, SysErrorMessage(err)]));
800
        Exit;
801
      end;
802
      try
803
        @fPluginInterfaceID := GetProcAddress(dllHandle, mnPluginInterfaceID);
804
        if not Assigned(fPluginInterfaceID) then
805
        begin
806
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginInterfaceID, dllFile]));
807
          Exit;
808
        end;
809
        pluginInterfaceID := fPluginInterfaceID();
810
        if not IsEqualGUID(pluginInterfaceID, GUID_USERDETECT2_IDPLUGIN_V1) then
811
        begin
812
          Errors.Add(Format(LNG_INVALID_PLUGIN, [dllFile]));
813
          Exit;
814
        end;
69 daniel-mar 815
 
90 daniel-mar 816
        Plugin := TUD2Plugin.Create;
817
        Plugin.FPluginDLL := dllFile;
86 daniel-mar 818
 
819
        @fDynamicIdentificationStringW := GetProcAddress(dllHandle, mnDynamicIdentificationStringW);
90 daniel-mar 820
        Plugin.FAcceptsDynamicRequests := Assigned(fDynamicIdentificationStringW);
86 daniel-mar 821
 
83 daniel-mar 822
        fIdentificationStringW := nil;
823
        if useDynamicData then
69 daniel-mar 824
        begin
90 daniel-mar 825
          if not Plugin.AcceptsDynamicRequests then
83 daniel-mar 826
          begin
827
            // TODO xxx: Darf hier ein fataler Fehler entstehen, obwohl dieses Szenario nur durch die INI file auftreten kann?
828
            // TODO (allgemein): doku
829
            Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnDynamicIdentificationStringW, dllFile]));
830
            Exit;
831
          end;
832
        end
833
        else
834
        begin
835
          @fIdentificationStringW := GetProcAddress(dllHandle, mnIdentificationStringW);
836
          if not Assigned(fIdentificationStringW) then
837
          begin
838
            Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationStringW, dllFile]));
839
            Exit;
840
          end;
69 daniel-mar 841
        end;
82 daniel-mar 842
 
843
        @fPluginNameW := GetProcAddress(dllHandle, mnPluginNameW);
844
        if not Assigned(fPluginNameW) then
845
        begin
846
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginNameW, dllFile]));
847
          Exit;
848
        end;
849
 
850
        @fPluginVendorW := GetProcAddress(dllHandle, mnPluginVendorW);
851
        if not Assigned(fPluginVendorW) then
852
        begin
853
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginVendorW, dllFile]));
854
          Exit;
855
        end;
856
 
857
        @fPluginVersionW := GetProcAddress(dllHandle, mnPluginVersionW);
858
        if not Assigned(fPluginVersionW) then
859
        begin
860
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginVersionW, dllFile]));
861
          Exit;
862
        end;
863
 
864
        @fCheckLicense := GetProcAddress(dllHandle, mnCheckLicense);
865
        if not Assigned(fCheckLicense) then
866
        begin
867
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnCheckLicense, dllFile]));
868
          Exit;
869
        end;
870
 
871
        @fIdentificationMethodNameW := GetProcAddress(dllHandle, mnIdentificationMethodNameW);
872
        if not Assigned(fIdentificationMethodNameW) then
873
        begin
874
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnIdentificationMethodNameW, dllFile]));
875
          Exit;
876
        end;
877
 
878
        @fDescribeOwnStatusCodeW := GetProcAddress(dllHandle, mnDescribeOwnStatusCodeW);
879
        if not Assigned(fDescribeOwnStatusCodeW) then
880
        begin
881
          Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnDescribeOwnStatusCodeW, dllFile]));
882
          Exit;
883
        end;
884
 
885
        if not _ApplyCompatibilityGUID then
886
        begin
887
          @fPluginIdentifier := GetProcAddress(dllHandle, mnPluginIdentifier);
888
          if not Assigned(fPluginIdentifier) then
889
          begin
890
            Errors.Add(Format(LNG_METHOD_NOT_FOUND, [mnPluginIdentifier, dllFile]));
891
            Exit;
892
          end;
90 daniel-mar 893
          Plugin.FPluginGUIDSet := true;
894
          Plugin.FPluginGUID := fPluginIdentifier();
82 daniel-mar 895
        end;
896
 
897
        statusCode := fCheckLicense(nil);
898
        if statusCode.wCategory = UD2_STATUSCAT_FAILED then
899
        begin
900
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnCheckLicense, dllFile]));
901
          Exit;
902
        end;
903
 
904
        ZeroMemory(@buf, cchBufferSize);
905
        statusCode := fPluginNameW(@buf, cchBufferSize, lngID);
90 daniel-mar 906
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FPluginName := PWideChar(@buf)
907
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FPluginName := ''
82 daniel-mar 908
        else
909
        begin
910
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginNameW, dllFile]));
911
          Exit;
912
        end;
913
 
914
        ZeroMemory(@buf, cchBufferSize);
915
        statusCode := fPluginVendorW(@buf, cchBufferSize, lngID);
90 daniel-mar 916
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FPluginVendor := PWideChar(@buf)
917
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FPluginVendor := ''
82 daniel-mar 918
        else
919
        begin
920
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginVendorW, dllFile]));
921
          Exit;
922
        end;
923
 
924
        ZeroMemory(@buf, cchBufferSize);
925
        statusCode := fPluginVersionW(@buf, cchBufferSize, lngID);
90 daniel-mar 926
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FPluginVersion := PWideChar(@buf)
927
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FPluginVersion := ''
82 daniel-mar 928
        else
929
        begin
930
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnPluginVersionW, dllFile]));
931
          Exit;
932
        end;
933
 
934
        ZeroMemory(@buf, cchBufferSize);
935
        statusCode := fIdentificationMethodNameW(@buf, cchBufferSize);
90 daniel-mar 936
             if statusCode.wCategory = UD2_STATUSCAT_SUCCESS   then Plugin.FIdentificationMethodName := PWideChar(@buf)
937
        else if statusCode.wCategory = UD2_STATUSCAT_NOT_AVAIL then Plugin.FIdentificationMethodName := ''
82 daniel-mar 938
        else
939
        begin
940
          Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnIdentificationMethodNameW, dllFile]));
941
          Exit;
942
        end;
943
 
944
        ZeroMemory(@buf, cchBufferSize);
945
        statusCode := UD2_STATUS_FAILURE_NO_RETURNED_VALUE; // This status will be used when the DLL does not return anything (which is an error by the developer)
83 daniel-mar 946
        if useDynamicData then
947
        begin
948
          statusCode := fDynamicIdentificationStringW(@buf, cchBufferSize, PWideChar(dynamicData));
949
        end
950
        else
951
        begin
952
          statusCode := fIdentificationStringW(@buf, cchBufferSize);
953
        end;
90 daniel-mar 954
        Plugin.FIdentificationProcedureStatusCode := statusCode;
955
        Plugin.FIdentificationProcedureStatusCodeDescribed := _ErrorLookup(statusCode);
82 daniel-mar 956
        if statusCode.wCategory = UD2_STATUSCAT_SUCCESS then
957
        begin
958
          sIdentifier := PWideChar(@buf);
959
          if UD2_STATUS_Equal(statusCode, UD2_STATUS_OK_MULTILINE, false) then
960
          begin
961
            // Multiple identifiers (e.g. multiple MAC addresses are delimited via UD2_MULTIPLE_ITEMS_DELIMITER)
83 daniel-mar 962
            SetLength(ResultIdentifiers, 0);
963
            ResultIdentifiers := SplitString(UD2_MULTIPLE_ITEMS_DELIMITER, sIdentifier);
964
            for i := Low(ResultIdentifiers) to High(ResultIdentifiers) do
82 daniel-mar 965
            begin
90 daniel-mar 966
              Plugin.AddIdentification(ResultIdentifiers[i]);
82 daniel-mar 967
            end;
968
          end
969
          else
970
          begin
90 daniel-mar 971
            Plugin.AddIdentification(sIdentifier);
83 daniel-mar 972
 
973
            SetLength(ResultIdentifiers, 1);
974
            ResultIdentifiers[0] := sIdentifier;
82 daniel-mar 975
          end;
976
        end
977
        else if statusCode.wCategory <> UD2_STATUSCAT_NOT_AVAIL then
978
        begin
979
          if _AutoOSNotSupportedMode >= 3 then
980
          begin
981
            _OverwriteStatusToOSNotSupported;
982
            Exit;
983
          end;
984
 
985
          // Errors.Add(Format(LNG_METHOD_FAILURE, [_ErrorLookup(statusCode), mnIdentificationStringW, dllFile]));
90 daniel-mar 986
          Errors.Add(Format(LNG_METHOD_FAILURE, [Plugin.IdentificationProcedureStatusCodeDescribed, mnIdentificationStringW, dllFile]));
82 daniel-mar 987
          Exit;
988
        end;
989
 
990
        result := true;
991
      finally
90 daniel-mar 992
        if not result and Assigned(Plugin) then FreeAndNil(Plugin);
82 daniel-mar 993
        FreeLibrary(dllHandle);
994
      end;
995
    finally
996
      UD2_SetThreadErrorMode(bakErrorMode, nil);
997
 
998
      if result then
69 daniel-mar 999
      begin
82 daniel-mar 1000
        endtime := GetTickCount;
1001
        time := endtime - starttime;
1002
        if endtime < starttime then time := High(Cardinal) - time;
90 daniel-mar 1003
        Plugin.FLoadingTime := time;
69 daniel-mar 1004
      end;
82 daniel-mar 1005
    end;
1006
  except
1007
    // TODO: when an exception happens in a cdecl DLL, then this code is somehow not
1008
    // executed. Probably the memory is corrupted. Anyway, a cdecl DLL shall NEVER
1009
    // raise an Exception.
1010
    on E: Exception do
69 daniel-mar 1011
    begin
82 daniel-mar 1012
      Errors.Add(Format(LNG_EXCEPTION, [dllFile, E.ClassName, E.Message]));
69 daniel-mar 1013
      Exit;
1014
    end;
1015
  end;
1016
end;
1017
 
68 daniel-mar 1018
end.