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_Utils;
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
85 daniel-mar 12
  Windows, SysUtils, Dialogs, ShellAPI, Classes, UD2_Parsing;
68 daniel-mar 13
 
73 daniel-mar 14
const
15
  EXITCODE_OK = 0;
16
  EXITCODE_TASK_NOTHING_MATCHES = 1;
17
  EXITCODE_RUN_FAILURE = 2;
18
  EXITCODE_TASK_NOT_EXISTS = 10;
19
  EXITCODE_INI_NOT_FOUND = 11;
20
  EXITCODE_RUNCMD_SYNTAX_ERROR = 12;
81 daniel-mar 21
  EXITCODE_SYNTAX_ERROR = 13;
73 daniel-mar 22
 
68 daniel-mar 23
type
82 daniel-mar 24
  TArrayOfString = array of string;
68 daniel-mar 25
 
26
  TIconFileIdx = record
27
    FileName: string;
28
    IconIndex: integer;
29
  end;
30
 
82 daniel-mar 31
function SplitString(const aSeparator, aString: string; aMax: Integer = 0): TArrayOfString;
86 daniel-mar 32
function MergeString(ary: TArrayOfString; glue: string): string;
82 daniel-mar 33
function BetterInterpreteBool(str: string): boolean;
68 daniel-mar 34
function GetOwnCmdName: string;
35
function ExpandEnvStr(const szInput: string): string;
85 daniel-mar 36
procedure UD2_RunCMD(cmd: TUD2Command);
68 daniel-mar 37
function SplitIconString(IconString: string): TIconFileIdx;
38
// function GetHTML(AUrl: string): string;
39
procedure VTS_CheckUpdates(VTSID, CurVer: string);
71 daniel-mar 40
function FormatOSError(ec: DWORD): string;
81 daniel-mar 41
function CheckBoolParam(idx: integer; name: string): boolean;
82 daniel-mar 42
function IndexOf_CS(aStrings: TStrings; aToken: String): Integer;
43
function UD2_GetThreadErrorMode: DWORD;
44
function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
68 daniel-mar 45
 
46
implementation
47
 
48
uses
49
  WinInet, Forms;
50
 
82 daniel-mar 51
function SplitString(const aSeparator, aString: string; aMax: Integer = 0): TArrayOfString;
68 daniel-mar 52
// http://stackoverflow.com/a/2626991/3544341
53
var
54
  i, strt, cnt: Integer;
55
  sepLen: Integer;
56
 
57
  procedure AddString(aEnd: Integer = -1);
58
  var
59
    endPos: Integer;
60
  begin
61
    if (aEnd = -1) then
62
      endPos := i
63
    else
64
      endPos := aEnd + 1;
65
 
66
    if (strt < endPos) then
67
      result[cnt] := Copy(aString, strt, endPos - strt)
68
    else
69
      result[cnt] := '';
70
 
71
    Inc(cnt);
72
  end;
73
 
74
begin
75
  if (aString = '') or (aMax < 0) then
76
  begin
77
    SetLength(result, 0);
78
    EXIT;
79
  end;
80
 
81
  if (aSeparator = '') then
82
  begin
83
    SetLength(result, 1);
84
    result[0] := aString;
85
    EXIT;
86
  end;
87
 
88
  sepLen := Length(aSeparator);
89
  SetLength(result, (Length(aString) div sepLen) + 1);
90
 
91
  i     := 1;
92
  strt  := i;
93
  cnt   := 0;
94
  while (i <= (Length(aString)- sepLen + 1)) do
95
  begin
96
    if (aString[i] = aSeparator[1]) then
97
      if (Copy(aString, i, sepLen) = aSeparator) then
98
      begin
99
        AddString;
100
 
101
        if (cnt = aMax) then
102
        begin
103
          SetLength(result, cnt);
104
          EXIT;
105
        end;
106
 
107
        Inc(i, sepLen - 1);
108
        strt := i + 1;
109
      end;
