Subversion Repositories userdetect2

Rev

Rev 70 | Rev 73 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit UD2_Utils;
  2.  
  3. interface
  4.  
  5. {$IF CompilerVersion >= 25.0}
  6. {$LEGACYIFEND ON}
  7. {$IFEND}
  8.  
  9. {$INCLUDE 'UserDetect2.inc'}
  10.  
  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. function FormatOSError(ec: DWORD): string;
  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.  
  140. function FormatOSError(ec: DWORD): string;
  141. resourcestring
  142.   LNG_UNKNOWN_ERROR = 'Operating system error %d';
  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);
  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
  163.       MessageDlg(FormatOSError(LastError), mtError, [mbOK], 0);
  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);
  263.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  264.   sei.fMask        := SEE_MASK_FLAG_NO_UI;
  265.   {$ENDIF}
  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;
  270.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  271.   CheckLastOSCall(false);
  272.   {$ENDIF}
  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.
  389.