Subversion Repositories userdetect2

Rev

Rev 73 | Rev 81 | 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
12
  Windows, SysUtils, Dialogs, ShellAPI;
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;
21
  EXTICODE_SYNTAX_ERROR = 13;
22
 
68 daniel-mar 23
type
24
  TArrayOfString = array of String;
25
 
26
  TIconFileIdx = record
27
    FileName: string;
28
    IconIndex: integer;
29
  end;
30
 
31
const
32
  // Prefixes for UD2_RunCmd()
33
  UD2_RUN_IN_OWN_DIRECTORY_PREFIX = '$RIOD$';
79 daniel-mar 34
  UD2_RUN_AS_ADMIN                = '$ADMIN$';
68 daniel-mar 35
 
36
function SplitString(const aSeparator, aString: String; aMax: Integer = 0): TArrayOfString;
37
function BetterInterpreteBool(str: String): boolean;
38
function GetOwnCmdName: string;
39
function ExpandEnvStr(const szInput: string): string;
79 daniel-mar 40
procedure UD2_RunCMD(cmdLine: string; WindowMode: integer=SW_NORMAL);
68 daniel-mar 41
function SplitIconString(IconString: string): TIconFileIdx;
42
// function GetHTML(AUrl: string): string;
43
procedure VTS_CheckUpdates(VTSID, CurVer: string);
71 daniel-mar 44
function FormatOSError(ec: DWORD): string;
68 daniel-mar 45
 
46
implementation
47
 
48
uses
49
  WinInet, Forms;
50
 
51
function SplitString(const aSeparator, aString: String; aMax: Integer = 0): TArrayOfString;
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
 
119
function BetterInterpreteBool(str: String): boolean;
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
 
79 daniel-mar 197
procedure UD2_RunCMD(cmdLine: string; WindowMode: integer=SW_NORMAL);
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;
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
 
68 daniel-mar 225
  cmdLine := ExpandEnvStr(cmdLine);
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
 
79 daniel-mar 259
  if Pos(UD2_RUN_IN_OWN_DIRECTORY_PREFIX, cmdLine) >= 1 then
68 daniel-mar 260
  begin
79 daniel-mar 261
    cmdLine := StringReplace(cmdLine, UD2_RUN_IN_OWN_DIRECTORY_PREFIX, '', [rfReplaceAll]);
262
 
68 daniel-mar 263
    cmdLine := Copy(cmdLine, 1+Length(UD2_RUN_IN_OWN_DIRECTORY_PREFIX), Length(cmdLine)-Length(UD2_RUN_IN_OWN_DIRECTORY_PREFIX));
264
 
265
    cmdFile := ExtractFileName(cmdLine);
266
    cmdDir  := ExtractFilePath(cmdLine);
267
  end
268
  else
269
  begin
270
    cmdFile := cmdLine;
271
    cmdDir := '';
272
  end;
273
 
274
  ZeroMemory(@sei, SizeOf(sei));
79 daniel-mar 275
 
276
  if Pos(UD2_RUN_AS_ADMIN, cmdLine) >= 1 then
277
  begin
278
    cmdLine := StringReplace(cmdLine, UD2_RUN_AS_ADMIN, '', [rfReplaceAll]);
279
 
280
    sei.lpVerb := 'runas';
281
  end;
282
 
68 daniel-mar 283
  sei.cbSize       := SizeOf(sei);
284
  sei.lpFile       := PChar(cmdFile);
