3535
3636#define CLOSING_PROP L" CLOSING"
3737
38+ #define FIRST_INSTANCE_MUTEX_NAME (APP_NAME L" .Shell.Instance" )
39+ #define ID_WM_COPYDATA_SENDOPENFILECOMMAND (WM_USER+1001 )
40+
3841// Global Variables:
3942DWORD g_appStartupTime;
4043HINSTANCE hInst; // current instance
@@ -142,6 +145,32 @@ std::wstring GetFilenamesFromCommandLine() {
142145 return result;
143146}
144147
148+ // EnumWindowsProc callback function
149+ // - searches for an already running Brackets application window
150+ BOOL CALLBACK FindSuitableBracketsInstance (HWND hwnd, LPARAM lParam)
151+ {
152+ ASSERT (lParam != NULL ); // must be passed an HWND pointer to return, if found
153+
154+ // check for the Brackets application window by class name and title
155+ WCHAR cName[MAX_PATH+1 ] = {0 }, cTitle[MAX_PATH+1 ] = {0 };
156+ ::GetClassName (hwnd, cName, MAX_PATH);
157+ ::GetWindowText (hwnd, cTitle, MAX_PATH);
158+ if ((wcscmp (cName, szWindowClass) == 0 ) && (wcsstr (cTitle, APP_NAME) != 0 )) {
159+ // found an already running instance of Brackets. Now, check that that window
160+ // isn't currently disabled (eg. modal dialog). If it is keep searching.
161+ if ((::GetWindowLong (hwnd, GWL_STYLE) & WS_DISABLED) == 0 ) {
162+ // return the window handle and stop searching
163+ *(HWND*)lParam = hwnd;
164+ return FALSE ;
165+ }
166+ }
167+
168+ return TRUE ; // otherwise, continue searching
169+ }
170+
171+ // forward declaration; implemented in appshell_extensions_win.cpp
172+ void ConvertToUnixPath (ExtensionString& filename);
173+
145174// Program entry point function.
146175int APIENTRY wWinMain (HINSTANCE hInstance,
147176 HINSTANCE hPrevInstance,
@@ -167,6 +196,43 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
167196 // Parse command line arguments. The passed in values are ignored on Windows.
168197 AppInitCommandLine (0 , NULL );
169198
199+ // Initialize global strings
200+ LoadString (hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
201+ LoadString (hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);
202+
203+ // Determine if we should use an already running instance of Brackets.
204+ HANDLE hMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE , FIRST_INSTANCE_MUTEX_NAME);
205+ if ((hMutex != NULL ) && AppGetCommandLine ()->HasArguments () && (lpCmdLine != NULL )) {
206+ // for subsequent instances, re-use an already running instance if we're being called to
207+ // open an existing file on the command-line (eg. Open With.. from Windows Explorer)
208+ HWND hFirstInstanceWnd = NULL ;
209+ ::EnumWindows (FindSuitableBracketsInstance, (LPARAM)&hFirstInstanceWnd);
210+ if (hFirstInstanceWnd != NULL ) {
211+ ::SetForegroundWindow (hFirstInstanceWnd);
212+ if (::IsIconic (hFirstInstanceWnd))
213+ ::ShowWindow (hFirstInstanceWnd, SW_RESTORE);
214+
215+ // message the other Brackets instance to actually open the given filename
216+ std::wstring wstrFilename = lpCmdLine;
217+ ConvertToUnixPath (wstrFilename);
218+ // note: WM_COPYDATA will manage passing the string across process space
219+ COPYDATASTRUCT data;
220+ data.dwData = ID_WM_COPYDATA_SENDOPENFILECOMMAND;
221+ data.cbData = (wstrFilename.length () + 1 ) * sizeof (WCHAR);
222+ data.lpData = (LPVOID)wstrFilename.c_str ();
223+ ::SendMessage (hFirstInstanceWnd, WM_COPYDATA, (WPARAM)(HWND)hFirstInstanceWnd, (LPARAM)(LPVOID)&data);
224+
225+ // exit this instance
226+ return 0 ;
227+ }
228+ // otherwise, fall thru and launch a new instance
229+ }
230+
231+ if (hMutex == NULL ) {
232+ // first instance of this app, so create the mutex and continue execution of this instance.
233+ hMutex = ::CreateMutex (NULL , FALSE , FIRST_INSTANCE_MUTEX_NAME);
234+ }
235+
170236 CefSettings settings;
171237
172238 // Populate the settings based on command line arguments.
@@ -180,9 +246,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
180246 // Initialize CEF.
181247 CefInitialize (main_args, settings, app.get ());
182248
183- // Initialize global strings
184- LoadString (hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
185- LoadString (hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);
249+ // Register window class
186250 MyRegisterClass (hInstance, *(app->GetCurrentLanguage ().GetStruct ()));
187251
188252 CefRefPtr<CefCommandLine> cmdLine = AppGetCommandLine ();
@@ -272,6 +336,10 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
272336 // Shut down CEF.
273337 CefShutdown ();
274338
339+ // release the first instance mutex
340+ if (hMutex != NULL )
341+ ReleaseMutex (hMutex);
342+
275343 return result;
276344}
277345
@@ -867,6 +935,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
867935 PostQuitMessage (0 );
868936 return 0 ;
869937
938+ case WM_COPYDATA:
939+ // handle the interprocess communication request from another Brackets running instance
940+ if (lParam != NULL ) {
941+ PCOPYDATASTRUCT data = (PCOPYDATASTRUCT)lParam;
942+ if ((data->dwData == ID_WM_COPYDATA_SENDOPENFILECOMMAND) && (data->cbData > 0 )) {
943+ // another Brackets instance requests that we open the given filename
944+ std::wstring wstrFilename = (LPCWSTR)data->lpData ;
945+ // Windows Explorer might enclose the filename in double-quotes. We need to strip these off.
946+ if ((wstrFilename.front () == ' \" ' ) && wstrFilename.back () == ' \" ' )
947+ wstrFilename = wstrFilename.substr (1 , wstrFilename.length () - 2 );
948+ ASSERT (g_handler != NULL );
949+ CefRefPtr<CefBrowser> browser = g_handler->GetBrowser ();
950+ // call into Javascript code to handle the open file command
951+ ASSERT (browser != NULL );
952+ g_handler->SendOpenFileCommand (browser, CefString (wstrFilename.c_str ()));
953+ }
954+ }
955+ break ;
956+
870957 case WM_INITMENUPOPUP:
871958 // Notify before popping up
872959 g_handler->SendJSCommand (g_handler->GetBrowser (), APP_BEFORE_MENUPOPUP);
0 commit comments