Subversion Repositories userdetect2

Rev

Rev 82 | Rev 86 | 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 BetterInterpreteBool(str: string): boolean;
  33. function GetOwnCmdName: string;
  34. function ExpandEnvStr(const szInput: string): string;
  35. procedure UD2_RunCMD(cmd: TUD2Command);
  36. function SplitIconString(IconString: string): TIconFileIdx;
  37. // function GetHTML(AUrl: string): string;
  38. procedure VTS_CheckUpdates(VTSID, CurVer: string);
  39. function FormatOSError(ec: DWORD): string;
  40. function CheckBoolParam(idx: integer; name: string): boolean;
  41. function IndexOf_CS(aStrings: TStrings; aToken: String): Integer;
  42. function UD2_GetThreadErrorMode: DWORD;
  43. function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
  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(cmd: TUD2Command);
  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.   cmdLine: string;
  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(cmd.executable);
  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.   ZeroMemory(@sei, SizeOf(sei));
  260.  
  261.   if cmd.runAsAdmin then
  262.   begin
  263.     sei.lpVerb := 'runas';
  264.   end;
  265.  
  266.   if cmd.runInOwnDirectory then
  267.   begin
  268.     cmdFile := ExtractFileName(cmdLine);
  269.     cmdDir  := ExtractFilePath(cmdLine);
  270.   end
  271.   else
  272.   begin
  273.     cmdFile := cmdLine;
  274.     cmdDir := '';
  275.   end;
  276.  
  277.   sei.cbSize       := SizeOf(sei);
  278.   sei.lpFile       := PChar(cmdFile);
  279.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  280.   sei.fMask        := SEE_MASK_FLAG_NO_UI;
  281.   {$ENDIF}
  282.   if cmdArgs <> '' then sei.lpParameters := PChar(cmdArgs);
  283.   if cmdDir  <> '' then sei.lpDirectory  := PChar(cmdDir);
  284.   sei.nShow        := cmd.windowMode;
  285.   if ShellExecuteEx(@sei) then Exit;
  286.   {$IFNDEF PREFER_SHELLEXECUTEEX_MESSAGES}
  287.   if not CheckLastOSCall(false) then ExitCode := EXITCODE_RUN_FAILURE;
  288.   {$ENDIF}
  289. end;
  290.  
  291. function GetHTML(AUrl: string): string;
  292. // http://www.delphipraxis.net/post43515.html
  293. // Modificated by ViaThinkSoft
  294. var
  295.   databuffer : array[0..4095] of char;
  296.   ResStr : string;
  297.   hSession, hfile: hInternet;
  298.   dwindex,dwcodelen,dwread,dwNumber: cardinal;
  299.   dwcode : array[1..20] of char;
  300.   res    : pchar;
  301.   Str    : pchar;
  302. begin
  303.   ResStr := '';
  304.   if system.pos('http://',lowercase(AUrl)) = 0 then
  305.   begin
  306.      AUrl:='http://'+AUrl;
  307.   end;
  308.  
  309.   // [ViaThinkSoft] Added
  310.   Application.ProcessMessages;
  311.  
  312.   hSession:=InternetOpen('InetURL:/1.0',
  313.                          INTERNET_OPEN_TYPE_PRECONFIG,
  314.                          nil,
  315.                          nil,
  316.                          0);
  317.   if assigned(hsession) then
  318.   begin
  319.     // [ViaThinkSoft] Added
  320.     Application.ProcessMessages;
  321.  
  322.     hfile := InternetOpenUrl(hsession,
  323.                              pchar(AUrl),
  324.                              nil,
  325.                              0,
  326.                              INTERNET_FLAG_RELOAD,
  327.                              0);
  328.     dwIndex   := 0;
  329.     dwCodeLen := 10;
  330.  
  331.     // [ViaThinkSoft] Added
  332.     Application.ProcessMessages;
  333.  
  334.     HttpQueryInfo(hfile,
  335.                   HTTP_QUERY_STATUS_CODE,
  336.                   @dwcode,
  337.                   dwcodeLen,
  338.                   dwIndex);
  339.     res := pchar(@dwcode);
  340.     dwNumber := sizeof(databuffer)-1;
  341.     if (res ='200') or (res ='302') then
  342.     begin
  343.       while (InternetReadfile(hfile,
  344.                               @databuffer,
  345.                               dwNumber,
  346.                               DwRead)) do
  347.       begin
  348.  
  349.         // [ViaThinkSoft] Added
  350.         Application.ProcessMessages;
  351.  
  352.         if dwRead =0 then
  353.           break;
  354.         databuffer[dwread]:=#0;
  355.         Str := pchar(@databuffer);
  356.         resStr := resStr + Str;
  357.       end;
  358.     end
  359.     else
  360.       ResStr := 'Status:'+res;
  361.     if assigned(hfile) then
  362.       InternetCloseHandle(hfile);
  363.   end;
  364.  
  365.   // Hinzugefügt
  366.   Application.ProcessMessages;
  367.  
  368.   InternetCloseHandle(hsession);
  369.   Result := resStr;
  370. end;
  371.  
  372. procedure VTS_CheckUpdates(VTSID, CurVer: string);
  373. resourcestring
  374.   (*
  375.   LNG_DOWNLOAD_ERR = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server temporär offline.';
  376.   LNG_NEW_VERSION = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?';
  377.   LNG_NO_UPDATE = 'Es ist keine neue Programmversion vorhanden.';
  378.   *)
  379.   LNG_DOWNLOAD_ERR = 'An error occurred while searching for updates. Please check your internet connection and firewall.';
  380.   LNG_NEW_VERSION = 'A new version is available. Do you want to download it now?';
  381.   LNG_NO_UPDATE = 'You already have the newest program version.';
  382. var
  383.   status: string;
  384. begin
  385.   status := GetHTML('http://www.viathinksoft.de/update/?id='+VTSID);
  386.   if Copy(status, 0, 7) = 'Status:' then
  387.   begin
  388.     MessageDlg(LNG_DOWNLOAD_ERR, mtError, [mbOK], 0);
  389.   end
  390.   else
  391.   begin
  392.     if status <> CurVer then
  393.     begin
  394.       if MessageDlg(LNG_NEW_VERSION, mtConfirmation, mbYesNoCancel, 0) = ID_YES then
  395.       begin
  396.         shellexecute(application.handle, 'open', pchar('http://www.viathinksoft.de/update/?id=@'+VTSID), '', '', SW_Normal);
  397.       end;
  398.     end
  399.     else
  400.     begin
  401.       MessageDlg(LNG_NO_UPDATE, mtInformation, [mbOk], 0);
  402.     end;
  403.   end;
  404. end;
  405.  
  406. function CheckBoolParam(idx: integer; name: string): boolean;
  407. begin
  408.   Result := ('/'+LowerCase(name) = LowerCase(ParamStr(idx))) or
  409.             ('-'+LowerCase(name) = LowerCase(ParamStr(idx)));
  410. end;
  411.  
  412. // function GetThreadErrorMode: DWORD; stdcall; external kernel32 name 'GetThreadErrorMode';
  413. function UD2_GetThreadErrorMode: DWORD;
  414. type
  415.   TFuncGetThreadErrorMode = function: DWORD; stdcall;
  416. var
  417.   dllHandle: Cardinal;
  418.   fGetThreadErrorMode: TFuncGetThreadErrorMode;
  419. begin
  420.   dllHandle := LoadLibrary(kernel32);
  421.   if dllHandle = 0 then
  422.   begin
  423.     result := 0;
  424.     Exit;
  425.   end;
  426.   try
  427.     @fGetThreadErrorMode := GetProcAddress(dllHandle, 'GetThreadErrorMode');
  428.     if not Assigned(fGetThreadErrorMode) then
  429.     begin
  430.       result := 0; // Windows Vista and prior
  431.       Exit;
  432.     end;
  433.     result := fGetThreadErrorMode();
  434.   finally
  435.     FreeLibrary(dllHandle);
  436.   end;
  437. end;
  438.  
  439. // function SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall; external kernel32 name 'SetThreadErrorMode';
  440. function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
  441. type
  442.   TFuncSetThreadErrorMode = function(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall;
  443. var
  444.   dllHandle: Cardinal;
  445.   fSetThreadErrorMode: TFuncSetThreadErrorMode;
  446. begin
  447.   dllHandle := LoadLibrary(kernel32);
  448.   if dllHandle = 0 then
  449.   begin
  450.     result := FALSE;
  451.     if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
  452.     Exit;
  453.   end;
  454.   try
  455.     @fSetThreadErrorMode := GetProcAddress(dllHandle, 'SetThreadErrorMode');
  456.     if not Assigned(fSetThreadErrorMode) then
  457.     begin
  458.       result := FALSE; // Windows Vista and prior
  459.       if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
  460.       Exit;
  461.     end;
  462.     result := fSetThreadErrorMode(dwNewMode, lpOldMode);
  463.   finally
  464.     FreeLibrary(dllHandle);
  465.   end;
  466. end;
  467.  
  468. function IndexOf_CS(aStrings: TStrings; aToken: String): Integer;
  469. var
  470.   i: Integer;
  471. begin
  472.   Result := -1;
  473.   for i := 0 to aStrings.Count-1 do
  474.   begin
  475.     if aStrings[i] = aToken then
  476.     begin
  477.       Result := i;
  478.       Break;
  479.     end;
  480.   end;
  481. end;
  482.  
  483. end.
  484.