Subversion Repositories userdetect2

Rev

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