70 daniel-mar 285
  {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
286
  sei.fMask        := SEE_MASK_FLAG_NO_UI;
287
  {$ENDIF}
68 daniel-mar 288
  if cmdArgs <> '' then sei.lpParameters := PChar(cmdArgs);
289
  if cmdDir  <> '' then sei.lpDirectory  := PChar(cmdDir);
290
  sei.nShow        := WindowMode;
291
  if ShellExecuteEx(@sei) then Exit;
70 daniel-mar 292
  {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
73 daniel-mar 293
  if not CheckLastOSCall(false) then ExitCode := EXITCODE_RUN_FAILURE;
70 daniel-mar 294
  {$ENDIF}
68 daniel-mar 295
end;
296
 
297
function GetHTML(AUrl: string): string;
298
// http://www.delphipraxis.net/post43515.html
79 daniel-mar 299
// Modificated by ViaThinkSoft
68 daniel-mar 300
var
301
  databuffer : array[0..4095] of char;
302
  ResStr : string;
303
  hSession, hfile: hInternet;
304
  dwindex,dwcodelen,dwread,dwNumber: cardinal;
305
  dwcode : array[1..20] of char;
306
  res    : pchar;
307
  Str    : pchar;
308
begin
79 daniel-mar 309
  ResStr := '';
310
  if system.pos('http://',lowercase(AUrl)) = 0 then
311
  begin
68 daniel-mar 312
     AUrl:='http://'+AUrl;
79 daniel-mar 313
  end;
68 daniel-mar 314
 
79 daniel-mar 315
  // [ViaThinkSoft] Added
68 daniel-mar 316
  Application.ProcessMessages;
317
 
318
  hSession:=InternetOpen('InetURL:/1.0',
319
                         INTERNET_OPEN_TYPE_PRECONFIG,
320
                         nil,
321
                         nil,
322
                         0);
323
  if assigned(hsession) then
324
  begin
79 daniel-mar 325
    // [ViaThinkSoft] Added
326
    Application.ProcessMessages;
68 daniel-mar 327
 
79 daniel-mar 328
    hfile := InternetOpenUrl(hsession,
329
                             pchar(AUrl),
330
                             nil,
331
                             0,
332
                             INTERNET_FLAG_RELOAD,
333
                             0);
334
    dwIndex   := 0;
68 daniel-mar 335
    dwCodeLen := 10;
336
 
79 daniel-mar 337
    // [ViaThinkSoft] Added
338
    Application.ProcessMessages;
68 daniel-mar 339
 
340
    HttpQueryInfo(hfile,
341
                  HTTP_QUERY_STATUS_CODE,
342
                  @dwcode,
343
                  dwcodeLen,
344
                  dwIndex);
345
    res := pchar(@dwcode);
346
    dwNumber := sizeof(databuffer)-1;
347
    if (res ='200') or (res ='302') then
348
    begin
349
      while (InternetReadfile(hfile,
350
                              @databuffer,
351
                              dwNumber,
352
                              DwRead)) do
353
      begin
354
 
79 daniel-mar 355
        // [ViaThinkSoft] Added
356
        Application.ProcessMessages;
68 daniel-mar 357
 
358
        if dwRead =0 then
359
          break;
360
        databuffer[dwread]:=#0;
361
        Str := pchar(@databuffer);
362
        resStr := resStr + Str;
363
      end;
364
    end
365
    else
366
      ResStr := 'Status:'+res;
367
    if assigned(hfile) then
368
      InternetCloseHandle(hfile);
369
  end;
370
 
371
  // Hinzugefügt
372
  Application.ProcessMessages;
373
 
374
  InternetCloseHandle(hsession);
375
  Result := resStr;
376
end;
377
 
378
procedure VTS_CheckUpdates(VTSID, CurVer: string);
379
resourcestring
380
  (*
381
  LNG_DOWNLOAD_ERR = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server temporär offline.';
382
  LNG_NEW_VERSION = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?';
383
  LNG_NO_UPDATE = 'Es ist keine neue Programmversion vorhanden.';
384
  *)
385
  LNG_DOWNLOAD_ERR = 'An error occurred while searching for updates. Please check your internet connection and firewall.';
386
  LNG_NEW_VERSION = 'A new version is available. Do you want to download it now?';
387
  LNG_NO_UPDATE = 'You already have the newest program version.';
388
var
79 daniel-mar 389
  status: string;
68 daniel-mar 390
begin
79 daniel-mar 391
  status := GetHTML('http://www.viathinksoft.de/update/?id='+VTSID);
392
  if Copy(status, 0, 7) = 'Status:' then
68 daniel-mar 393
  begin
394
    MessageDlg(LNG_DOWNLOAD_ERR, mtError, [mbOK], 0);
395
  end
396
  else
397
  begin
79 daniel-mar 398
    if status <> CurVer then
68 daniel-mar 399
    begin
400
      if MessageDlg(LNG_NEW_VERSION, mtConfirmation, mbYesNoCancel, 0) = ID_YES then
401
      begin
79 daniel-mar 402
        shellexecute(application.handle, 'open', pchar('http://www.viathinksoft.de/update/?id=@'+VTSID), '', '', sw_normal);
68 daniel-mar 403
      end;
404
    end
405
    else
406
    begin
407
      MessageDlg(LNG_NO_UPDATE, mtInformation, [mbOk], 0);
408
    end;
409
  end;
410
end;
411
 
412
end.