//*************************************************************************
//                                                                        *
//                        IEDownload Tools                                *
//                       For Delphi                                       *
//                                                                        *
//                     Freeware Unit                                      *
//                            by                                          *
//  Eran Bodankin -bsalsa(bsalsa@gmail.com)                              *
//                                                                        *
//                                                                        *
//  Updated versions:                                                     *
//               http://www.bsalsa.com                                    *
//*************************************************************************
{LICENSE:
THIS SOFTWARE IS PROVIDED TO YOU "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED INCLUDING BUT NOT LIMITED TO THE APPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
YOU ASSUME THE ENTIRE RISK AS TO THE ACCURACY AND THE USE OF THE SOFTWARE
AND ALL OTHER RISK ARISING OUT OF THE USE OR PERFORMANCE OF THIS SOFTWARE
AND DOCUMENTATION. BSALSA PRODUCTIONS DOES NOT WARRANT THAT THE SOFTWARE IS ERROR-FREE
OR WILL OPERATE WITHOUT INTERRUPTION. THE SOFTWARE IS NOT DESIGNED, INTENDED
OR LICENSED FOR USE IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE CONTROLS,
INCLUDING WITHOUT LIMITATION, THE DESIGN, CONSTRUCTION, MAINTENANCE OR
OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS,
AIR TRAFFIC CONTROL, AND LIFE SUPPORT OR WEAPONS SYSTEMS. BSALSA PRODUCTIONS SPECIFICALLY
DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR SUCH PURPOSE.

You may use, change or modify the component under 4 conditions:
1. In your website, add a link to "http://www.bsalsa.com"
2. In your application, add credits to "Embedded Web Browser"
3. Mail me  (bsalsa@gmail.com) any code change in the unit
   for the benefit of the other users.
4. Please, consider donation in our web site!
{*******************************************************************************}

unit IEDownloadTools;

interface

uses
  Windows, EWB.IeConst, SysUtils, ActiveX;

function ResponseCodeToStr(dwResponse: Integer): string;
function FormatSize(Byte: Double): string;
function FormatTickToTime(TickCount: Cardinal): string;
function IsValidURL(const URL: Widestring): Boolean;
function WideStringToLPOLESTR(const Source: Widestring): POleStr;
function WideStringToWideChar(const inString: WideString): PWideChar;
function CodeInstallProblemToStr(ulStatusCode: Integer): string;
function DataAvalibleToStr(Code: Integer): string;
function SecToStr(const Seconds: LongInt): string;
procedure FreeWideChar(var inString: PWideChar);
function StrToCase(StringOf: string; CasesList: array of string): Integer;
function QueryRemoteFileInfo(const Url: string; dwInfoFlag: Integer): string;
function GetInfoByMimeType(const ContentType: string): boolean;
function StrContain(SubString: string; Str: string): boolean;
function IncludeTrailingPathDelimiter(const S: string): string;
function CharReplace(const Source: string; OldChar, NewChar: Char): string;


implementation

uses
  WinInet, UrlMon, Registry;

// Some constatants are missing in previous delphi versions.
// That's why they are declared here.
const
{$EXTERNALSYM BINDSTATUS_COOKIE_SENT}
  BINDSTATUS_COOKIE_SENT = BINDSTATUS_ACCEPTRANGES + 1;
{$EXTERNALSYM BINDSTATUS_COMPACT_POLICY_RECEIVED}
  BINDSTATUS_COMPACT_POLICY_RECEIVED = BINDSTATUS_COOKIE_SENT + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_SUPPRESSED}
  BINDSTATUS_COOKIE_SUPPRESSED = BINDSTATUS_COMPACT_POLICY_RECEIVED + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_STATE_UNKNOWN}
  BINDSTATUS_COOKIE_STATE_UNKNOWN = BINDSTATUS_COOKIE_SUPPRESSED + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_STATE_ACCEPT}
  BINDSTATUS_COOKIE_STATE_ACCEPT = BINDSTATUS_COOKIE_STATE_UNKNOWN + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_STATE_REJECT}
  BINDSTATUS_COOKIE_STATE_REJECT = BINDSTATUS_COOKIE_STATE_ACCEPT + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_STATE_PROMPT}
  BINDSTATUS_COOKIE_STATE_PROMPT = BINDSTATUS_COOKIE_STATE_REJECT + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_STATE_LEASH}
  BINDSTATUS_COOKIE_STATE_LEASH = BINDSTATUS_COOKIE_STATE_PROMPT + 1;
{$EXTERNALSYM BINDSTATUS_COOKIE_STATE_DOWNGRADE}
  BINDSTATUS_COOKIE_STATE_DOWNGRADE = BINDSTATUS_COOKIE_STATE_LEASH + 1;
{$EXTERNALSYM BINDSTATUS_POLICY_HREF}
  BINDSTATUS_POLICY_HREF = BINDSTATUS_COOKIE_STATE_DOWNGRADE + 1;
{$EXTERNALSYM BINDSTATUS_P3P_HEADER}
  BINDSTATUS_P3P_HEADER = BINDSTATUS_POLICY_HREF + 1;
{$EXTERNALSYM BINDSTATUS_SESSION_COOKIE_RECEIVED}
  BINDSTATUS_SESSION_COOKIE_RECEIVED = BINDSTATUS_P3P_HEADER + 1;
{$EXTERNALSYM BINDSTATUS_PERSISTENT_COOKIE_RECEIVED}
  BINDSTATUS_PERSISTENT_COOKIE_RECEIVED = BINDSTATUS_SESSION_COOKIE_RECEIVED +
    1;

