Subversion Repositories userdetect2

Rev

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