Skip to content
This repository has been archived by the owner on Sep 2, 2021. It is now read-only.

Commit

Permalink
Merge pull request #299 from adobe/bchin/open-with-brackets-from-wind…
Browse files Browse the repository at this point in the history
…ows-explorer-no-msi

Implement Windows Explorer "Open with..." option (no .msi)
  • Loading branch information
JeffryBooher committed Aug 15, 2013
2 parents 914c17a + 42f25c6 commit 04c8c4d
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 4 deletions.
93 changes: 90 additions & 3 deletions appshell/cefclient_win.cpp
Expand Up @@ -35,6 +35,9 @@

#define CLOSING_PROP L"CLOSING"

#define FIRST_INSTANCE_MUTEX_NAME (APP_NAME L".Shell.Instance")
#define ID_WM_COPYDATA_SENDOPENFILECOMMAND (WM_USER+1001)

// Global Variables:
DWORD g_appStartupTime;
HINSTANCE hInst; // current instance
Expand Down Expand Up @@ -142,6 +145,32 @@ std::wstring GetFilenamesFromCommandLine() {
return result;
}

// EnumWindowsProc callback function
// - searches for an already running Brackets application window
BOOL CALLBACK FindSuitableBracketsInstance(HWND hwnd, LPARAM lParam)
{
ASSERT(lParam != NULL); // must be passed an HWND pointer to return, if found

// check for the Brackets application window by class name and title
WCHAR cName[MAX_PATH+1] = {0}, cTitle[MAX_PATH+1] = {0};
::GetClassName(hwnd, cName, MAX_PATH);
::GetWindowText(hwnd, cTitle, MAX_PATH);
if ((wcscmp(cName, szWindowClass) == 0) && (wcsstr(cTitle, APP_NAME) != 0)) {
// found an already running instance of Brackets. Now, check that that window
// isn't currently disabled (eg. modal dialog). If it is keep searching.
if ((::GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED) == 0) {
//return the window handle and stop searching
*(HWND*)lParam = hwnd;
return FALSE;
}
}

return TRUE; // otherwise, continue searching
}

// forward declaration; implemented in appshell_extensions_win.cpp
void ConvertToUnixPath(ExtensionString& filename);

// Program entry point function.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
Expand All @@ -167,6 +196,43 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
// Parse command line arguments. The passed in values are ignored on Windows.
AppInitCommandLine(0, NULL);

// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);

// Determine if we should use an already running instance of Brackets.
HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, FIRST_INSTANCE_MUTEX_NAME);
if ((hMutex != NULL) && AppGetCommandLine()->HasArguments() && (lpCmdLine != NULL)) {
// for subsequent instances, re-use an already running instance if we're being called to
// open an existing file on the command-line (eg. Open With.. from Windows Explorer)
HWND hFirstInstanceWnd = NULL;
::EnumWindows(FindSuitableBracketsInstance, (LPARAM)&hFirstInstanceWnd);
if (hFirstInstanceWnd != NULL) {
::SetForegroundWindow(hFirstInstanceWnd);
if (::IsIconic(hFirstInstanceWnd))
::ShowWindow(hFirstInstanceWnd, SW_RESTORE);

// message the other Brackets instance to actually open the given filename
std::wstring wstrFilename = lpCmdLine;
ConvertToUnixPath(wstrFilename);
// note: WM_COPYDATA will manage passing the string across process space
COPYDATASTRUCT data;
data.dwData = ID_WM_COPYDATA_SENDOPENFILECOMMAND;
data.cbData = (wstrFilename.length() + 1) * sizeof(WCHAR);
data.lpData = (LPVOID)wstrFilename.c_str();
::SendMessage(hFirstInstanceWnd, WM_COPYDATA, (WPARAM)(HWND)hFirstInstanceWnd, (LPARAM)(LPVOID)&data);

// exit this instance
return 0;
}
// otherwise, fall thru and launch a new instance
}

if (hMutex == NULL) {
// first instance of this app, so create the mutex and continue execution of this instance.
hMutex = ::CreateMutex(NULL, FALSE, FIRST_INSTANCE_MUTEX_NAME);
}

CefSettings settings;

// Populate the settings based on command line arguments.
Expand All @@ -180,9 +246,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
// Initialize CEF.
CefInitialize(main_args, settings, app.get());

// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);
// Register window class
MyRegisterClass(hInstance, *(app->GetCurrentLanguage().GetStruct()));

CefRefPtr<CefCommandLine> cmdLine = AppGetCommandLine();
Expand Down Expand Up @@ -272,6 +336,10 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
// Shut down CEF.
CefShutdown();

// release the first instance mutex
if (hMutex != NULL)
ReleaseMutex(hMutex);

return result;
}

Expand Down Expand Up @@ -867,6 +935,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
PostQuitMessage(0);
return 0;