function CodeInstallProblemToStr(ulStatusCode: Integer): string;
begin
  case ulStatusCode of
    CIP_DISK_FULL: Result := 'Destination can accept no more data.';
    CIP_ACCESS_DENIED: Result := 'Permissions problem.';
    CIP_NEWER_VERSION_EXISTS: Result :=
      'Destination contains a newer version than the source.';
    CIP_OLDER_VERSION_EXISTS: Result :=
      'Destination contains an older version than the source.';
    CIP_NAME_CONFLICT: Result :=
      'Destination does not allow the naming convention used by the source.';
    CIP_TRUST_VERIFICATION_COMPONENT_MISSING: Result :=
      'Destination cannot verify the source.';
    CIP_EXE_SELF_REGISTERATION_TIMEOUT: Result := 'Timeout has occurred. ';
    CIP_UNSAFE_TO_ABORT: Result :=
      'Installation or download should not be aborted.';
    CIP_NEED_REBOOT: Result := 'Destination machine requires rebooting.';
    CIP_NEED_REBOOT_UI_PERMISSION: Result :=
      'Destination machine should reboot without user interface (UI).';
  else
    Result := ResponseCodeToStr(ulStatusCode)
  end;
end;

function DataAvalibleToStr(Code: Integer): string;
begin
  case Code of
    BSCF_FIRSTDATANOTIFICATION: Result :=
      'Identifed the first call to OnDataAvailable.';
    BSCF_INTERMEDIATEDATANOTIFICATION: Result :=
      'Identifed the intermediate call to OnDataAvailable.';
    BSCF_LASTDATANOTIFICATION: Result :=
      'Identifed the last call to OnDataAvailable.';
    BSCF_DATAFULLYAVAILABLE: Result :=
      'All of the requested data is available.';
    BSCF_AVAILABLEDATASIZEUNKNOWN: Result :=
      'Size of the data available is unknown.';
    //  BSCF_SKIPDRAINDATAFORFILEURLS: Result := 'Bypass cache downloads.';
  else
    Result := ResponseCodeToStr(Code)
  end;
end;

