32

Usually, I use: ShellExecute(0, 'OPEN', PChar(edtURL.Text), '', '', SW_SHOWNORMAL);

How can I have the same behaviour (opening a link in the default browser), on all platforms (Windows and OSX)?

Whiler
  • 7,998
  • 4
  • 32
  • 56

8 Answers8

24

Regarding the answer of mjn, I have written the following unit. I have successfully tested it on Windows but I don't have an OSX to test it on this platform. If someone can confirm it works, I'd appreciate.

unit fOpen;

interface

uses
{$IFDEF MSWINDOWS}
  Winapi.ShellAPI, Winapi.Windows;
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
  Posix.Stdlib;
{$ENDIF POSIX}

type
  TMisc = class
    class procedure Open(sCommand: string);
  end;

implementation

class procedure TMisc.Open(sCommand: string);
begin
{$IFDEF MSWINDOWS}
  ShellExecute(0, 'OPEN', PChar(sCommand), '', '', SW_SHOWNORMAL);
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
  _system(PAnsiChar('open ' + AnsiString(sCommand)));
{$ENDIF POSIX}
end;

end.

and I call it like this:

TMisc.Open('https://stackoverflow.com/questions/7443264/how-to-open-an-url-with-the-default-browser-with-firemonkey-cross-platform-applic');
Community
  • 1
  • 1
Whiler
  • 7,998
  • 4
  • 32
  • 56
15

In the FireMonkey discussion forum I found this code for a question about NSWorkspace.URLForApplicationToOpenURL:

uses
  Posix.Stdlib;
....
  _system(PAnsiChar('open ' + ACommand));

(not tested by me)


Update: Posix is not available on Windows so it is not possible to write a solution which uses the same OS calls on all platforms. I suggest to move such code in a central 'XPlatform' unit which has some IFDEF POSIX etc.

