| 1 | /* runemacs --- Simple program to start Emacs with its console window hidden. |
| 2 | |
| 3 | Copyright (C) 2001-2014 Free Software Foundation, Inc. |
| 4 | |
| 5 | This file is part of GNU Emacs. |
| 6 | |
| 7 | GNU Emacs is free software: you can redistribute it and/or modify |
| 8 | it under the terms of the GNU General Public License as published by |
| 9 | the Free Software Foundation, either version 3 of the License, or |
| 10 | (at your option) any later version. |
| 11 | |
| 12 | GNU Emacs is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | GNU General Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU General Public License |
| 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ |
| 19 | |
| 20 | |
| 21 | /* |
| 22 | Simple program to start Emacs with its console window hidden. |
| 23 | |
| 24 | This program is provided purely for convenience, since most users will |
| 25 | use Emacs in windowing (GUI) mode, and will not want to have an extra |
| 26 | console window lying around. */ |
| 27 | |
| 28 | /* |
| 29 | You may want to define this if you want to be able to install updated |
| 30 | emacs binaries even when other users are using the current version. |
| 31 | The problem with some file servers (notably Novell) is that an open |
| 32 | file cannot be overwritten, deleted, or even renamed. So if someone |
| 33 | is running emacs.exe already, you cannot install a newer version. |
| 34 | By defining CHOOSE_NEWEST_EXE, you can name your new emacs.exe |
| 35 | something else which matches "emacs*.exe", and runemacs will |
| 36 | automatically select the newest emacs executable in the bin directory. |
| 37 | (So you'll probably be able to delete the old version some hours/days |
| 38 | later). |
| 39 | */ |
| 40 | |
| 41 | /* #define CHOOSE_NEWEST_EXE */ |
| 42 | |
| 43 | #include <windows.h> |
| 44 | #include <string.h> |
| 45 | #include <malloc.h> |
| 46 | |
| 47 | static void set_user_model_id (void); |
| 48 | static int ensure_unicows_dll (void); |
| 49 | |
| 50 | int WINAPI |
| 51 | WinMain (HINSTANCE hSelf, HINSTANCE hPrev, LPSTR cmdline, int nShow) |
| 52 | { |
| 53 | STARTUPINFO start; |
| 54 | SECURITY_ATTRIBUTES sec_attrs; |
| 55 | PROCESS_INFORMATION child; |
| 56 | int wait_for_child = FALSE; |
| 57 | DWORD priority_class = NORMAL_PRIORITY_CLASS; |
| 58 | DWORD ret_code = 0; |
| 59 | char *new_cmdline; |
| 60 | char *p; |
| 61 | char modname[MAX_PATH]; |
| 62 | |
| 63 | if (!ensure_unicows_dll ()) |
| 64 | goto error; |
| 65 | |
| 66 | set_user_model_id (); |
| 67 | |
| 68 | if (!GetModuleFileName (NULL, modname, MAX_PATH)) |
| 69 | goto error; |
| 70 | if ((p = strrchr (modname, '\\')) == NULL) |
| 71 | goto error; |
| 72 | *p = 0; |
| 73 | |
| 74 | new_cmdline = alloca (MAX_PATH + strlen (cmdline) + 3); |
| 75 | /* Quote executable name in case of spaces in the path. */ |
| 76 | *new_cmdline = '"'; |
| 77 | strcpy (new_cmdline + 1, modname); |
| 78 | /* Detect and handle un-installed runemacs.exe in nt/ subdirectory, |
| 79 | while emacs.exe is in src/. */ |
| 80 | if ((p = strrchr (new_cmdline, '\\')) != NULL |
| 81 | && stricmp (p, "\\nt") == 0) |
| 82 | strcpy (p, "\\src"); |
| 83 | |
| 84 | #ifdef CHOOSE_NEWEST_EXE |
| 85 | { |
| 86 | /* Silly hack to allow new versions to be installed on |
| 87 | server even when current version is in use. */ |
| 88 | |
| 89 | char * best_name = alloca (MAX_PATH + 1); |
| 90 | FILETIME best_time = {0,0}; |
| 91 | WIN32_FIND_DATA wfd; |
| 92 | HANDLE fh; |
| 93 | p = new_cmdline + strlen (new_cmdline); |
| 94 | strcpy (p, "\\emacs*.exe\" "); |
| 95 | fh = FindFirstFile (new_cmdline, &wfd); |
| 96 | if (fh == INVALID_HANDLE_VALUE) |
| 97 | goto error; |
| 98 | do |
| 99 | { |
| 100 | if (wfd.ftLastWriteTime.dwHighDateTime > best_time.dwHighDateTime |
| 101 | || (wfd.ftLastWriteTime.dwHighDateTime == best_time.dwHighDateTime |
| 102 | && wfd.ftLastWriteTime.dwLowDateTime > best_time.dwLowDateTime)) |
| 103 | { |
| 104 | best_time = wfd.ftLastWriteTime; |
| 105 | strcpy (best_name, wfd.cFileName); |
| 106 | } |
| 107 | } |
| 108 | while (FindNextFile (fh, &wfd)); |
| 109 | FindClose (fh); |
| 110 | *p++ = '\\'; |
| 111 | strcpy (p, best_name); |
| 112 | strcat (p, " "); |
| 113 | } |
| 114 | #else |
| 115 | strcat (new_cmdline, "\\emacs.exe\" "); |
| 116 | #endif |
| 117 | |
| 118 | /* Append original arguments if any; first look for arguments we |
| 119 | recognize (-wait, -high, and -low), and apply them ourselves. */ |
| 120 | while (cmdline[0] == '-' || cmdline[0] == '/') |
| 121 | { |
| 122 | if (strncmp (cmdline+1, "wait", 4) == 0) |
| 123 | { |
| 124 | wait_for_child = TRUE; |
| 125 | cmdline += 5; |
| 126 | } |
| 127 | else if (strncmp (cmdline+1, "high", 4) == 0) |
| 128 | { |
| 129 | priority_class = HIGH_PRIORITY_CLASS; |
| 130 | cmdline += 5; |
| 131 | } |
| 132 | else if (strncmp (cmdline+1, "low", 3) == 0) |
| 133 | { |
| 134 | priority_class = IDLE_PRIORITY_CLASS; |
| 135 | cmdline += 4; |
| 136 | } |
| 137 | else |
| 138 | break; |
| 139 | /* Look for next argument. */ |
| 140 | while (*++cmdline == ' '); |
| 141 | } |
| 142 | |
| 143 | strcat (new_cmdline, cmdline); |
| 144 | |
| 145 | /* Set emacs_dir variable if runemacs was in "%emacs_dir%\bin". */ |
| 146 | if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0) |
| 147 | { |
| 148 | *p = 0; |
| 149 | for (p = modname; *p; p++) |
| 150 | if (*p == '\\') *p = '/'; |
| 151 | SetEnvironmentVariable ("emacs_dir", modname); |
| 152 | } |
| 153 | |
| 154 | memset (&start, 0, sizeof (start)); |
| 155 | start.cb = sizeof (start); |
| 156 | start.dwFlags = STARTF_USESHOWWINDOW | STARTF_USECOUNTCHARS; |
| 157 | start.wShowWindow = SW_HIDE; |
| 158 | /* Ensure that we don't waste memory if the user has specified a huge |
| 159 | default screen buffer for command windows. */ |
| 160 | start.dwXCountChars = 80; |
| 161 | start.dwYCountChars = 25; |
| 162 | |
| 163 | sec_attrs.nLength = sizeof (sec_attrs); |
| 164 | sec_attrs.lpSecurityDescriptor = NULL; |
| 165 | sec_attrs.bInheritHandle = FALSE; |
| 166 | |
| 167 | if (CreateProcess (NULL, new_cmdline, &sec_attrs, NULL, TRUE, priority_class, |
| 168 | NULL, NULL, &start, &child)) |
| 169 | { |
| 170 | if (wait_for_child) |
| 171 | { |
| 172 | WaitForSingleObject (child.hProcess, INFINITE); |
| 173 | GetExitCodeProcess (child.hProcess, &ret_code); |
| 174 | } |
| 175 | CloseHandle (child.hThread); |
| 176 | CloseHandle (child.hProcess); |
| 177 | } |
| 178 | else |
| 179 | goto error; |
| 180 | return (int) ret_code; |
| 181 | |
| 182 | error: |
| 183 | MessageBox (NULL, "Could not start Emacs.", "Error", MB_ICONSTOP); |
| 184 | return 1; |
| 185 | } |
| 186 | |
| 187 | void |
| 188 | set_user_model_id (void) |
| 189 | { |
| 190 | HMODULE shell; |
| 191 | HRESULT (WINAPI * set_user_model) (wchar_t * id); |
| 192 | |
| 193 | /* On Windows 7 and later, we need to set the user model ID |
| 194 | to associate emacsclient launched files with Emacs frames |
| 195 | in the UI. */ |
| 196 | shell = LoadLibrary ("shell32.dll"); |
| 197 | if (shell) |
| 198 | { |
| 199 | set_user_model |
| 200 | = (void *) GetProcAddress (shell, |
| 201 | "SetCurrentProcessExplicitAppUserModelID"); |
| 202 | |
| 203 | /* If the function is defined, then we are running on Windows 7 |
| 204 | or newer, and the UI uses this to group related windows |
| 205 | together. Since emacs, runemacs, emacsclient are related, we |
| 206 | want them grouped even though the executables are different, |
| 207 | so we need to set a consistent ID between them. */ |
| 208 | if (set_user_model) |
| 209 | set_user_model (L"GNU.Emacs"); |
| 210 | |
| 211 | FreeLibrary (shell); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | static int |
| 216 | ensure_unicows_dll (void) |
| 217 | { |
| 218 | OSVERSIONINFO os_ver; |
| 219 | HMODULE h; |
| 220 | |
| 221 | ZeroMemory (&os_ver, sizeof (OSVERSIONINFO)); |
| 222 | os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); |
| 223 | if (!GetVersionEx (&os_ver)) |
| 224 | return 0; |
| 225 | |
| 226 | if (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) |
| 227 | { |
| 228 | h = LoadLibrary ("Unicows.dll"); |
| 229 | if (!h) |
| 230 | { |
| 231 | int button; |
| 232 | |
| 233 | button = MessageBox (NULL, |
| 234 | "Emacs cannot load the UNICOWS.DLL library.\n" |
| 235 | "This library is essential for using Emacs\n" |
| 236 | "on this system. You need to install it.\n\n" |
| 237 | "Emacs will exit when you click OK.", |
| 238 | "Emacs cannot load UNICOWS.DLL", |
| 239 | MB_ICONERROR | MB_TASKMODAL |
| 240 | | MB_SETFOREGROUND | MB_OK); |
| 241 | switch (button) |
| 242 | { |
| 243 | case IDOK: |
| 244 | default: |
| 245 | return 0; |
| 246 | } |
| 247 | } |
| 248 | FreeLibrary (h); |
| 249 | return 1; |
| 250 | } |
| 251 | return 1; |
| 252 | } |