Subversion Repositories userdetect2

Rev

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