function ResponseCodeToStr(dwResponse: Integer): string;
begin
  case dwResponse of
    100: Result := 'Continue';
    101: Result := 'Switching Protocols';
    200: Result := 'OK';
    201: Result := 'Created';
    202: Result := 'Accepted';
    203: Result := 'Non-Authoritative Information';
    204: Result := 'No Content';
    205: Result := 'Reset Content';
    206: Result := 'Partial Content';
    300: Result := 'Multiple Choices';
    301: Result := 'Moved Permanently';
    302: Result := 'Moved Temporarily';
    303: Result := 'See Other';
    304: Result := 'Not Modified';
    305: Result := 'Use Proxy';
    400: Result := 'Bad Request';
    401: Result := 'Unauthorized';
    402: Result := 'Payment Required';
    403: Result := 'Forbidden';
    404: Result := 'Not Found';
    405: Result := 'Method Not Allowed';
    406: Result := 'None Acceptable';
    407: Result := 'Proxy Authentication Required';
    408: Result := 'Request Timeout';
    409: Result := 'Conflict';
    410: Result := 'Gone';
    411: Result := 'Length Required';
    412: Result := 'Precondition Failed';
    413: Result := 'Request Entity Too Large';
    414: Result := 'Request-URI Too Long';
    415: Result := 'Unsupported Media Type';
    416: Result := 'Requested Range Not Satisfiable';
    417: Result := 'Expectation Failed';
    500: Result := 'Internal Server Error';
    501: Result := 'Not Implemented';
    502: Result := 'Bad Gateway';
    503: Result := 'Service Unavailable';
    504: Result := 'Gateway Timeout';
    505: Result := 'HTTP Version Not Supported';
    BINDSTATUS_FINDINGRESOURCE: Result := 'Finding resource...';
    BINDSTATUS_CONNECTING: Result := 'Connecting...';
    BINDSTATUS_REDIRECTING: Result := 'Server redirecting client...';
    BINDSTATUS_BEGINDOWNLOADDATA: Result := 'Beginning to download data...';
    BINDSTATUS_DOWNLOADINGDATA: Result := 'Downloading data...';
    BINDSTATUS_ENDDOWNLOADDATA: Result := 'Ending data download...';
    BINDSTATUS_BEGINDOWNLOADCOMPONENTS: Result :=
      'Beginning to download components...';
    BINDSTATUS_INSTALLINGCOMPONENTS: Result := 'Installing components...';
    BINDSTATUS_ENDDOWNLOADCOMPONENTS: Result := 'Ending component download...';
    BINDSTATUS_USINGCACHEDCOPY: Result := 'Using cached copy...';
    BINDSTATUS_SENDINGREQUEST: Result := 'Sending request...';
    BINDSTATUS_CLASSIDAVAILABLE: Result := 'CLSID available...'; //***
    BINDSTATUS_MIMETYPEAVAILABLE: Result := 'MIME type available...';
    BINDSTATUS_CACHEFILENAMEAVAILABLE: Result := 'Cache file name available...';
    BINDSTATUS_BEGINSYNCOPERATION: Result :=
      'Synchronous operation has started...';
    BINDSTATUS_ENDSYNCOPERATION: Result := 'Ending synchronous operation...';
    BINDSTATUS_BEGINUPLOADDATA: Result := 'Beginning to upload data...';
    BINDSTATUS_UPLOADINGDATA: Result := 'Uploading data...';
    BINDSTATUS_ENDUPLOADDATA: Result := 'Ending data upload...';
    BINDSTATUS_PROTOCOLCLASSID: Result :=
      'Protocol handler CLSID is available...';
    BINDSTATUS_ENCODING: Result := 'Encoding data...';
    BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE: Result :=
      'Verified MIME type is available...';
    BINDSTATUS_CLASSINSTALLLOCATION: Result :=
      'Class install location is available...';
    BINDSTATUS_DECODING: Result := 'Decoding data...';
    BINDSTATUS_LOADINGMIMEHANDLER: Result :=
      'Pluggable MIME handler is being loaded...';
    BINDSTATUS_CONTENTDISPOSITIONATTACH: Result :=
      'Content-Disposition resource is an attachment...';
    BINDSTATUS_FILTERREPORTMIMETYPE: Result :=
      'New MIME type available for resource...';
    BINDSTATUS_CLSIDCANINSTANTIATE: Result :=
      'CLSID to return on BindToObject...';
    BINDSTATUS_IUNKNOWNAVAILABLE: Result :=
      'IUnknown interface has been released...';
    BINDSTATUS_DIRECTBIND: Result :=
      'Connected directly to the pluggable protocol handler...';
    BINDSTATUS_RAWMIMETYPE: Result := 'Raw MIME type...';
    BINDSTATUS_PROXYDETECTING: Result := 'Proxy server has been detected...';
    BINDSTATUS_ACCEPTRANGES: Result := 'Valid types of range requests...';
    BINDSTATUS_COOKIE_SENT: Result := 'A cookie was sent';
    BINDSTATUS_COMPACT_POLICY_RECEIVED: Result :=
      'A Platform for Privacy Preferences was received.';
    BINDSTATUS_COOKIE_SUPPRESSED: Result :=
      'A cookie was suppressed from being sent to the web server.';
    BINDSTATUS_COOKIE_STATE_UNKNOWN: Result :=
      'A a cookie has been initialized. ';
    BINDSTATUS_COOKIE_STATE_ACCEPT: Result :=
      'A cookie sent by the server was accepted.';
    BINDSTATUS_COOKIE_STATE_REJECT: Result :=
      'A cookie sent by the server was rejected.';
    BINDSTATUS_COOKIE_STATE_PROMPT: Result :=
      'User settings require a prompt before performing a cookie operation.';
    BINDSTATUS_COOKIE_STATE_LEASH: Result := 'A cookie is a leashed cookie.';
    BINDSTATUS_COOKIE_STATE_DOWNGRADE: Result :=
      'A cookie is a downgraded cookie.';
    BINDSTATUS_POLICY_HREF: Result :=
      'HTTP headers contain a link to the full privacy policy';
    BINDSTATUS_P3P_HEADER: Result :=
      'HTTP response from the server contains the P3P privacy header';
    BINDSTATUS_SESSION_COOKIE_RECEIVED: Result :=
      'A session cookie was received.';
    BINDSTATUS_PERSISTENT_COOKIE_RECEIVED: Result :=
      'A persistent cookie was received. ';
    BINDSTATUS_SESSION_COOKIES_ALLOWED: Result :=
      'The session cookies are allowed.';
    BINDSTATUS_CACHECONTROL: Result :=
      'A response from the server was written to memory only.'; //IE7
    BINDSTATUS_CONTENTDISPOSITIONFILENAME: Result :=
      'The Content-Disposition header contains a file name.';
    BINDSTATUS_MIMETEXTPLAINMISMATCH: Result :=
      'The reported Content-Type of the file does not match the content. ';
    BINDSTATUS_PUBLISHERAVAILABLE: Result :=
      'The publisher name is being downloaded and is available.';
    BINDSTATUS_DISPLAYNAMEAVAILABLE: Result :=
      'The display name being downloaded and is available.';
    BINDSTATUS_SSLUX_NAVBLOCKED: Result := 'A problem with the SSL certificate.';
    BINDSTATUS_SERVER_MIMETYPEAVAILABLE: Result :=
      'Server''s authoritative MIME type reported'; //IE8  - Attention: This documentation is preliminary and is subject to change
    BINDSTATUS_SNIFFED_CLASSIDAVAILABLE: Result :=
      'Class identifier (CLSID) generated from authoritative Content-Type HTTP response header'; //IE8  - Attention: This documentation is preliminary and is subject to change
    BINDSTATUS_64BIT_PROGRESS: Result :=
      'Download progress values are above the maximum 32-bit file size limit.'; //IE8  - Attention: This documentation is preliminary and is subject to change
    // E_PENDING: Result := 'The data necessary to complete this operation is not yet available';
    E_UNEXPECTED: Result := 'Catastrophic failure';
    E_FAIL: Result := 'Failed';
    E_NOINTERFACE: Result := 'No such interface supported';
    E_NOTIMPL: Result := 'Not implemented';
    E_ABORT: Result := 'Operation Aborted.';
    E_ACCESSDENIED: Result := 'Access Denied.';
    E_INVALIDARG: Result := 'One or more parameters are invalid.';
    E_OUTOFMEMORY: Result := 'There was insufficient memory ...';
    ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION: Result :=
      'The redirection requires user confirmation.';
    ERROR_INTERNET_CHG_POST_IS_NON_SECURE: Result :=
      'The application is posting and attempting to change multiple lines of text on a server that is not secure.';
    ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED: Result :=
      'The server is requesting client authentication.';
    ERROR_INTERNET_CLIENT_AUTH_NOT_SETUP: Result :=
      'Client authorization is not set up on this computer.';
    ERROR_INTERNET_FORTEZZA_LOGIN_NEEDED: Result :=
      'The requested resource requires Fortezza authentication.';
    ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR: Result :=
      'The application is moving from a non-SSL to an SSL connection because of a redirect.';
    ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR: Result :=
      'The application is moving from an SSL to an non-SSL connection because of a redirect.';
    ERROR_INTERNET_INVALID_CA: Result :=
      'The function is unfamiliar with the Certificate Authority that generated the server''s certificate.';
    ERROR_INTERNET_MIXED_SECURITY: Result :=
      'The content is not entirely secure. Some of the content being viewed may have come from unsecured servers.';
    ERROR_INTERNET_POST_IS_NON_SECURE: Result :=
      'The application is posting data to a server that is not secure.';
    ERROR_INTERNET_SEC_CERT_CN_INVALID: Result :=
      'SSL certificate common name (host name field) is incorrect???for example, if you entered www.server.com and the common name on the certificate says www.different.com.';
    ERROR_INTERNET_SEC_CERT_DATE_INVALID: Result :=
      'SSL certificate date that was received from the server is bad. The certificate is expired.';
    ERROR_INTERNET_SEC_CERT_REVOKED: Result := 'SSL certificate was revoked.';
    INET_E_AUTHENTICATION_REQUIRED: Result :=
      'Authentication is needed to access the object.';
    INET_E_CANNOT_CONNECT: Result :=
      'The attempt to connect to the Internet has failed.';
    INET_E_CANNOT_INSTANTIATE_OBJECT: Result := 'CoCreateInstance failed.';
    INET_E_CANNOT_LOAD_DATA: Result := 'The object could not be loaded.';
    INET_E_CANNOT_LOCK_REQUEST: Result :=
      'The requested resource could not be locked.';
    INET_E_CANNOT_REPLACE_SFP_FILE: Result :=
      'Cannot replace a protected System File Protection (SFP) file.';
    INET_E_CODE_DOWNLOAD_DECLINED: Result :=
      'Permission to download is declined.';
    INET_E_CONNECTION_TIMEOUT: Result :=
      'The Internet connection has timed out.';
    INET_E_DATA_NOT_AVAILABLE: Result :=
      'An Internet connection was established, but the data cannot be retrieved.';
    INET_E_DEFAULT_ACTION: Result := 'Use the default action.';
    INET_E_DOWNLOAD_FAILURE: Result :=
      'The download has failed (the connection was interrupted).';
    INET_E_INVALID_REQUEST: Result := 'The request was invalid.';
    INET_E_INVALID_URL: Result := 'The URL could not be parsed.';
    INET_E_NO_SESSION: Result := 'No Internet session was established.';
    INET_E_NO_VALID_MEDIA: Result :=
      'The object is not in one of the acceptable MIME types.';
    INET_E_OBJECT_NOT_FOUND: Result := 'The object was not found.';
    INET_E_QUERYOPTION_UNKNOWN: Result := 'The requested option is unknown.';
    INET_E_REDIRECT_TO_DIR: Result :=
      'The request is being redirected to a directory.';
    INET_E_REDIRECTING: Result := 'The request is being redirected.';
    INET_E_RESOURCE_NOT_FOUND: Result := 'The server or proxy was not found.';
    INET_E_RESULT_DISPATCHED: Result := 'Result is dispatched.';
    INET_E_SECURITY_PROBLEM: Result := 'A security problem was encountered.';
    INET_E_TERMINATED_BIND: Result := 'Binding is terminated.';
    INET_E_UNKNOWN_PROTOCOL: Result :=
      'The protocol is not known and no pluggable protocols have been entered that match.';
    //INET_E_USE_DEFAULT_PROTOCOLHANDLER: Result := 'Use the default protocol handler.';
    INET_E_USE_EXTEND_BINDING: Result :=
      'Re-issue request with extended Binding.';
    INET_E_USE_DEFAULT_SETTING: Result := 'Use the default settings.';
    MK_E_CANTOPENFILE: Result := 'Moniker cannot open file.';
    MK_E_CONNECTMANUALLY: Result :=
      'The operation was unable to connect to the storage, possibly because a network device could not be connected to. For more information, see IMoniker::BindToObject.';
    MK_E_ENUMERATION_FAILED: Result := 'Moniker could not be enumerated';
    MK_E_EXCEEDEDDEADLINE: Result :=
      'The operation could not be completed within the time limit specified by the bind context''s BIND_OPTS structure.';
    MK_E_INTERMEDIATEINTERFACENOTSUPPORTED: Result :=
      'An intermediate object was found but it did not support an interface required for an operation. For example, an item moniker returns this value if its container does not support the IOleItemContainer interface.';
    MK_E_INVALIDEXTENSION: Result := 'Bad extension for file.';
    MK_E_MUSTBOTHERUSER: Result :=
      'User input required for operation to succeed.';
    MK_E_NEEDGENERIC: Result := 'Moniker needs to be generic.';
    MK_E_NOINVERSE: Result := 'Moniker class has no inverse.';
    MK_E_NOOBJECT: Result := 'No object for moniker.';
    MK_E_NOPREFIX: Result := 'No common prefix.';
    MK_E_NOSTORAGE: Result :=
      'The object identified by the moniker does not have its own storage.';
    MK_E_NOTBINDABLE: Result := 'Moniker is not bindable.';
    MK_E_NOTBOUND: Result := 'Moniker is not bound.';
    MK_E_SYNTAX: Result :=
      'A moniker could not be created because of invalid URL syntax.';
    MK_E_UNAVAILABLE: Result := 'Operation unavailable.';
    S_OK: Result := 'The operation was successful';
    STG_E_ACCESSDENIED: Result := 'Unable to access the storage object.';
    MK_S_ASYNCHRONOUS: Result := 'Asynchronous mode.';
  else
    Result := 'Unknown Response: ' + IntToStr(dwResponse) + ' ' +
      SysErrorMessage(dwResponse)
  end;
