Subversion Repositories userdetect2

Rev

Rev 86 | 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.   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(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(AUrl: string): string;
  293. // http://www.delphipraxis.net/post43515.html
  294. // Modificated by ViaThinkSoft
  295. var
  296.   databuffer : array[0..4095] of char;
  297.   ResStr : string;
  298.   hSession, hfile: hInternet;
  299.   dwindex,dwcodelen,dwread,dwNumber: cardinal;
  300.   dwcode : array[1..20] of char;
  301.   res    : pchar;
  302.   Str    : pchar;
  303. begin
  304.   ResStr := '';
  305.   if system.pos('http://',lowercase(AUrl)) = 0 then
  306.   begin
  307.      AUrl:='http://'+AUrl;
  308.   end;
  309.  
  310.   // [ViaThinkSoft] Added
  311.   Application.ProcessMessages;
  312.  
  313.   hSession:=InternetOpen('InetURL:/1.0',
  314.                          INTERNET_OPEN_TYPE_PRECONFIG,
  315.                          nil,
  316.                          nil,
  317.                          0);
  318.   if assigned(hsession) then
  319.   begin
  320.     // [ViaThinkSoft] Added
  321.     Application.ProcessMessages;
  322.  
  323.     hfile := InternetOpenUrl(hsession,
  324.                              pchar(AUrl),
  325.                              nil,
  326.                              0,
  327.                              INTERNET_FLAG_RELOAD,
  328.                              0);
  329.     dwIndex   := 0;
  330.     dwCodeLen := 10;
  331.  
  332.     // [ViaThinkSoft] Added
  333.     Application.ProcessMessages;
  334.  
  335.     HttpQueryInfo(hfile,
  336.                   HTTP_QUERY_STATUS_CODE,
  337.                   @dwcode,
  338.                   dwcodeLen,
  339.                   dwIndex);
  340.     res := pchar(@dwcode);
  341.     dwNumber := sizeof(databuffer)-1;
  342.     if (res ='200') or (res ='302') then
  343.     begin
  344.       while (InternetReadfile(hfile,
  345.                               @databuffer,
  346.                               dwNumber,
  347.                               DwRead)) do
  348.       begin
  349.  
  350.         // [ViaThinkSoft] Added
  351.         Application.ProcessMessages;
  352.  
  353.         if dwRead =0 then
  354.           break;
  355.         databuffer[dwread]:=#0;
  356.         Str := pchar(@databuffer);
  357.         resStr := resStr + Str;
  358.       end;
  359.     end
  360.     else
  361.       ResStr := 'Status:'+res;
  362.     if assigned(hfile) then
  363.       InternetCloseHandle(hfile);
  364.   end;
  365.  
  366.   // Hinzugefügt
  367.   Application.ProcessMessages;
  368.  
  369.   InternetCloseHandle(hsession);
  370.   Result := resStr;
  371. end;
  372.  
  373. procedure VTS_CheckUpdates(VTSID, CurVer: string);
  374. resourcestring
  375.   (*
  376.   LNG_DOWNLOAD_ERR = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server temporär offline.';
  377.   LNG_NEW_VERSION = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?';
  378.   LNG_NO_UPDATE = 'Es ist keine neue Programmversion vorhanden.';
  379.   *)
  380.   LNG_DOWNLOAD_ERR = 'An error occurred while searching for updates. Please check your internet connection and firewall.';
  381.   LNG_NEW_VERSION = 'A new version is available. Do you want to download it now?';
  382.   LNG_NO_UPDATE = 'You already have the newest program version.';
  383. var
  384.   status: string;
  385. begin
  386.   status := GetHTML('http://www.viathinksoft.de/update/?id='+VTSID);
  387.   if Copy(status, 0, 7) = 'Status:' then
  388.   begin
  389.     MessageDlg(LNG_DOWNLOAD_ERR, mtError, [mbOK], 0);
  390.   end
  391.   else
  392.   begin
  393.     if status <> CurVer then
  394.     begin
  395.       if MessageDlg(LNG_NEW_VERSION, mtConfirmation, mbYesNoCancel, 0) = ID_YES then
  396.       begin
  397.         shellexecute(application.handle, 'open', pchar('http://www.viathinksoft.de/update/?id=@'+VTSID), '', '', SW_Normal);
  398.       end;
  399.     end
  400.     else
  401.     begin
  402.       MessageDlg(LNG_NO_UPDATE, mtInformation, [mbOk], 0);
  403.     end;
  404.   end;
  405. end;
  406.  
  407. function CheckBoolParam(idx: integer; name: string): boolean;
  408. begin
  409.   Result := ('/'+LowerCase(name) = LowerCase(ParamStr(idx))) or
  410.             ('-'+LowerCase(name) = LowerCase(ParamStr(idx)));
  411. end;
  412.  
  413. // function GetThreadErrorMode: DWORD; stdcall; external kernel32 name 'GetThreadErrorMode';
  414. function UD2_GetThreadErrorMode: DWORD;
  415. type
  416.   TFuncGetThreadErrorMode = function: DWORD; stdcall;
  417. var
  418.   dllHandle: Cardinal;
  419.   fGetThreadErrorMode: TFuncGetThreadErrorMode;
  420. begin
  421.   dllHandle := LoadLibrary(kernel32);
  422.   if dllHandle = 0 then
  423.   begin
  424.     result := 0;
  425.     Exit;
  426.   end;
  427.   try
  428.     @fGetThreadErrorMode := GetProcAddress(dllHandle, 'GetThreadErrorMode');
  429.     if not Assigned(fGetThreadErrorMode) then
  430.     begin
  431.       result := 0; // Windows Vista and prior
  432.       Exit;
  433.     end;
  434.     result := fGetThreadErrorMode();
  435.   finally
  436.     FreeLibrary(dllHandle);
  437.   end;
  438. end;
  439.  
  440. // function SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall; external kernel32 name 'SetThreadErrorMode';
  441. function UD2_SetThreadErrorMode(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL;
  442. type
  443.   TFuncSetThreadErrorMode = function(dwNewMode: DWORD; lpOldMode: LPDWORD): BOOL; stdcall;
  444. var
  445.   dllHandle: Cardinal;
  446.   fSetThreadErrorMode: TFuncSetThreadErrorMode;
  447. begin
  448.   dllHandle := LoadLibrary(kernel32);
  449.   if dllHandle = 0 then
  450.   begin
  451.     result := FALSE;
  452.     if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
  453.     Exit;
  454.   end;
  455.   try
  456.     @fSetThreadErrorMode := GetProcAddress(dllHandle, 'SetThreadErrorMode');
  457.     if not Assigned(fSetThreadErrorMode) then
  458.     begin
  459.       result := FALSE; // Windows Vista and prior
  460.       if Assigned(lpOldMode) then lpOldMode^ := UD2_GetThreadErrorMode;
  461.       Exit;
  462.     end;
  463.     result := fSetThreadErrorMode(dwNewMode, lpOldMode);
  464.   finally
  465.     FreeLibrary(dllHandle);
  466.   end;
  467. end;
  468.  
  469. function IndexOf_CS(aStrings: TStrings; aToken: String): Integer;
  470. var
  471.   i: Integer;
  472. begin
  473.   Result := -1;
  474.   for i := 0 to aStrings.Count-1 do
  475.   begin
  476.     if aStrings[i] = aToken then
  477.     begin
  478.       Result := i;
  479.       Break;
  480.     end;
  481.   end;
  482. end;
  483.  
  484. function MergeString(ary: TArrayOfString; glue: string): string;
  485. var
  486.   i: integer;
  487. begin
  488.   result := '';
  489.   for i := Low(ary) to High(ary) do
  490.   begin
  491.     if result <> '' then result := result + glue;
  492.     result := result + ary[i];
  493.   end;
  494. end;
  495.  
  496. end.
  497.