93c03bd7a2dc187d3f7987512473c24aabcd23da
[idea/community.git] / native / WinLauncher / WinLauncher / WinLauncher.cpp
1 /*
2 * Copyright 2000-2013 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "stdafx.h"
18 #include "WinLauncher.h"
19
20 typedef JNIIMPORT jint(JNICALL *JNI_createJavaVM)(JavaVM **pvm, JNIEnv **env, void *args);
21
22 HINSTANCE hInst; // Current instance.
23 char jvmPath[_MAX_PATH] = "";
24 JavaVMOption* vmOptions = NULL;
25 int vmOptionCount = 0;
26 bool bServerJVM = false;
27 HMODULE hJVM = NULL;
28 JNI_createJavaVM pCreateJavaVM = NULL;
29 JavaVM* jvm = NULL;
30 JNIEnv* env = NULL;
31 volatile bool terminating = false;
32 bool nativesplash = false;
33
34 HANDLE hFileMapping;
35 HANDLE hEvent;
36 HANDLE hSingleInstanceWatcherThread;
37 const int FILE_MAPPING_SIZE = 16000;
38
39 #ifdef _M_X64
40 bool need64BitJRE = true;
41 #define BITS_STR "64-bit"
42 #else
43 bool need64BitJRE = false;
44 #define BITS_STR "32-bit"
45 #endif
46
47 std::string EncodeWideACP(const std::wstring &str)
48 {
49   int cbANSI = WideCharToMultiByte(CP_ACP, 0, str.c_str(), str.size(), NULL, 0, NULL, NULL);
50   char* ansiBuf = new char[cbANSI];
51   WideCharToMultiByte(CP_ACP, 0, str.c_str(), str.size(), ansiBuf, cbANSI, NULL, NULL);
52   std::string result(ansiBuf, cbANSI);
53   delete[] ansiBuf;
54   return result;
55 }
56
57 std::string LoadStdString(int id)
58 {
59   wchar_t *buf = NULL;
60   int len = LoadStringW(hInst, id, reinterpret_cast<LPWSTR>(&buf), 0);
61   return len ? EncodeWideACP(std::wstring(buf, len)) : "";
62 }
63
64 bool FileExists(const std::string& path)
65 {
66   return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES;
67 }
68
69 bool IsValidJRE(const char* path)
70 {
71   std::string dllPath(path);
72   if (dllPath[dllPath.size() - 1] != '\\')
73   {
74     dllPath += "\\";
75   }
76   return FileExists(dllPath + "bin\\server\\jvm.dll") || FileExists(dllPath + "bin\\client\\jvm.dll");
77 }
78
79 bool Is64BitJRE(const char* path)
80 {
81   std::string cfgPath(path);
82   cfgPath += "\\lib\\amd64\\jvm.cfg";
83   return FileExists(cfgPath);
84 }
85
86 bool FindValidJVM(const char* path)
87 {
88   if (IsValidJRE(path))
89   {
90     strcpy_s(jvmPath, _MAX_PATH - 1, path);
91     return true;
92   }
93   char jrePath[_MAX_PATH];
94   strcpy_s(jrePath, path);
95   if (jrePath[strlen(jrePath) - 1] != '\\')
96   {
97     strcat_s(jrePath, "\\");
98   }
99   strcat_s(jrePath, _MAX_PATH - 1, "jre");
100   if (IsValidJRE(jrePath))
101   {
102     strcpy_s(jvmPath, jrePath);
103     return true;
104   }
105   return false;
106 }
107
108 std::string GetAdjacentDir(const char* suffix)
109 {
110   char libDir[_MAX_PATH];
111   GetModuleFileNameA(NULL, libDir, _MAX_PATH - 1);
112   char* lastSlash = strrchr(libDir, '\\');
113   if (!lastSlash) return "";
114   *lastSlash = '\0';
115   lastSlash = strrchr(libDir, '\\');
116   if (!lastSlash) return "";
117   strcpy(lastSlash + 1, suffix);
118   strcat_s(libDir, "\\");
119   return std::string(libDir);
120 }
121
122 bool FindJVMInEnvVar(const char* envVarName, bool& result)
123 {
124   char envVarValue[_MAX_PATH];
125   if (GetEnvironmentVariableA(envVarName, envVarValue, _MAX_PATH - 1))
126   {
127     if (FindValidJVM(envVarValue))
128     {
129       if (Is64BitJRE(jvmPath) != need64BitJRE) return false;
130       result = true;
131     }
132     else
133     {
134       char buf[_MAX_PATH];
135       sprintf_s(buf, "The environment variable %s (with the value of %s) does not point to a valid JVM installation.",
136         envVarName, envVarValue);
137       std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
138       MessageBoxA(NULL, buf, error.c_str(), MB_OK);
139       result = false;
140     }
141     return true;
142   }
143   return false;
144 }
145
146 bool FindJVMInRegistryKey(const char* key, bool wow64_32)
147 {
148   HKEY hKey;
149   int flags = KEY_READ;
150   if (wow64_32) flags |= KEY_WOW64_32KEY;
151   if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return false;
152   char javaHome[_MAX_PATH];
153   DWORD javaHomeSize = _MAX_PATH - 1;
154   bool success = false;
155   if (RegQueryValueExA(hKey, "JavaHome", NULL, NULL, (LPBYTE)javaHome, &javaHomeSize) == ERROR_SUCCESS)
156   {
157     success = FindValidJVM(javaHome);
158   }
159   RegCloseKey(hKey);
160   return success;
161 }
162
163 bool FindJVMInRegistryWithVersion(const char* version, bool wow64_32)
164 {
165   const char* keyName = LoadStdString(IDS_JDK_ONLY) == std::string("true")
166     ? "Java Development Kit"
167     : "Java Runtime Environment";
168
169   char buf[_MAX_PATH];
170   sprintf_s(buf, "Software\\JavaSoft\\%s\\%s", keyName, version);
171   return FindJVMInRegistryKey(buf, wow64_32);
172 }
173
174 bool FindJVMInRegistry()
175 {
176 #ifndef _M_X64
177   if (FindJVMInRegistryWithVersion("1.8", true))
178     return true;
179   if (FindJVMInRegistryWithVersion("1.7", true))
180     return true;
181   if (FindJVMInRegistryWithVersion("1.6", true))
182     return true;
183 #endif
184
185   if (FindJVMInRegistryWithVersion("1.8", false))
186     return true;
187   if (FindJVMInRegistryWithVersion("1.7", false))
188     return true;
189   if (FindJVMInRegistryWithVersion("1.6", false))
190     return true;
191   return false;
192 }
193
194 // The following code is taken from http://msdn.microsoft.com/en-us/library/ms684139(v=vs.85).aspx
195 // and provides a backwards compatible way to check if this application is a 32-bit process running
196 // on a 64-bit OS
197 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
198
199 LPFN_ISWOW64PROCESS fnIsWow64Process;
200
201 BOOL IsWow64()
202 {
203   BOOL bIsWow64 = FALSE;
204
205   //IsWow64Process is not available on all supported versions of Windows.
206   //Use GetModuleHandle to get a handle to the DLL that contains the function
207   //and GetProcAddress to get a pointer to the function if available.
208
209   fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
210       GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
211
212   if (NULL != fnIsWow64Process)
213   {
214     fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
215   }
216   return bIsWow64;
217 }
218
219 bool LocateJVM()
220 {
221   bool result;
222   if (FindJVMInEnvVar(LoadStdString(IDS_JDK_ENV_VAR).c_str(), result))
223   {
224     return result;
225   }
226
227   std::vector<std::string> jrePaths;
228   if(need64BitJRE) jrePaths.push_back(GetAdjacentDir("jre64"));
229   jrePaths.push_back(GetAdjacentDir("jre"));
230   for(std::vector<std::string>::iterator it = jrePaths.begin(); it != jrePaths.end(); ++it) {
231     if (FindValidJVM((*it).c_str()) && Is64BitJRE(jvmPath) == need64BitJRE)
232     {
233       return true;
234     }
235   }
236
237   if (FindJVMInEnvVar("JAVA_HOME", result))
238   {
239     return result;
240   }
241
242   if (FindJVMInRegistry())
243   {
244     return true;
245   }
246
247   std::string jvmError;
248   jvmError = "No JVM installation found. Please install a " BITS_STR " JDK.\n"
249     "If you already have a JDK installed, define a JAVA_HOME variable in\n"
250     "Computer > System Properties > System Settings > Environment Variables.";
251
252   if (IsWow64())
253   {
254     // If WoW64, this means we are running a 32-bit program on 64-bit Windows. This may explain
255     // why we couldn't locate the JVM.
256     jvmError += "\n\nNOTE: We have detected that you are running a 64-bit version of the "
257         "Windows operating system but are running the 32-bit executable. This "
258         "can prevent you from finding a 64-bit installation of Java. Consider running "
259         "the 64-bit version instead, if this is the problem you're encountering.";
260   }
261
262   std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
263   MessageBoxA(NULL, jvmError.c_str(),  error.c_str(), MB_OK);
264   return false;
265 }
266
267 void TrimLine(char* line)
268 {
269   char *p = line + strlen(line) - 1;
270   if (p >= line && *p == '\n')
271   {
272     *p-- = '\0';
273   }
274   while (p >= line && (*p == ' ' || *p == '\t'))
275   {
276     *p-- = '\0';
277   }
278 }
279
280 bool LoadVMOptionsFile(const TCHAR* path, std::vector<std::string>& vmOptionLines)
281 {
282   FILE *f = _tfopen(path, _T("rt"));
283   if (!f) return false;
284
285   char line[_MAX_PATH];
286   while (fgets(line, _MAX_PATH, f))
287   {
288     TrimLine(line);
289     if (line[0] == '#') continue;
290     if (strcmp(line, "-server") == 0)
291     {
292       bServerJVM = true;
293     }
294     else if (strlen(line) > 0)
295     {
296       vmOptionLines.push_back(line);
297     }
298   }
299   fclose(f);
300
301   return true;
302 }
303
304 std::string FindToolsJar()
305 {
306   std::string toolsJarPath = jvmPath;
307   size_t lastSlash = toolsJarPath.rfind('\\');
308   if (lastSlash != std::string::npos)
309   {
310     toolsJarPath = toolsJarPath.substr(0, lastSlash + 1) + "lib\\tools.jar";
311     if (FileExists(toolsJarPath))
312     {
313       return toolsJarPath;
314     }
315   }
316   return "";
317 }
318
319 std::string CollectLibJars(const std::string& jarList)
320 {
321   std::string libDir = GetAdjacentDir("lib");
322   if (libDir.size() == 0 || !FileExists(libDir))
323   {
324     return "";
325   }
326
327   std::string result;
328   int pos = 0;
329   while (pos < jarList.size())
330   {
331     int delimiterPos = jarList.find(';', pos);
332     if (delimiterPos == std::string::npos)
333     {
334       delimiterPos = jarList.size();
335     }
336     if (result.size() > 0)
337     {
338       result += ";";
339     }
340     result += libDir;
341     result += jarList.substr(pos, delimiterPos - pos);
342     pos = delimiterPos + 1;
343   }
344   return result;
345 }
346
347 std::string BuildClassPath()
348 {
349   std::string classpathLibs = LoadStdString(IDS_CLASSPATH_LIBS);
350   std::string result = CollectLibJars(classpathLibs);
351
352   std::string toolsJar = FindToolsJar();
353   if (toolsJar.size() > 0)
354   {
355     result += ";";
356     result += toolsJar;
357   }
358
359   return result;
360 }
361
362 bool AddClassPathOptions(std::vector<std::string>& vmOptionLines)
363 {
364   std::string classPath = BuildClassPath();
365   if (classPath.size() == 0) return false;
366   vmOptionLines.push_back(std::string("-Djava.class.path=") + classPath);
367
368   std::string bootClassPathLibs = LoadStdString(IDS_BOOTCLASSPATH_LIBS);
369   std::string bootClassPath = CollectLibJars(bootClassPathLibs);
370   if (bootClassPath.size() > 0)
371   {
372     vmOptionLines.push_back(std::string("-Xbootclasspath/a:") + bootClassPath);
373   }
374
375   return true;
376 }
377
378 //return VMOptions from one of the files in the following order:
379 //$<IDE-NAME>_VM_OPTIONS
380 //$CONFIG_DIRECTORY/<ide-name>[64][.exe].vmoptions
381 //bin/<ide-name>[64][.exe].vmoptions
382 bool FindValidVMOptions(std::vector<std::wstring> files, std::wstring& used, std::vector<std::string>& vmOptionLines)
383 {
384   if (files.size() != 0)
385   {
386     for (int i = 0; i < files.size(); i++)
387     {
388       if (GetFileAttributes(files[i].c_str()) != INVALID_FILE_ATTRIBUTES)
389       {
390         if (LoadVMOptionsFile(files[i].c_str(), vmOptionLines))
391         {
392           used += (used.size() ? L"," : L"") + files[i];
393           return true;
394         }
395       }
396     }
397   }
398   return false;
399 }
400
401 void AddPredefinedVMOptions(std::vector<std::string>& vmOptionLines)
402 {
403   std::string vmOptions = LoadStdString(IDS_VM_OPTIONS);
404   while (vmOptions.size() > 0)
405   {
406     int pos = vmOptions.find(' ');
407     if (pos == std::string::npos) pos = vmOptions.size();
408     vmOptionLines.push_back(vmOptions.substr(0, pos));
409     while (pos < vmOptions.size() && vmOptions[pos] == ' ') pos++;
410     vmOptions = vmOptions.substr(pos);
411   }
412
413   char propertiesFile[_MAX_PATH];
414   if (GetEnvironmentVariableA(LoadStdString(IDS_PROPS_ENV_VAR).c_str(), propertiesFile, _MAX_PATH))
415   {
416     vmOptionLines.push_back(std::string("-Didea.properties.file=") + propertiesFile);
417   }
418 }
419
420 bool LoadVMOptions()
421 {
422   TCHAR buffer[_MAX_PATH];
423   TCHAR copy[_MAX_PATH];
424
425   std::vector<std::wstring> files;
426
427   GetModuleFileName(NULL, buffer, _MAX_PATH);
428   std::wstring module(buffer);
429
430   if (LoadString(hInst, IDS_VM_OPTIONS_ENV_VAR, buffer, _MAX_PATH))
431   {
432     if (GetEnvironmentVariableW(buffer, copy, _MAX_PATH)) {
433       ExpandEnvironmentStrings(copy, buffer, _MAX_PATH);
434       files.push_back(std::wstring(buffer));
435     }
436   }
437
438   if (LoadString(hInst, IDS_VM_OPTIONS_PATH, buffer, _MAX_PATH))
439   {
440     ExpandEnvironmentStrings(buffer, copy, _MAX_PATH - 1);
441     std::wstring selector(copy);
442     files.push_back(selector + module.substr(module.find_last_of('\\')) + L".vmoptions");
443   }
444
445   files.push_back(module + L".vmoptions");
446   std::wstring used;
447   std::vector<std::string> vmOptionLines;
448
449   if (!FindValidVMOptions(files, used, vmOptionLines))
450   {
451     std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
452     MessageBoxA(NULL, "Cannot find VM options file", error.c_str(), MB_OK);
453     return false;
454   }
455
456   vmOptionLines.push_back(std::string("-Djb.vmOptionsFile=") + EncodeWideACP(used));
457
458   if (!AddClassPathOptions(vmOptionLines)) return false;
459   AddPredefinedVMOptions(vmOptionLines);
460
461   vmOptionCount = vmOptionLines.size();
462   vmOptions = (JavaVMOption*)malloc(vmOptionCount * sizeof(JavaVMOption));
463   for (int i = 0; i < vmOptionLines.size(); i++)
464   {
465     vmOptions[i].optionString = _strdup(vmOptionLines[i].c_str());
466     vmOptions[i].extraInfo = 0;
467   }
468
469   return true;
470 }
471
472 bool LoadJVMLibrary()
473 {
474   std::string dllName(jvmPath);
475   std::string binDir = dllName + "\\bin";
476   std::string serverDllName = binDir + "\\server\\jvm.dll";
477   std::string clientDllName = binDir + "\\client\\jvm.dll";
478   if ((bServerJVM && FileExists(serverDllName)) || !FileExists(clientDllName))
479   {
480     dllName = serverDllName;
481   }
482   else
483   {
484     dllName = clientDllName;
485   }
486
487   // ensure we can find msvcr100.dll which is located in jre/bin directory; jvm.dll depends on it.
488   SetCurrentDirectoryA(binDir.c_str());
489   hJVM = LoadLibraryA(dllName.c_str());
490   if (hJVM)
491   {
492     pCreateJavaVM = (JNI_createJavaVM) GetProcAddress(hJVM, "JNI_CreateJavaVM");
493   }
494   if (!pCreateJavaVM)
495   {
496     std::string jvmError = "Failed to load JVM DLL ";
497     jvmError += dllName.c_str();
498     jvmError += "\n"
499         "If you already have a " BITS_STR " JDK installed, define a JAVA_HOME variable in "
500         "Computer > System Properties > System Settings > Environment Variables.";
501     std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
502     MessageBoxA(NULL, jvmError.c_str(), error.c_str(), MB_OK);
503     return false;
504   }
505   return true;
506 }
507
508 bool CreateJVM()
509 {
510   JavaVMInitArgs initArgs;
511   initArgs.version = JNI_VERSION_1_2;
512   initArgs.options = vmOptions;
513   initArgs.nOptions = vmOptionCount;
514   initArgs.ignoreUnrecognized = JNI_FALSE;
515
516   int result = pCreateJavaVM(&jvm, &env, &initArgs);
517
518   for (int i = 0; i < vmOptionCount; i++)
519   {
520     free(vmOptions[i].optionString);
521   }
522   free(vmOptions);
523   vmOptions = NULL;
524
525   if (result != JNI_OK)
526   {
527     std::stringstream buf;
528
529     buf << "Failed to create JVM: error code " << result << ".\n";
530     buf << "JVM Path: " << jvmPath << "\n";
531     buf << "If you already have a " BITS_STR " JDK installed, define a JAVA_HOME variable in \n";
532     buf << "Computer > System Properties > System Settings > Environment Variables.";
533     std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
534     MessageBoxA(NULL, buf.str().c_str(), error.c_str(), MB_OK);
535   }
536
537   return result == JNI_OK;
538 }
539
540 jobjectArray PrepareCommandLine()
541 {
542   int numArgs;
543   LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &numArgs);
544   jclass stringClass = env->FindClass("java/lang/String");
545   jobjectArray args = env->NewObjectArray(numArgs - (nativesplash ? 2 : 1), stringClass, NULL);
546   for (int i = 1, k = 0; i < numArgs; i++)
547   {
548     const wchar_t* arg = argv[i];
549     if (_wcsicmp(arg, _T("/nativesplash")) == 0) continue;
550     env->SetObjectArrayElement(args, k++, env->NewString((const jchar *)arg, wcslen(argv[i])));
551   }
552   return args;
553 }
554
555 bool RunMainClass()
556 {
557   std::string mainClassName = LoadStdString(IDS_MAIN_CLASS);
558   jclass mainClass = env->FindClass(mainClassName.c_str());
559   if (!mainClass)
560   {
561     char buf[_MAX_PATH + 256];
562     sprintf_s(buf, "Could not find main class %s", mainClassName.c_str());
563     std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
564     MessageBoxA(NULL, buf, error.c_str(), MB_OK);
565     return false;
566   }
567
568   jmethodID mainMethod = env->GetStaticMethodID(mainClass, "main", "([Ljava/lang/String;)V");
569   if (!mainMethod)
570   {
571     std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
572     MessageBoxA(NULL, "Could not find main method", error.c_str(), MB_OK);
573     return false;
574   }
575
576   jobjectArray args = PrepareCommandLine();
577   env->CallStaticVoidMethod(mainClass, mainMethod, args);
578   jthrowable exc = env->ExceptionOccurred();
579   if (exc)
580   {
581     std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP);
582     MessageBoxA(NULL, "Error invoking main method", error.c_str(), MB_OK);
583   }
584
585   return true;
586 }
587
588 void CallCommandLineProcessor(const std::wstring& curDir, const std::wstring& args)
589 {
590   JNIEnv *env;
591   JavaVMAttachArgs attachArgs;
592   attachArgs.version = JNI_VERSION_1_2;
593   attachArgs.name = "WinLauncher external command processing thread";
594   attachArgs.group = NULL;
595   jvm->AttachCurrentThread((void**)&env, &attachArgs);
596
597   std::string processorClassName = LoadStdString(IDS_COMMAND_LINE_PROCESSOR_CLASS);
598   jclass processorClass = env->FindClass(processorClassName.c_str());
599   if (processorClass)
600   {
601     jmethodID processMethodID = env->GetStaticMethodID(processorClass, "processWindowsLauncherCommandLine", "(Ljava/lang/String;Ljava/lang/String;)V");
602     if (processMethodID)
603     {
604       jstring jCurDir = env->NewString((const jchar *)curDir.c_str(), curDir.size());
605       jstring jArgs = env->NewString((const jchar *)args.c_str(), args.size());
606       env->CallStaticVoidMethod(processorClass, processMethodID, jCurDir, jArgs);
607       jthrowable exc = env->ExceptionOccurred();
608       if (exc)
609       {
610         MessageBox(NULL, _T("Error sending command line to existing instance"), _T("Error"), MB_OK);
611       }
612     }
613   }
614
615   jvm->DetachCurrentThread();
616 }
617
618 DWORD WINAPI SingleInstanceThread(LPVOID args)
619 {
620   while (true)
621   {
622     WaitForSingleObject(hEvent, INFINITE);
623     if (terminating) break;
624
625     wchar_t *view = static_cast<wchar_t *>(MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0));
626     if (!view) continue;
627     std::wstring command(view);
628     int pos = command.find('\n');
629     if (pos >= 0)
630     {
631       std::wstring curDir = command.substr(0, pos);
632       std::wstring args = command.substr(pos + 1);
633
634       CallCommandLineProcessor(curDir, args);
635     }
636
637     UnmapViewOfFile(view);
638   }
639   return 0;
640 }
641
642 void SendCommandLineToFirstInstance()
643 {
644   wchar_t curDir[_MAX_PATH];
645   GetCurrentDirectoryW(_MAX_PATH - 1, curDir);
646   std::wstring command(curDir);
647   command += _T("\n");
648   command += GetCommandLineW();
649
650   void *view = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
651   if (view)
652   {
653     memcpy(view, command.c_str(), (command.size() + 1) * sizeof(wchar_t));
654     UnmapViewOfFile(view);
655   }
656   SetEvent(hEvent);
657 }
658
659 bool CheckSingleInstance()
660 {
661   char moduleFileName[_MAX_PATH];
662   GetModuleFileNameA(NULL, moduleFileName, _MAX_PATH - 1);
663   for (char *p = moduleFileName; *p; p++)
664   {
665     if (*p == ':' || *p == '\\') *p = '_';
666   }
667   std::string mappingName = std::string("IntelliJLauncherMapping.") + moduleFileName;
668   std::string eventName = std::string("IntelliJLauncherEvent.") + moduleFileName;
669
670   hEvent = CreateEventA(NULL, FALSE, FALSE, eventName.c_str());
671
672   hFileMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, mappingName.c_str());
673   if (!hFileMapping)
674   {
675     hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, FILE_MAPPING_SIZE,
676       mappingName.c_str());
677     return true;
678   }
679   else
680   {
681     SendCommandLineToFirstInstance();
682     CloseHandle(hFileMapping);
683     CloseHandle(hEvent);
684     return false;
685   }
686 }
687
688 void DrawSplashImage(HWND hWnd)
689 {
690   HBITMAP hSplashBitmap = (HBITMAP)GetWindowLongPtr(hWnd, GWLP_USERDATA);
691   PAINTSTRUCT ps;
692   HDC hDC = BeginPaint(hWnd, &ps);
693   HDC hMemDC = CreateCompatibleDC(hDC);
694   HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hSplashBitmap);
695   BITMAP splashBitmap;
696   GetObject(hSplashBitmap, sizeof(splashBitmap), &splashBitmap);
697   BitBlt(hDC, 0, 0, splashBitmap.bmWidth, splashBitmap.bmHeight, hMemDC, 0, 0, SRCCOPY);
698   SelectObject(hMemDC, hOldBmp);
699   DeleteDC(hMemDC);
700   EndPaint(hWnd, &ps);
701 }
702
703 LRESULT CALLBACK SplashScreenWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
704 {
705   switch (uMsg)
706   {
707   case WM_PAINT:
708     DrawSplashImage(hWnd);
709     break;
710   }
711   return DefWindowProc(hWnd, uMsg, wParam, lParam);
712 }
713
714 const TCHAR splashClassName[] = _T("IntelliJLauncherSplash");
715
716 void RegisterSplashScreenWndClass()
717 {
718   WNDCLASSEX wcx;
719   wcx.cbSize = sizeof(wcx);
720   wcx.style = 0;
721   wcx.lpfnWndProc = SplashScreenWndProc;
722   wcx.cbClsExtra = 0;
723   wcx.cbWndExtra = 0;
724   wcx.hInstance = hInst;
725   wcx.hIcon = 0;
726   wcx.hCursor = LoadCursor(NULL, IDC_WAIT);
727   wcx.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
728   wcx.lpszMenuName = 0;
729   wcx.lpszClassName = splashClassName;
730   wcx.hIconSm = 0;
731
732   RegisterClassEx(&wcx);
733 }
734
735 HWND ShowSplashScreenWindow(HBITMAP hSplashBitmap)
736 {
737   RECT workArea;
738   SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
739   BITMAP splashBitmap;
740   GetObject(hSplashBitmap, sizeof(splashBitmap), &splashBitmap);
741   int x = workArea.left + ((workArea.right - workArea.left) - splashBitmap.bmWidth) / 2;
742   int y = workArea.top + ((workArea.bottom - workArea.top) - splashBitmap.bmHeight) / 2;
743
744   HWND splashWindow = CreateWindowEx(WS_EX_TOOLWINDOW, splashClassName, splashClassName, WS_POPUP,
745     x, y, splashBitmap.bmWidth, splashBitmap.bmHeight, NULL, NULL, NULL, NULL);
746   SetWindowLongPtr(splashWindow, GWLP_USERDATA, (LONG_PTR)hSplashBitmap);
747   ShowWindow(splashWindow, SW_SHOW);
748   UpdateWindow(splashWindow);
749   return splashWindow;
750 }
751
752 DWORD parentProcId;
753 HANDLE parentProcHandle;
754
755 BOOL IsParentProcessRunning(HANDLE hProcess)
756 {
757   if (hProcess == NULL) return FALSE;
758   DWORD ret = WaitForSingleObject(hProcess, 0);
759   return ret == WAIT_TIMEOUT;
760 }
761
762 BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
763 {
764   DWORD procId = 0;
765   GetWindowThreadProcessId(hWnd, &procId);
766   if (parentProcId == procId)
767   {
768     WINDOWINFO wi;
769     wi.cbSize = sizeof(WINDOWINFO);
770     GetWindowInfo(hWnd, &wi);
771     if ((wi.dwStyle & WS_VISIBLE) != 0)
772     {
773       HWND *phNewWindow = (HWND *)lParam;
774       *phNewWindow = hWnd;
775       return FALSE;
776     }
777   }
778   return TRUE;
779 }
780
781 DWORD WINAPI SplashScreen(HBITMAP hSplashBitmap)
782 {
783   RegisterSplashScreenWndClass();
784   HWND splashWindow = ShowSplashScreenWindow(hSplashBitmap);
785   MSG msg;
786   while (true)
787   {
788     while (PeekMessage(&msg, splashWindow, 0, 0, PM_REMOVE))
789     {
790       TranslateMessage(&msg);
791       DispatchMessage(&msg);
792     }
793     Sleep(50);
794     HWND hNewWindow = NULL;
795     EnumWindows(EnumWindowsProc, (LPARAM)&hNewWindow);
796     if (hNewWindow)
797     {
798       BringWindowToTop(hNewWindow);
799       Sleep(100);
800       DeleteObject(hSplashBitmap);
801       DestroyWindow(splashWindow);
802       break;
803     }
804     if (!IsParentProcessRunning(parentProcHandle)) break;
805   }
806   return 0;
807 }
808
809 void StartSplashProcess()
810 {
811   TCHAR ownPath[_MAX_PATH];
812   TCHAR params[_MAX_PATH];
813
814   PROCESS_INFORMATION splashProcessInformation;
815   STARTUPINFO startupInfo;
816   memset(&splashProcessInformation, 0, sizeof(splashProcessInformation));
817   memset(&startupInfo, 0, sizeof(startupInfo));
818   startupInfo.cb = sizeof(startupInfo);
819   startupInfo.dwFlags = STARTF_USESHOWWINDOW;
820   startupInfo.wShowWindow = SW_SHOW;
821
822   GetModuleFileName(NULL, ownPath, (sizeof(ownPath)));
823   _snwprintf(params, _MAX_PATH, _T("SPLASH %d"), GetCurrentProcessId());
824   if (CreateProcess(ownPath, params, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &splashProcessInformation))
825   {
826     CloseHandle(splashProcessInformation.hProcess);
827     CloseHandle(splashProcessInformation.hThread);
828   }
829 }
830
831 int APIENTRY _tWinMain(HINSTANCE hInstance,
832                        HINSTANCE hPrevInstance,
833                        LPTSTR    lpCmdLine,
834                        int       nCmdShow)
835 {
836   UNREFERENCED_PARAMETER(hPrevInstance);
837
838   hInst = hInstance;
839
840   if (__argc == 2 && _wcsicmp(__wargv[0], _T("SPLASH")) == 0)
841   {
842     HBITMAP hSplashBitmap = static_cast<HBITMAP>(LoadImage(hInst, MAKEINTRESOURCE(IDB_SPLASH), IMAGE_BITMAP, 0, 0, 0));
843     if (hSplashBitmap)
844     {
845       parentProcId = _wtoi(__wargv[1]);
846       parentProcHandle = OpenProcess(SYNCHRONIZE, FALSE, parentProcId);
847       if (IsParentProcessRunning(parentProcHandle)) SplashScreen(hSplashBitmap);
848     }
849     CloseHandle(parentProcHandle);
850     return 0;
851   }
852
853   if (!CheckSingleInstance()) return 1;
854
855   if (nativesplash = wcsstr(lpCmdLine, _T("/nativesplash")) != NULL) StartSplashProcess();
856
857   if (!LocateJVM()) return 1;
858   if (!LoadVMOptions()) return 1;
859   if (!LoadJVMLibrary()) return 1;
860   if (!CreateJVM()) return 1;
861
862   hSingleInstanceWatcherThread = CreateThread(NULL, 0, SingleInstanceThread, NULL, 0, NULL);
863
864   if (!RunMainClass()) return 1;
865
866   jvm->DestroyJavaVM();
867
868   terminating = true;
869   SetEvent(hEvent);
870   WaitForSingleObject(hSingleInstanceWatcherThread, INFINITE);
871   CloseHandle(hEvent);
872   CloseHandle(hFileMapping);
873
874   return 0;
875 }