end;

function WideStringToLPOLESTR(const Source: Widestring): POleStr;
var
  Len: Integer;
begin
  Len := Length(Source) * SizeOf(WideChar);
  Result := CoTaskMemAlloc(Len + 2);
  FillChar(Result^, Len + 2, 0);
  Move(Result^, PWideString(Source)^, Len);
end;

function SecToStr(const Seconds: LongInt): string;
var
  H: Integer;
  M: Integer;
  S: Integer;
begin
  h := (Seconds div 60) div 60 mod 60;
  m := (Seconds div 60) mod 60;
  s := Seconds mod 60;
  Result := Format('%2.2d:%2.2d:%2.2d', (.H, M, S.));
end;

function FormatTickToTime(TickCount: Cardinal): string;
var
  Hours, Minutes, Seconds, MSecs: Cardinal;
  S_HUR, S_MIN, S_SEC, S_MSC: string;
begin
  S_MSC := '000';
  S_SEC := '00';
  S_MIN := '00';
  S_HUR := '00';
  try
    if (TickCount > 0) then
    begin
      MSecs := TickCount mod MSecsPerSec;
      TickCount := TickCount div MSecsPerSec;
      S_MSC := Format('%.3d', [MSecs]);
      Seconds := TickCount mod SecsPerMin;
      TickCount := TickCount div SecsPerMin;
      S_SEC := Format('%.2d', [Seconds]);
      Minutes := TickCount mod MinsPerHour;
      TickCount := TickCount div MinsPerHour;
      S_MIN := Format('%.2d', [Minutes]);
      Hours := TickCount mod HoursPerDay;
      S_HUR := Format('%.2d', [Hours]);
    end;
  finally
    if S_HUR = '00' then
      Result := S_MIN + 'm :' + S_SEC + 's :' + S_MSC + 'ms';
    if (S_HUR = '00') and (S_MIN = '00') then
      Result := S_SEC + 's :' + S_MSC + 'ms';
    if S_HUR <> '00' then
      Result := S_HUR + 'h :' + S_MIN + 'm :' + S_SEC + 's :' + S_MSC + 'ms';
  end;
