Here's how I solved it. Basically, there are two parts. Or three.
First, the app needs to register itself in the registry, like this. It took some googling to find out how to use the windows register functions, but they were pretty straightforward. By adding this to the registry, your application will launch when a link with your custom url protocol is clicked.
Secondly, the app needs to detect that it's been started from a browser. Obviously quite trivial, just check the command line for "/uri" or however you chose to customize it.
Third, you don't actually want to start your application - it should already be running! Instead, when you've detected that you got started from a hyperlink, you need to detect if another instance of the application is already running. After that, you need to pass the command line to it. Here's how I did it:
bool ShouldContinueStartEditor( const std::string& command_line )
{
// Check if this instance was spawned from a web browser
if ( command_line.find( "/uri" ) != std::string::npos )
{
// Try to find other instance of JustEdit
HWND wnd = FindWindow( "AV_MainFrame", NULL );
if ( wnd )
{
COPYDATASTRUCT cds;
NEditorCopyData::SCommandLine data_to_copy;
strncpy( data_to_copy.m_CommandLine, command_line.c_str(), sizeof(data_to_copy.m_CommandLine) - 2 );
cds.dwData = NEditorCopyData::ECommandLine; // function identifier
cds.cbData = sizeof( data_to_copy ); // size of data
cds.lpData = &data_to_copy; // data structure
SendMessage( wnd, WM_COPYDATA, NULL, (LPARAM) (LPVOID) &cds );
}
return false;
}
return true;
}
"AV_Mainframe" is the name of the hwnd. If you happen to be using WTL, you can declare it like this.
DECLARE_FRAME_WND_CLASS("AV_MainFrame", IDR_MAINFRAME)
Now, in your window class, you need to handle the WM_COPYDATA message like this:
MESSAGE_HANDLER(WM_COPYDATA, OnCopyData);
LRESULT OnCopyData(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT CMainFrame::OnCopyData(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
PCOPYDATASTRUCT cds = (PCOPYDATASTRUCT) lParam;
if ( cds->dwData == NEditorCopyData::ECommandLine )
{
NEditorCopyData::SCommandLine* command_line = static_cast( cds->lpData );
const char* internal_path = strstr( command_line->m_CommandLine, "/uri" );
if ( internal_path != NULL )
{
// Do your thang
}
}
return 0;
}
And that's pretty much all there's to it. Oh, this is what the copy data namespace looks like:
namespace NEditorCopyData
{
enum ECopyDataMessages
{
ECommandLine = 0
};
struct SCommandLine
{
char m_CommandLine[512];
};
}