110
 
111
    Inc(i);
112
  end;
113
 
114
  AddString(Length(aString));
115
 
116
  SetLength(result, cnt);
117
end;
118
 
82 daniel-mar 119
function BetterInterpreteBool(str: string): boolean;
68 daniel-mar 120
resourcestring
121
  LNG_CANNOT_INTERPRETE_BOOL = 'Cannot determinate the boolean value of "%s"';
122
begin
123
  str := LowerCase(str);
124
  if (str = 'yes') or (str = 'true') or (str = '1') then
125
    result := true
126
  else if (str = 'no') or (str = 'false') or (str = '0') then
127
    result := false
128
  else
129
    raise EConvertError.CreateFmt(LNG_CANNOT_INTERPRETE_BOOL, [str]);
130
end;
131
 
132
function GetOwnCmdName: string;
133
begin
134
  result := ParamStr(0);
135
  result := ExtractFileName(result);
136
  result := ChangeFileExt(result, '');
137
  result := UpperCase(result);
138
end;
139
 
140
function ExpandEnvStr(const szInput: string): string;
141
// http://stackoverflow.com/a/2833147/3544341
142
const
143
  MAXSIZE = 32768;
144
begin
145
  SetLength(Result, MAXSIZE);
146
  SetLength(Result, ExpandEnvironmentStrings(pchar(szInput),
147
    @Result[1],length(Result)));
148
end;
149
 
71 daniel-mar 150
function FormatOSError(ec: DWORD): string;
68 daniel-mar 151
resourcestring
152
  LNG_UNKNOWN_ERROR = 'Operating system error %d';
71 daniel-mar 153
begin
154
  result := SysErrorMessage(ec);
155
 
156
  // Some errors have no error message, e.g. error 193 (BAD_EXE_FORMAT) in the German version of Windows 10
157
  if result = '' then result := Format(LNG_UNKNOWN_ERROR, [ec]);
158
end;
159
 
73 daniel-mar 160
function CheckLastOSCall(AThrowException: boolean): boolean;
68 daniel-mar 161
var
162
  LastError: Cardinal;
163
begin
164
  LastError := GetLastError;
73 daniel-mar 165
  result := LastError = 0;
166
  if not result then
68 daniel-mar 167
  begin
168
    if AThrowException then
169
    begin
170
      RaiseLastOSError;
171
    end
172
    else
173
    begin
71 daniel-mar 174
      MessageDlg(FormatOSError(LastError), mtError, [mbOK], 0);
68 daniel-mar 175
    end;
176
  end;
177
end;
178
 
179
function SplitIconString(IconString: string): TIconFileIdx;
180
var
181
  p: integer;
182
begin
183
  p := Pos(',', IconString);
184
 
185
  if p = 0 then
186
  begin
187
    result.FileName := IconString;
188
    result.IconIndex := 0;
189
  end
190
  else
191
  begin
192
    result.FileName  := ExpandEnvStr(copy(IconString, 0, p-1));
193
    result.IconIndex := StrToInt(Copy(IconString, p+1, Length(IconString)-p));
194
  end;
195
end;
196
 
85 daniel-mar 197
procedure UD2_RunCMD(cmd: TUD2Command);
68 daniel-mar 198
// Discussion: http://stackoverflow.com/questions/32802679/acceptable-replacement-for-winexec/32804669#32804669
199
// Version 1: http://pastebin.com/xQjDmyVe
200
// --> CreateProcess + ShellExecuteEx
201
// --> Problem: Run-In-Same-Directory functionality is not possible
202
//              (requires manual command and argument separation)
203
// Version 2: http://pastebin.com/YpUmF5rd
204
// --> Splits command and arguments manually, and uses ShellExecute
205
// --> Problem: error handling wrong
206
// --> Problem: Run-In-Same-Directory functionality is not implemented
207
// Current version:
208
// --> Splits command and arguments manually, and uses ShellExecute
209
// --> Run-In-Same-Directory functionality is implemented
210
resourcestring
211
  LNG_INVALID_SYNTAX = 'The command line has an invalid syntax';
