Subversion Repositories userdetect2

Rev

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