Subversion Repositories userdetect2

Rev

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