mjn
  • 36,362
  • 28
  • 176
  • 378
  • I understand the ifdef... but I don't know how to build an application with uses that doesn't exist ;o( – Whiler Sep 16 '11 at 11:06
  • 2
    If you switch to OSX platform in the project view, the Posix units will be found. – mjn Sep 16 '11 at 11:10
  • I have updated the question with the implementation I've done with your advices. Do not hesitate to tell me if we could do better... – Whiler Sep 16 '11 at 12:09
  • @EASI this solution was already in my answer: use IFDEF POSIX etc. to separate platform specific code – mjn Jul 14 '14 at 15:55
  • 1
    It is "open" on Mac OS X, "xdg-open" on Linux, "start" on Windows – OCTAGRAM Feb 08 '18 at 13:04
14

For all platforms (Windows, macOs, iOS and Android) you can use the unit I wrote for my blog

unit u_urlOpen;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
{$IF Defined(IOS)}
  macapi.helpers, iOSapi.Foundation, FMX.helpers.iOS;
{$ELSEIF Defined(ANDROID)}
Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Net,
   Androidapi.JNI.App,
  Androidapi.helpers;
{$ELSEIF Defined(MACOS)}
Posix.Stdlib;
{$ELSEIF Defined(MSWINDOWS)}
Winapi.ShellAPI, Winapi.Windows;
{$ENDIF}

type
  tUrlOpen = class
    class procedure Open(URL: string);
  end;

implementation

class procedure tUrlOpen.Open(URL: string);
{$IF Defined(ANDROID)}
var
  Intent: JIntent;
{$ENDIF}
begin
{$IF Defined(ANDROID)}
  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
  Intent.setData(StrToJURI(URL));
  tandroidhelper.Activity.startActivity(Intent);
  // SharedActivity.startActivity(Intent);
{$ELSEIF Defined(MSWINDOWS)}
  ShellExecute(0, 'OPEN', PWideChar(URL), nil, nil, SW_SHOWNORMAL);
{$ELSEIF Defined(IOS)}
  SharedApplication.OpenURL(StrToNSUrl(URL));
{$ELSEIF Defined(MACOS)}
  _system(PAnsiChar('open ' + AnsiString(URL)));
{$ENDIF}
end;

end.
  • Great solution! – Sergey Zubkov Mar 31 '21 at 15:25
  • I tried this, doesn't work, at least on Windows. Included the unit in my project, added a urlOpen: tUrlOpen in my vars, then wrote code to call create, call Open with the url and then free. It throws an access violation on the create. – Juan Jimenez Sep 03 '21 at 16:16
  • Hi. Don't call create. The Open() method is a class method. Call it simply with TURLOpen.Open('your url or resource to open') – Patrick PREMARTIN Sep 05 '21 at 07:49
  • @PatrickPREMARTIN It doesn't seem to work with https links, any clues ? – Rohit Gupta Sep 21 '21 at 02:39
  • Hi @RohitGupta There is no reason it doesn't work for https : it works for http, mailto and a lot of protocoles. What platform did you tried ? Are the SSL certificate and https url valid for the website ? – Patrick PREMARTIN Sep 22 '21 at 09:15
  • What about Linux solution? When I'm trying _system(PAnsiChar('open ' + AnsiString(URL))); then PAServer tell "open: not found" – Alex Egorov Apr 12 '22 at 20:33
  • 1
    Hi @AlexEgorov. The answer is on https://stackoverflow.com/questions/5116473/linux-command-to-open-url-in-default-browser and unfortunately I can say "It's Complicated". No standard command, have to check many Linux GUI and setups. I add it to my todo list for this unit, but for your solution, You'll have to try open, gnome-open and others instead of just the one I proposed for MacOS. – Patrick PREMARTIN Apr 22 '22 at 06:58
  • Does not work for android 12. – Greg T Aug 19 '22 at 22:13
  • Hi @GregT I don't have an Android 12 device and can't check. Some things have changed in StartActivity() use, but it shouldn't impact this code. Could you open an issue on https://github.com/DeveloppeurPascal/librairies/issues or a new SO message and give a sample of what you try to open ? – Patrick PREMARTIN Aug 21 '22 at 06:52
  • Just remove SetAction and SetData .. put this one line in it's place for Android 11-12: Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW, TJnet_Uri.JavaClass.parse(StringToJString(URL))); .....You might have to add uses Androidapi.JNI.JavaTypes ...but I don't remember. – Greg T Aug 21 '22 at 20:19
  • Thanks @GregT I'll update my unit after testing this code. – Patrick PREMARTIN Aug 23 '22 at 05:33
3

XE2 C++ code that is tested succesfully (Windows 7 64 and OSX Lion), minor improvements. Thank's Whiler, the pain is over :)

#include <fmx.h>
// ---------------------------------------------------------------------------
#ifdef _WIN32
#include <shellapi.h>
#endif// Windows
#ifdef TARGET_OS_MAC
#include <Posix.Stdlib.hpp>
#endif // Mac

void OpenCommand(String _sCommand) {
    #ifdef _Windows
    String open = "open";
    ShellExecute(0, open.w_str(), _sCommand.c_str(), NULL, NULL, SW_SHOWNORMAL);
    #endif // Windows

    #ifdef TARGET_OS_MAC
    system(AnsiString("open " + AnsiString(_sCommand)).c_str());
    #endif // Mac
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Be careful... some chars can generate issue.... for instance, try an URL with `()`... `()` are interpreted by the `open`... you need to encapsulate the URL with `'` ;o) – Whiler Oct 19 '12 at 08:53
1

And now a C++ version (OSx code untested, also not sure about the _POSIX #def):

#ifdef _Windows
#include <Winapi.Windows.hpp>
#endif // _Windows
#ifdef _POSIX
#include <Posix.Stdlib.h>
#endif // _POSIX

void OpenCommand(String _sCommand)
{
    #ifdef _Windows
    ShellExecute(0, _T("open"), _sCommand.c_str(), _T(""), _T(""), SW_SHOWNORMAL);
    #endif // _Windows
    #ifdef _POSIX
    _system(AnsiString("open " + AnsiString(_sCommand)).c_str());
    #endif // _POSIX
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

As @NicoBlu mentioned, the accepted solution seems to truncate the URL after the first occurence of '&'. Here is what works for me without truncation:

uses Macapi.AppKit, Macapi.Foundation, Macapi.Helpers;

// ...

procedure OpenLinkInDefaultBrowser(const Link: string);
  var URL : NSURL;
      Workspace : NSWorkspace;
begin
  URL := TNSURL.Wrap(TNSURL.OCClass.URLWithString(StrToNSStr(Link)));
  Workspace := TNSWorkspace.Wrap(TNSWorkspace.OCClass.sharedWorkspace);
  Workspace.openURL(URL);
end;
DNR
  • 1,619
  • 2
  • 14
  • 22
0
_system(PAnsiChar('open ' + AnsiString(sCommand)));

not works if URL string (sCommand) includes ampersand char (&), necessary to specify many arguments in querystring.

URL sended to def. browser in OSX (Safari) is truncated at the first occurence of &.

Wtower
  • 18,848
  • 11
  • 103
  • 80
NicoBlu
  • 19
  • 4
0
LEncodedString : String;

begin
    LEncodedString := TIdURI.URLEncode('http://www.malcolmgroves.com');
    sharedApplication.openURL(StringToNSURL(LEncodedString));
end;
Maxime Bonin
  • 71
  • 1
  • 7
  • where does that sharedApplication object come from ? – Tuncay Göncüoğlu Jul 07 '17 at 16:27
  • good question @TuncayGöncüoğlu ! I don't have access to my project for the weekend so I'm not 100% sure but have a look at the first answer's code sample [link](https://stackoverflow.com/questions/16354876/opening-url-within-ios-application) – Maxime Bonin Jul 08 '17 at 00:18