Subversion Repositories userdetect2

Rev

Rev 71 | Rev 79 | 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$';
34
 
35
function SplitString(const aSeparator, aString: String; aMax: Integer = 0): TArrayOfString;
36
function BetterInterpreteBool(str: String): boolean;
37
function GetOwnCmdName: string;
38
function ExpandEnvStr(const szInput: string): string;
39
procedure UD2_RunCMD(cmdLine: string; WindowMode: integer);
40
function SplitIconString(IconString: string): TIconFileIdx;
41
// function GetHTML(AUrl: string): string;
42
procedure VTS_CheckUpdates(VTSID, CurVer: string);
71 daniel-mar 43
function FormatOSError(ec: DWORD): string;
68 daniel-mar 44
 
45
implementation
46
 
47
uses
48
  WinInet, Forms;
49
 
50
function SplitString(const aSeparator, aString: String; aMax: Integer = 0): TArrayOfString;
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
 
118
function BetterInterpreteBool(str: String): boolean;
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
 
196
procedure UD2_RunCMD(cmdLine: string; WindowMode: integer);
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;
215
begin
216
  // We need a function which does following:
217
  // 1. Replace the Environment strings, e.g. %SystemRoot%
218
  // 2. Runs EXE files with parameters (e.g. "cmd.exe /?")
219
  // 3. Runs EXE files without path (e.g. "calc.exe")
220
  // 4. Runs EXE files without extension (e.g. "calc")
221
  // 5. Runs non-EXE files (e.g. "Letter.doc")
222
  // 6. Commands with white spaces (e.g. "C:\Program Files\xyz.exe") must be enclosed in quotes.
223
 
224
  cmdLine := ExpandEnvStr(cmdLine);
225
 
226
  // Split command line from argument list
227
  if Copy(cmdLine, 1, 1) = '"' then
228
  begin
229
    cmdLine := Copy(cmdLine, 2, Length(cmdLine)-1);
230
    p := Pos('"', cmdLine);
231
    if p = 0 then
232
    begin
233
      // No matching quotes
234
      // CreateProcess() handles the whole command line as single file name  ("abc -> "abc")
235
      // ShellExecuteEx() does not accept the command line
73 daniel-mar 236
      ExitCode := EXITCODE_RUNCMD_SYNTAX_ERROR;
68 daniel-mar 237
      MessageDlg(LNG_INVALID_SYNTAX, mtError, [mbOK], 0);
238
      Exit;
239
    end;
240
    cmdFile := Copy(cmdLine, 1, p-1);
241
    cmdArgs := Copy(cmdLine, p+2, Length(cmdLine)-p-1);
242
  end
243
  else
244
  begin
245
    p := Pos(' ', cmdLine);
246
    if p = 0 then
247
    begin
248
      cmdFile := cmdLine;
249
      cmdArgs := '';
250
    end
251
    else
252
    begin
253
      cmdFile := Copy(cmdLine, 1, p-1);
254
      cmdArgs := Copy(cmdLine, p+1, Length(cmdLine)-p);
255
    end;
256
  end;
257
 
258
  if Copy(cmdLine, 1, Length(UD2_RUN_IN_OWN_DIRECTORY_PREFIX)) = UD2_RUN_IN_OWN_DIRECTORY_PREFIX then
259
  begin
260
    cmdLine := Copy(cmdLine, 1+Length(UD2_RUN_IN_OWN_DIRECTORY_PREFIX), Length(cmdLine)-Length(UD2_RUN_IN_OWN_DIRECTORY_PREFIX));
261
 
262
    cmdFile := ExtractFileName(cmdLine);
263
    cmdDir  := ExtractFilePath(cmdLine);
264
  end
265
  else
266
  begin
267
    cmdFile := cmdLine;
268
    cmdDir := '';
269
  end;
270
 
271
  ZeroMemory(@sei, SizeOf(sei));
272
  sei.cbSize       := SizeOf(sei);
273
  sei.lpFile       := PChar(cmdFile);
