Subversion Repositories userdetect2

Rev

Rev 73 | 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. 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.  
  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);
  43. function FormatOSError(ec: DWORD): string;
  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.  
  149. function FormatOSError(ec: DWORD): string;
  150. resourcestring
  151.   LNG_UNKNOWN_ERROR = 'Operating system error %d';
  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.  
  159. function CheckLastOSCall(AThrowException: boolean): boolean;
  160. var
  161.   LastError: Cardinal;
  162. begin
  163.   LastError := GetLastError;
  164.   result := LastError = 0;
  165.   if not result then
  166.   begin
  167.     if AThrowException then
  168.     begin
  169.       RaiseLastOSError;
  170.     end
  171.     else
  172.     begin
  173.       MessageDlg(FormatOSError(LastError), mtError, [mbOK], 0);
  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
  236.       ExitCode := EXITCODE_RUNCMD_SYNTAX_ERROR;
  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);
  274.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  275.   sei.fMask        := SEE_MASK_FLAG_NO_UI;
  276.   {$ENDIF}
  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;
  281.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  282.   if not CheckLastOSCall(false) then ExitCode := EXITCODE_RUN_FAILURE;
  283.   {$ENDIF}
  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.
  400.