212
var
213
  cmdFile, cmdArgs, cmdDir: string;
214
  p: integer;
215
  sei: TShellExecuteInfo;
85 daniel-mar 216
  cmdLine: string;
68 daniel-mar 217
begin
218
  // We need a function which does following:
219
  // 1. Replace the Environment strings, e.g. %SystemRoot%
220
  // 2. Runs EXE files with parameters (e.g. "cmd.exe /?")
221
  // 3. Runs EXE files without path (e.g. "calc.exe")
222
  // 4. Runs EXE files without extension (e.g. "calc")
223
  // 5. Runs non-EXE files (e.g. "Letter.doc")
224
  // 6. Commands with white spaces (e.g. "C:\Program Files\xyz.exe") must be enclosed in quotes.
79 daniel-mar 225
 
85 daniel-mar 226
  cmdLine := ExpandEnvStr(cmd.executable);
68 daniel-mar 227
 
228
  // Split command line from argument list
229
  if Copy(cmdLine, 1, 1) = '"' then
230
  begin
231
    cmdLine := Copy(cmdLine, 2, Length(cmdLine)-1);
232
    p := Pos('"', cmdLine);
233
    if p = 0 then
234
    begin
235
      // No matching quotes
236
      // CreateProcess() handles the whole command line as single file name  ("abc -> "abc")
237
      // ShellExecuteEx() does not accept the command line
73 daniel-mar 238
      ExitCode := EXITCODE_RUNCMD_SYNTAX_ERROR;
68 daniel-mar 239
      MessageDlg(LNG_INVALID_SYNTAX, mtError, [mbOK], 0);
240
      Exit;
241
    end;
242
    cmdFile := Copy(cmdLine, 1, p-1);
243
    cmdArgs := Copy(cmdLine, p+2, Length(cmdLine)-p-1);
244
  end
245
  else
246
  begin
247
    p := Pos(' ', cmdLine);
248
    if p = 0 then
249
    begin
250
      cmdFile := cmdLine;
251
      cmdArgs := '';
252
    end
253
    else
254
    begin
255
      cmdFile := Copy(cmdLine, 1, p-1);
256
      cmdArgs := Copy(cmdLine, p+1, Length(cmdLine)-p);
257
    end;
258
  end;
259
 
81 daniel-mar 260
  ZeroMemory(@sei, SizeOf(sei));
261
 
85 daniel-mar 262
  if cmd.runAsAdmin then
81 daniel-mar 263
  begin
264
    sei.lpVerb := 'runas';
265
  end;
266
 
85 daniel-mar 267
  if cmd.runInOwnDirectory then
68 daniel-mar 268
  begin
269
    cmdFile := ExtractFileName(cmdLine);
270
    cmdDir  := ExtractFilePath(cmdLine);
271
  end
272
  else
273
  begin
274
    cmdFile := cmdLine;
275
    cmdDir := '';
276
  end;
277
 
278
  sei.cbSize       := SizeOf(sei);
279
  sei.lpFile       := PChar(cmdFile);
