Subversion Repositories userdetect2

Rev

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