Subversion Repositories userdetect2

Rev

Rev 87 | Rev 95 | 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, Classes, UD2_Parsing;
  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.   EXITCODE_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. function SplitString(const aSeparator, aString: string; aMax: Integer = 0): TArrayOfString;
  32. function MergeString(ary: TArrayOfString; glue: string): string;
  33. function BetterInterpreteBool(str: string): boolean;
  34. function GetOwnCmdName: string;
  35. function ExpandEnvStr(const szInput: string): string;
  36. procedure UD2_RunCMD(cmd: TUD2Command);
  37. function SplitIconString(IconString: string): TIconFileIdx;
  38. // function GetHTML(AUrl: string): string;
  39. procedure VTS_CheckUpdates(VTSID, CurVer: string);
  40. function FormatOSError(ec: DWORD): string;
  41. function CheckBoolParam(idx: integer; name: string): boolean;
  42. function IndexOf_CS(aStrings: TStrings; aToken: string): Integer;
  43. function UD2_GetThreadErrorMode: DWORD;
  44. function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
  45.  
  46. implementation
  47.  
  48. uses
  49.   idhttp, 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(cmd: TUD2Command);
  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.   cmdLine: string;
  217. begin
  218.   // We need a function which does following:
  219.   // 1. Replace the Environment strings, e.g. %SystemRoot%
  220.   // 2. Runs EXE files with parameters (e.g. "cmd.exe /?")
  221.   // 3. Runs EXE files without path (e.g. "calc.exe")
  222.   // 4. Runs EXE files without extension (e.g. "calc")
  223.   // 5. Runs non-EXE files (e.g. "Letter.doc")
  224.   // 6. Commands with white spaces (e.g. "C:\Program Files\xyz.exe") must be enclosed in quotes.
  225.  
  226.   cmdLine := ExpandEnvStr(cmd.executable);
  227.  
  228.   // Split command line from argument list
  229.   if Copy(cmdLine, 1, 1) = '"' then
  230.   begin
  231.     cmdLine := Copy(cmdLine, 2, Length(cmdLine)-1);
  232.     p := Pos('"', cmdLine);
  233.     if p = 0 then
  234.     begin
  235.       // No matching quotes
  236.       // CreateProcess() handles the whole command line as single file name  ("abc -> "abc")
  237.       // ShellExecuteEx() does not accept the command line
  238.       ExitCode := EXITCODE_RUNCMD_SYNTAX_ERROR;
  239.       MessageDlg(LNG_INVALID_SYNTAX, mtError, [mbOK], 0);
  240.       Exit;
  241.     end;
  242.     cmdFile := Copy(cmdLine, 1, p-1);
  243.     cmdArgs := Copy(cmdLine, p+2, Length(cmdLine)-p-1);
  244.   end
  245.   else
  246.   begin
  247.     p := Pos(' ', cmdLine);
  248.     if p = 0 then
  249.     begin
  250.       cmdFile := cmdLine;
  251.       cmdArgs := '';
  252.     end
  253.     else
  254.     begin
  255.       cmdFile := Copy(cmdLine, 1, p-1);
  256.       cmdArgs := Copy(cmdLine, p+1, Length(cmdLine)-p);
  257.     end;
  258.   end;
  259.  
  260.   ZeroMemory(@sei, SizeOf(sei));
  261.  
  262.   if cmd.runAsAdmin then
  263.   begin
  264.     sei.lpVerb := 'runas';
  265.   end;
  266.  
  267.   if cmd.runInOwnDirectory then
  268.   begin
  269.     cmdFile := ExtractFileName(cmdLine);
  270.     cmdDir  := ExtractFilePath(cmdLine);
  271.   end
  272.   else
  273.   begin
  274.     cmdFile := cmdLine;
  275.     cmdDir := '';
  276.   end;
  277.  
  278.   sei.cbSize       := SizeOf(sei);
  279.   sei.lpFile       := PChar(cmdFile);
  280.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  281.   sei.fMask        := SEE_MASK_FLAG_NO_UI;
  282.   {$ENDIF}
  283.   if cmdArgs <> '' then sei.lpParameters := PChar(cmdArgs);
  284.   if cmdDir  <> '' then sei.lpDirectory  := PChar(cmdDir);
  285.   sei.nShow        := cmd.windowMode;
  286.   if ShellExecuteEx(@sei) then Exit;
  287.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  288.   if not CheckLastOSCall(false) then ExitCode := EXITCODE_RUN_FAILURE;
  289.   {$ENDIF}
  290. end;
  291.  
  292. function GetHTML(const url: string): string;
  293. var
  294.   idhttp :Tidhttp;
  295. begin
  296.   idhttp := Tidhttp.Create(nil);
  297.   try
  298.     result := idhttp.Get(url);
  299.   finally
  300.     idhttp.Free;
  301.   end;
  302. end;
  303.  
  304. procedure VTS_CheckUpdates(VTSID, CurVer: string);
  305. resourcestring
  306.   (*
  307.   LNG_DOWNLOAD_ERR = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server temporär offline.';
  308.   LNG_NEW_VERSION = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?';
  309.   LNG_NO_UPDATE = 'Es ist keine neue Programmversion vorhanden.';
  310.   *)
  311.   LNG_DOWNLOAD_ERR = 'An error occurred while searching for updates. Please check your internet connection and firewall.';
  312.   LNG_NEW_VERSION = 'A new version is available. Do you want to download it now?';
  313.   LNG_NO_UPDATE = 'You already have the newest program version.';
  314. var
  315.   status: string;
  316. begin
  317.   status := GetHTML('http://www.viathinksoft.de/update/?id='+VTSID);
  318.   if Copy(status, 0, 7) = 'Status:' then
  319.   begin
  320.     MessageDlg(LNG_DOWNLOAD_ERR, mtError, [mbOK], 0);
  321.   end
  322.   else
  323.   begin
  324.     if status <> CurVer then
  325.     begin
  326.       if MessageDlg(LNG_NEW_VERSION, mtConfirmation, mbYesNoCancel, 0) = ID_YES then
  327.       begin
  328.         shellexecute(application.handle, 'open', pchar('http://www.viathinksoft.de/update/?id=@'+VTSID), '', '', SW_Normal);
  329.       end;
  330.     end
  331.     else
  332.     begin
  333.       MessageDlg(LNG_NO_UPDATE, mtInformation, [mbOk], 0);
  334.     end;
  335.   end;
  336. end;
  337.  
  338. function CheckBoolParam(idx: integer; name: string): boolean;
  339. begin
  340.   Result := ('/'+LowerCase(name) = LowerCase(ParamStr(idx))) or
  341.             ('-'+LowerCase(name) = LowerCase(ParamStr(idx)));
  342. end;
  343.  
  344. // function GetThreadErrorMode: DWORD; stdcall; external kernel32 name 'GetThreadErrorMode';
  345. function UD2_GetThreadErrorMode: DWORD;
  346. type
  347.   TFuncGetThreadErrorMode = function: DWORD; stdcall;
  348. var
  349.   dllHandle: Cardinal;
  350.   fGetThreadErrorMode: TFuncGetThreadErrorMode;
  351. begin
  352.   dllHandle := LoadLibrary(kernel32);
  353.   if dllHandle = 0 then
  354.   begin
  355.     result := 0;
  356.     Exit;
  357.   end;
  358.   try
  359.     @fGetThreadErrorMode := GetProcAddress(dllHandle, 'GetThreadErrorMode');
  360.     if not Assigned(fGetThreadErrorMode) then
  361.     begin
  362.       result := 0; // Windows Vista and prior
  363.       Exit;
  364.     end;
  365.     result := fGetThreadErrorMode();
  366.   finally
  367.     FreeLibrary(dllHandle);
  368.   end;
  369. end;
  370.  
  371. // function SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall; external kernel32 name 'SetThreadErrorMode';
  372. function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
  373. type
  374.   TFuncSetThreadErrorMode = function(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall;
  375. var
  376.   dllHandle: Cardinal;
  377.   fSetThreadErrorMode: TFuncSetThreadErrorMode;
  378. begin
  379.   dllHandle := LoadLibrary(kernel32);
  380.   if dllHandle = 0 then
  381.   begin
  382.     result := FALSE;
  383.     if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
  384.     Exit;
  385.   end;
  386.   try
  387.     @fSetThreadErrorMode := GetProcAddress(dllHandle, 'SetThreadErrorMode');
  388.     if not Assigned(fSetThreadErrorMode) then
  389.     begin
  390.       result := FALSE; // Windows Vista and prior
  391.       if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
  392.       Exit;
  393.     end;
  394.     result := fSetThreadErrorMode(dwNewMode, lpOldMode);
  395.   finally
  396.     FreeLibrary(dllHandle);
  397.   end;
  398. end;
  399.  
  400. function IndexOf_CS(aStrings: TStrings; aToken: String): Integer;
  401. var
  402.   i: Integer;
  403. begin
  404.   Result := -1;
  405.   for i := 0 to aStrings.Count-1 do
  406.   begin
  407.     if aStrings[i] = aToken then
  408.     begin
  409.       Result := i;
  410.       Break;
  411.     end;
  412.   end;
  413. end;
  414.  
  415. function MergeString(ary: TArrayOfString; glue: string): string;
  416. var
  417.   i: integer;
  418. begin
  419.   result := '';
  420.   for i := Low(ary) to High(ary) do
  421.   begin
  422.     if result <> '' then result := result + glue;
  423.     result := result + ary[i];
  424.   end;
  425. end;
  426.  
  427. end.
  428.