70 daniel-mar 280
  {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
281
  sei.fMask        := SEE_MASK_FLAG_NO_UI;
282
  {$ENDIF}
68 daniel-mar 283
  if cmdArgs <> '' then sei.lpParameters := PChar(cmdArgs);
284
  if cmdDir  <> '' then sei.lpDirectory  := PChar(cmdDir);
85 daniel-mar 285
  sei.nShow        := cmd.windowMode;
68 daniel-mar 286
  if ShellExecuteEx(@sei) then Exit;
70 daniel-mar 287
  {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
73 daniel-mar 288
  if not CheckLastOSCall(false) then ExitCode := EXITCODE_RUN_FAILURE;
70 daniel-mar 289
  {$ENDIF}
68 daniel-mar 290
end;
291
 
292
function GetHTML(AUrl: string): string;
293
// http://www.delphipraxis.net/post43515.html
79 daniel-mar 294
// Modificated by ViaThinkSoft
68 daniel-mar 295
var
296
  databuffer : array[0..4095] of char;
297
  ResStr : string;
298
  hSession, hfile: hInternet;
299
  dwindex,dwcodelen,dwread,dwNumber: cardinal;
300
  dwcode : array[1..20] of char;
301
  res    : pchar;
302
  Str    : pchar;
303
begin
79 daniel-mar 304
  ResStr := '';
305
  if system.pos('http://',lowercase(AUrl)) = 0 then
306
  begin
68 daniel-mar 307
     AUrl:='http://'+AUrl;
79 daniel-mar 308
  end;
68 daniel-mar 309
 
79 daniel-mar 310
  // [ViaThinkSoft] Added
68 daniel-mar 311
  Application.ProcessMessages;
312
 
313
  hSession:=InternetOpen('InetURL:/1.0',
314
                         INTERNET_OPEN_TYPE_PRECONFIG,
315
                         nil,
316
                         nil,
317
                         0);
318
  if assigned(hsession) then
319
  begin
79 daniel-mar 320
    // [ViaThinkSoft] Added
321
    Application.ProcessMessages;
68 daniel-mar 322
 
79 daniel-mar 323
    hfile := InternetOpenUrl(hsession,
324
                             pchar(AUrl),
325
                             nil,
326
                             0,
327
                             INTERNET_FLAG_RELOAD,
328
                             0);
329
    dwIndex   := 0;
68 daniel-mar 330
    dwCodeLen := 10;
331
 
79 daniel-mar 332
    // [ViaThinkSoft] Added
333
    Application.ProcessMessages;
68 daniel-mar 334
 
335
    HttpQueryInfo(hfile,
336
                  HTTP_QUERY_STATUS_CODE,
337
                  @dwcode,
338
                  dwcodeLen,
339
                  dwIndex);
340
    res := pchar(@dwcode);
341
    dwNumber := sizeof(databuffer)-1;
342
    if (res ='200') or (res ='302') then
343
    begin
344
      while (InternetReadfile(hfile,
345
                              @databuffer,
346
                              dwNumber,
347
                              DwRead)) do
348
      begin
349
 
79 daniel-mar 350
        // [ViaThinkSoft] Added
351
        Application.ProcessMessages;
68 daniel-mar 352
 
353
        if dwRead =0 then
354
          break;
355
        databuffer[dwread]:=#0;
356
        Str := pchar(@databuffer);
357
        resStr := resStr + Str;
358
      end;
359
    end
360
    else
361
      ResStr := 'Status:'+res;
362
    if assigned(hfile) then
363
      InternetCloseHandle(hfile);
364
  end;
365
 
366
  // Hinzugefügt
367
  Application.ProcessMessages;
368
 
369
  InternetCloseHandle(hsession);
370
  Result := resStr;
371
end;
372
 
373
procedure VTS_CheckUpdates(VTSID, CurVer: string);
374
resourcestring
375
  (*
376
  LNG_DOWNLOAD_ERR = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server temporär offline.';
377
  LNG_NEW_VERSION = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?';
378
  LNG_NO_UPDATE = 'Es ist keine neue Programmversion vorhanden.';
379
  *)
380
  LNG_DOWNLOAD_ERR = 'An error occurred while searching for updates. Please check your internet connection and firewall.';
381
  LNG_NEW_VERSION = 'A new version is available. Do you want to download it now?';
382
  LNG_NO_UPDATE = 'You already have the newest program version.';
383
var
79 daniel-mar 384
  status: string;
68 daniel-mar 385
begin
79 daniel-mar 386
  status := GetHTML('http://www.viathinksoft.de/update/?id='+VTSID);
387
  if Copy(status, 0, 7) = 'Status:' then
68 daniel-mar 388
  begin
389
    MessageDlg(LNG_DOWNLOAD_ERR, mtError, [mbOK], 0);
390
  end
391
  else
392
  begin
79 daniel-mar 393
    if status <> CurVer then
68 daniel-mar 394
    begin
395
      if MessageDlg(LNG_NEW_VERSION, mtConfirmation, mbYesNoCancel, 0) = ID_YES then
396
      begin
82 daniel-mar 397
        shellexecute(application.handle, 'open', pchar('http://www.viathinksoft.de/update/?id=@'+VTSID), '', '', SW_Normal);
68 daniel-mar 398
      end;
399
    end
400
    else
401
    begin
402
      MessageDlg(LNG_NO_UPDATE, mtInformation, [mbOk], 0);
403
    end;
404
  end;
405
end;
406
 
81 daniel-mar 407
function CheckBoolParam(idx: integer; name: string): boolean;
408
begin
409
  Result := ('/'+LowerCase(name) = LowerCase(ParamStr(idx))) or
410
            ('-'+LowerCase(name) = LowerCase(ParamStr(idx)));
411
end;
412
 
82 daniel-mar 413
// function GetThreadErrorMode: DWORD; stdcall; external kernel32 name 'GetThreadErrorMode';
414
function UD2_GetThreadErrorMode: DWORD;
415
type
416
  TFuncGetThreadErrorMode = function: DWORD; stdcall;
417
var
418
  dllHandle: Cardinal;
419
  fGetThreadErrorMode: TFuncGetThreadErrorMode;
420
begin
421
  dllHandle := LoadLibrary(kernel32);
422
  if dllHandle = 0 then
423
  begin
424
    result := 0;
425
    Exit;
426
  end;
427
  try
428
    @fGetThreadErrorMode := GetProcAddress(dllHandle, 'GetThreadErrorMode');
429
    if not Assigned(fGetThreadErrorMode) then
430
    begin
431
      result := 0; // Windows Vista and prior
432
      Exit;
433
    end;
434
    result := fGetThreadErrorMode();
435
  finally
436
    FreeLibrary(dllHandle);
437
  end;
438
end;
439
 
440
// function SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall; external kernel32 name 'SetThreadErrorMode';
441
function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
442
type
443
  TFuncSetThreadErrorMode = function(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall;
444
var
445
  dllHandle: Cardinal;
446
  fSetThreadErrorMode: TFuncSetThreadErrorMode;
447
begin
448
  dllHandle := LoadLibrary(kernel32);
449
  if dllHandle = 0 then
450
  begin
451
    result := FALSE;
452
    if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
453
    Exit;
454
  end;
455
  try
456
    @fSetThreadErrorMode := GetProcAddress(dllHandle, 'SetThreadErrorMode');
457
    if not Assigned(fSetThreadErrorMode) then
458
    begin
459
      result := FALSE; // Windows Vista and prior
460
      if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
461
      Exit;
462
    end;
463
    result := fSetThreadErrorMode(dwNewMode, lpOldMode);
464
  finally
465
    FreeLibrary(dllHandle);
466
  end;
467
end;
468
 
469
function IndexOf_CS(aStrings: TStrings; aToken: String): Integer;
470
var
85 daniel-mar 471
  i: Integer;
82 daniel-mar 472
begin
473
  Result := -1;
85 daniel-mar 474
  for i := 0 to aStrings.Count-1 do
475
  begin
476
    if aStrings[i] = aToken then
477
    begin
82 daniel-mar 478
      Result := i;
479
      Break;
480
    end;
85 daniel-mar 481
  end;
82 daniel-mar 482
end;
483
 
86 daniel-mar 484
function MergeString(ary: TArrayOfString; glue: string): string;
485
var
486
  i: integer;
487
begin
488
  result := '';
489
  for i := Low(ary) to High(ary) do
490
  begin
491
    if result <> '' then result := result + glue;
492
    result := result + ary[i];
493
  end;
494
end;
495
 
68 daniel-mar 496
end.