Subversion Repositories userdetect2

Rev

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