70 daniel-mar 274
  {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
275
  sei.fMask        := SEE_MASK_FLAG_NO_UI;
276
  {$ENDIF}
68 daniel-mar 277
  if cmdArgs <> '' then sei.lpParameters := PChar(cmdArgs);
278
  if cmdDir  <> '' then sei.lpDirectory  := PChar(cmdDir);
279
  sei.nShow        := WindowMode;
280
  if ShellExecuteEx(@sei) then Exit;
70 daniel-mar 281
  {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
73 daniel-mar 282
  if not CheckLastOSCall(false) then ExitCode := EXITCODE_RUN_FAILURE;
70 daniel-mar 283
  {$ENDIF}
68 daniel-mar 284
end;
285
 
286
function GetHTML(AUrl: string): string;
287
// http://www.delphipraxis.net/post43515.html
288
var
289
  databuffer : array[0..4095] of char;
290
  ResStr : string;
291
  hSession, hfile: hInternet;
292
  dwindex,dwcodelen,dwread,dwNumber: cardinal;
293
  dwcode : array[1..20] of char;
294
  res    : pchar;
295
  Str    : pchar;
296
begin
297
  ResStr:='';
298
  if system.pos('http://',lowercase(AUrl))=0 then
299
     AUrl:='http://'+AUrl;
300
 
301
  // Hinzugefügt
302
  Application.ProcessMessages;
303
 
304
  hSession:=InternetOpen('InetURL:/1.0',
305
                         INTERNET_OPEN_TYPE_PRECONFIG,
306
                         nil,
307
                         nil,
308
                         0);
309
  if assigned(hsession) then
310
  begin
311
    // Hinzugefügt
312
    application.ProcessMessages;
313
 
314
    hfile:=InternetOpenUrl(
315
           hsession,
316
           pchar(AUrl),
317
           nil,
318
           0,
319
           INTERNET_FLAG_RELOAD,
320
           0);
321
    dwIndex  := 0;
322
    dwCodeLen := 10;
323
 
324
    // Hinzugefügt
325
    application.ProcessMessages;
326
 
327
    HttpQueryInfo(hfile,
328
                  HTTP_QUERY_STATUS_CODE,
329
                  @dwcode,
330
                  dwcodeLen,
331
                  dwIndex);
332
    res := pchar(@dwcode);
333
    dwNumber := sizeof(databuffer)-1;
334
    if (res ='200') or (res ='302') then
335
    begin
336
      while (InternetReadfile(hfile,
337
                              @databuffer,
338
                              dwNumber,
339
                              DwRead)) do
340
      begin
341
 
342
        // Hinzugefügt
343
        application.ProcessMessages;
344
 
345
        if dwRead =0 then
346
          break;
347
        databuffer[dwread]:=#0;
348
        Str := pchar(@databuffer);
349
        resStr := resStr + Str;
350
      end;
351
    end
352
    else
353
      ResStr := 'Status:'+res;
354
    if assigned(hfile) then
355
      InternetCloseHandle(hfile);
356
  end;
357
 
358
  // Hinzugefügt
359
  Application.ProcessMessages;
360
 
361
  InternetCloseHandle(hsession);
362
  Result := resStr;
363
end;
364
 
365
procedure VTS_CheckUpdates(VTSID, CurVer: string);
366
resourcestring
367
  (*
368
  LNG_DOWNLOAD_ERR = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server temporär offline.';
369
  LNG_NEW_VERSION = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?';
370
  LNG_NO_UPDATE = 'Es ist keine neue Programmversion vorhanden.';
371
  *)
372
  LNG_DOWNLOAD_ERR = 'An error occurred while searching for updates. Please check your internet connection and firewall.';
373
  LNG_NEW_VERSION = 'A new version is available. Do you want to download it now?';
374
  LNG_NO_UPDATE = 'You already have the newest program version.';
375
var
376
  temp: string;
377
begin
378
  temp := GetHTML('http://www.viathinksoft.de/update/?id='+VTSID);
379
  if Copy(temp, 0, 7) = 'Status:' then
380
  begin
381
    MessageDlg(LNG_DOWNLOAD_ERR, mtError, [mbOK], 0);
382
  end
383
  else
384
  begin
385
    if GetHTML('http://www.viathinksoft.de/update/?id='+VTSID) <> CurVer then
386
    begin
387
      if MessageDlg(LNG_NEW_VERSION, mtConfirmation, mbYesNoCancel, 0) = ID_YES then
388
      begin
389
        shellexecute(application.handle, 'open', pchar('http://www.viathinksoft.de/update/?id=@spacemission'), '', '', sw_normal);
390
      end;
391
    end
392
    else
393
    begin
394
      MessageDlg(LNG_NO_UPDATE, mtInformation, [mbOk], 0);
395
    end;
396
  end;
397
end;
398
 
399
end.