case WM_COPYDATA:
// handle the interprocess communication request from another Brackets running instance
if (lParam != NULL) {
PCOPYDATASTRUCT data = (PCOPYDATASTRUCT)lParam;
if ((data->dwData == ID_WM_COPYDATA_SENDOPENFILECOMMAND) && (data->cbData > 0)) {
// another Brackets instance requests that we open the given filename
std::wstring wstrFilename = (LPCWSTR)data->lpData;
// Windows Explorer might enclose the filename in double-quotes. We need to strip these off.
if ((wstrFilename.front() == '\"') && wstrFilename.back() == '\"')
wstrFilename = wstrFilename.substr(1, wstrFilename.length() - 2);
ASSERT(g_handler != NULL);
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
// call into Javascript code to handle the open file command
ASSERT(browser != NULL);
g_handler->SendOpenFileCommand(browser, CefString(wstrFilename.c_str()));
}
}
break;

case WM_INITMENUPOPUP:
// Notify before popping up
g_handler->SendJSCommand(g_handler->GetBrowser(), APP_BEFORE_MENUPOPUP);
Expand Down
25 changes: 25 additions & 0 deletions installer/win/Brackets.wxs
Expand Up @@ -42,6 +42,29 @@ xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension">
</Component>
</DirectoryRef>

<Component Id="FileAssociations" Guid="{D0195E8D-0881-42B6-9B4F-DA84D9396506}" Directory="INSTALLDIR" KeyPath="yes">
<!-- Capabilities keys for Vista/7 "Set Program Access and Defaults" -->
<RegistryValue Root="HKLM" Key="SOFTWARE\$(var.RegistryRoot)\Capabilities" Name="ApplicationIcon" Value="[INSTALLDIR]$(var.ExeName).exe,0" Type="string" />
<RegistryValue Root="HKLM" Key="SOFTWARE\$(var.RegistryRoot)\Capabilities" Name="ApplicationName" Value="!(loc.ProductName) $(var.ProductVersionName)" Type="string" />
<RegistryValue Root="HKLM" Key="SOFTWARE\RegisteredApplications" Name="!(loc.ProductName) $(var.ProductVersionName)" Value="SOFTWARE\$(var.RegistryRoot)\Capabilities" Type="string" />

<!-- File associations -->
<?define SupportedFiletypes=txt;groovy;ini;properties;css;scss;html;htm;shtm;shtml;xhtml;cfm;cfm1;cfc;dhtml;xht;tpl;twig;hbs;handlebars;kit;jsp;aspx;ejs;js;jsx;json;svg;xml;wxs;wxl;wsdl;rss;atom;rdf;xslt;xul;xbl;mathml;config;php;php3;php4;php5;phtm;phtml;ctp;c;h;i;cc;cp;cpp;c++;cxx;hh;hpp;hxx;h++;ii;cs;cshtml;asax;ashx;java;scala;sbt;coffee;cson;cf;clj;pl;pm;rb;ru;gemspec;rake;py;pyw;wsgi;sass;less;lua;sql;diff;patch;md;markdown;yaml;yml;hx;sh?>

<?foreach filetype in $(var.SupportedFiletypes)?>
<!-- associate program with file type -->
<RegistryValue Root="HKLM" Key="SOFTWARE\$(var.RegistryRoot)\Capabilities\FileAssociations" Value="!(loc.ProductName) $(var.ProductVersionName) FileExt" Name=".$(var.filetype)" Type="string" />

<!-- associate each supported filetype with application -->
<RegistryValue Root="HKCR" Key=".$(var.filetype)" Value="text" Name="PerceivedType" Type="string" />
<RegistryValue Root="HKCR" Key=".$(var.filetype)\OpenWithProgids" Value="" Name="!(loc.ProductName) $(var.ProductVersionName) FileExt" Type="string" />
<?endforeach?>

<!-- create ProgId entry -->
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\!(loc.ProductName) $(var.ProductVersionName) FileExt\shell\open\command" Value="&quot;[INSTALLDIR]$(var.ExeName).exe&quot; &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\$(var.ExeName).exe\shell\open" Name="FriendlyAppName" Value="!(loc.ProductName) $(var.ProductVersionName)" Type="string" />

</Component>
<!-- Start Menu Shortcuts-->
<UIRef Id="WixUI_MyInstallDir" />
<UIRef Id="WixUI_ErrorProgressText" />
Expand Down Expand Up @@ -78,6 +101,8 @@ xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension">
<ComponentGroupRef Id='BRACKETSHARVESTMANAGER'/>

<ComponentRef Id='StartMenuShortcut' />

<ComponentRef Id='FileAssociations' />
</Feature>
</Product>
</Wix>
Expand Down
2 changes: 1 addition & 1 deletion installer/win/brackets-win-install-build.xml
Expand Up @@ -16,7 +16,7 @@ default="build.mul">
<property name="product.version.number" value="0.${product.sprint.number}"/>
<property name="product.version.name" value="Sprint ${product.sprint.number}"/>
<property name="product.manufacturer" value="brackets.io"/>
<property name="product.registry.root" value="${product.shortname}"/>
<property name="product.registry.root" value="${product.shortname} ${product.version.name}"/>


<!-- Installer properties -->
Expand Down

0 comments on commit 04c8c4d

Please sign in to comment.