Subversion Repositories userdetect2

Rev

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