end;


function StrToCase(StringOf: string; CasesList: array of string): Integer;
var
  Idx: integer;
begin
  Result := -1;
  for Idx := 0 to Length(CasesList) - 1 do
  begin
    if CompareText(StringOf, CasesList[Idx]) = 0 then
    begin
      Result := Idx;
      Break;
    end;
  end;
end;

function FormatSize(Byte: Double): string;
begin
  if (Byte > 0) then
  begin
    if (Byte < 1024) then
      Result := Format('%.2n b', [Byte])
    else
      if (Byte > 1024) then
      begin
        Byte := (Byte / 1024);
        if (Byte < 1024) then
          Result := Format('%.2n Kb', [Byte])
        else
        begin
          Byte := (Byte / 1024);
          Result := Format('%.2n Mb', [Byte]);
        end;
      end;
  end;
end;

function IsValidURL(const URL: Widestring): Boolean;
begin
  Result := UrlMon.IsValidURL(nil, PWideChar(URL), 0) = S_OK;
end;


function WideStringToWideChar(const inString: WideString): PWideChar;
begin
  Result := CoTaskMemAlloc((Length(inString) + 1) * SizeOf(WideChar));
  Move(inString, Result, (Length(inString) + 1) * SizeOf(WideChar));
end;

procedure FreeWideChar(var inString: PWideChar);
begin
  if Assigned(inString) then
  begin
    CoTaskMemFree(inString);
    inString := nil;
  end;
end;

function QueryRemoteFileInfo(const Url: string; dwInfoFlag: Integer): string;
var
  hInet: HINTERNET;
  hConnect: HINTERNET;
  infoBuffer: array[0..512] of char;
  dummy: DWORD;
  bufLen: DWORD;
  lbResult: LongBool;
begin
  hInet := InternetOpen(PChar('TIDownload'),
    INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, nil, nil, 0);
  hConnect := InternetOpenUrl(hInet, PChar(Url), nil, 0, INTERNET_FLAG_NO_UI, 0);
  if not Assigned(hConnect) then
  begin
    Result := '';
  end
  else
  begin
    dummy := 0;
    bufLen := Length(infoBuffer);
    lbResult := HttpQueryInfo(hConnect, dwInfoFlag, @infoBuffer[0], bufLen, dummy);
    if lbResult then
      Result := infoBuffer
    else
      Result := '';
    InternetCloseHandle(hConnect);
  end;
  InternetCloseHandle(hInet);
end;

function GetInfoByMimeType(const ContentType: string): boolean;
var
  Reg: TRegistry;
  CLSID: string;
begin
  Result := FALSE;

  Reg := TRegistry.Create(KEY_READ);
  try
    with Reg do
    begin
      RootKey := HKEY_CLASSES_ROOT;
      if OpenKeyReadOnly('MIME\Database\Content Type\' + ContentType) then
      begin
        if ValueExists('CLSID') then
        begin
          CLSID := ReadString('CLSID');
          if CLSID <> '' then
          begin
            if OpenKeyReadOnly('\CLSID\' + CLSID) then
            begin
              if KeyExists('Control') then
              begin
                if KeyExists('EnableFullPage') then
                begin
                  Result := TRUE;
                end;
              end;
            end;
          end;
        end;
      end;
      CloseKey;
    end;
  finally
    Reg.Free;
  end;
end;

function StrContain(SubString: string; Str: string): boolean;
begin
  Result := AnsiPos(AnsiLowerCase(SubString), AnsiLowerCase(Str)) > 0;
end;

function IncludeTrailingPathDelimiter(const S: string): string;
begin
  Result := S;
  if not IsPathDelimiter(Result, Length(Result)) then
    Result := Result + PathDelim;
end;

function CharReplace(const Source: string; OldChar, NewChar: Char): string;
var
  i: Integer;
begin
  Result := Source;
  for i := 1 to Length(Result) do
    if Result[i] = OldChar then
      Result[i] := NewChar
end;

end.
