root/nt/runemacs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. WinMain
  2. set_user_model_id
  3. ensure_unicows_dll

     1 /* runemacs --- Simple program to start Emacs with its console window hidden.
     2 
     3 Copyright (C) 2001-2023 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 (at
    10 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 <https://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 #define DEFER_MS_W32_H
    44 #include <config.h>
    45 
    46 #include <windows.h>
    47 #include <string.h>
    48 #include <malloc.h>
    49 
    50 static void set_user_model_id (void);
    51 static int ensure_unicows_dll (void);
    52 
    53 int WINAPI
    54 WinMain (HINSTANCE hSelf, HINSTANCE hPrev, LPSTR cmdline, int nShow)
    55 {
    56   STARTUPINFO start;
    57   SECURITY_ATTRIBUTES sec_attrs;
    58   PROCESS_INFORMATION child;
    59   int wait_for_child = FALSE;
    60   DWORD priority_class = NORMAL_PRIORITY_CLASS;
    61   DWORD ret_code = 0;
    62   char *new_cmdline;
    63   char *p;
    64   char modname[MAX_PATH];
    65   static const char iconic_opt[] = "--iconic ", maximized_opt[] = "--maximized ";
    66 
    67   if (!ensure_unicows_dll ())
    68     goto error;
    69 
    70   set_user_model_id ();
    71 
    72   if (!GetModuleFileName (NULL, modname, MAX_PATH))
    73     goto error;
    74   if ((p = strrchr (modname, '\\')) == NULL)
    75     goto error;
    76   *p = 0;
    77 
    78   new_cmdline = alloca (MAX_PATH
    79                         + strlen (cmdline)
    80                         + ((nShow == SW_SHOWMINNOACTIVE
    81                             || nShow == SW_SHOWMAXIMIZED)
    82                            ? max (sizeof (iconic_opt), sizeof (maximized_opt))
    83                            : 0)
    84                         + 3);
    85   /* Quote executable name in case of spaces in the path. */
    86   *new_cmdline = '"';
    87   strcpy (new_cmdline + 1, modname);
    88   /* Detect and handle un-installed runemacs.exe in nt/ subdirectory,
    89      while emacs.exe is in src/.  */
    90   if ((p = strrchr (new_cmdline, '\\')) != NULL
    91       && stricmp (p, "\\nt") == 0)
    92     strcpy (p, "\\src");
    93 
    94 #ifdef CHOOSE_NEWEST_EXE
    95   {
    96     /* Silly hack to allow new versions to be installed on
    97        server even when current version is in use. */
    98 
    99     char * best_name = alloca (MAX_PATH + 1);
   100     FILETIME best_time = {0,0};
   101     WIN32_FIND_DATA wfd;
   102     HANDLE fh;
   103     p = new_cmdline + strlen (new_cmdline);
   104     strcpy (p, "\\emacs*.exe\" ");
   105     fh = FindFirstFile (new_cmdline, &wfd);
   106     if (fh == INVALID_HANDLE_VALUE)
   107       goto error;
   108     do
   109       {
   110         if (wfd.ftLastWriteTime.dwHighDateTime > best_time.dwHighDateTime
   111             || (wfd.ftLastWriteTime.dwHighDateTime == best_time.dwHighDateTime
   112                 && wfd.ftLastWriteTime.dwLowDateTime > best_time.dwLowDateTime))
   113           {
   114             best_time = wfd.ftLastWriteTime;
   115             strcpy (best_name, wfd.cFileName);
   116           }
   117       }
   118     while (FindNextFile (fh, &wfd));
   119     FindClose (fh);
   120     *p++ = '\\';
   121     strcpy (p, best_name);
   122     strcat (p, " ");
   123   }
   124 #else
   125   strcat (new_cmdline, "\\emacs.exe\" ");
   126 #endif
   127 
   128   /* Append original arguments if any; first look for arguments we
   129      recognize (-wait, -high, and -low), and apply them ourselves.  */
   130   while (cmdline[0] == '-' || cmdline[0] == '/')
   131     {
   132       if (strncmp (cmdline+1, "wait", 4) == 0)
   133         {
   134           wait_for_child = TRUE;
   135           cmdline += 5;
   136         }
   137       else if (strncmp (cmdline+1, "high", 4) == 0)
   138         {
   139           priority_class = HIGH_PRIORITY_CLASS;
   140           cmdline += 5;
   141         }
   142       else if (strncmp (cmdline+1, "low", 3) == 0)
   143         {
   144           priority_class = IDLE_PRIORITY_CLASS;
   145           cmdline += 4;
   146         }
   147       else
   148         break;
   149       /* Look for next argument.  */
   150       while (*++cmdline == ' ');
   151     }
   152 
   153   /* If the desktop shortcut properties tell to invoke runemacs
   154      minimized, or if they invoked runemacs via "start /min", pass
   155      '--iconic' to Emacs, as that's what users will expect.  Likewise
   156      with invoking runemacs maximized: pass '--maximized' to Emacs.  */
   157   if (nShow == SW_SHOWMINNOACTIVE)
   158     strcat (new_cmdline, iconic_opt);
   159   else if (nShow == SW_SHOWMAXIMIZED)
   160     strcat (new_cmdline, maximized_opt);
   161   strcat (new_cmdline, cmdline);
   162 
   163   /* Set emacs_dir variable if runemacs was in "%emacs_dir%\bin".  */
   164   if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0)
   165     {
   166       *p = 0;
   167       for (p = modname; *p; p++)
   168         if (*p == '\\') *p = '/';
   169       SetEnvironmentVariable ("emacs_dir", modname);
   170     }
   171 
   172   memset (&start, 0, sizeof (start));
   173   start.cb = sizeof (start);
   174   start.dwFlags = STARTF_USESHOWWINDOW | STARTF_USECOUNTCHARS;
   175   start.wShowWindow = SW_HIDE;
   176   /* Ensure that we don't waste memory if the user has specified a huge
   177      default screen buffer for command windows.  */
   178   start.dwXCountChars = 80;
   179   start.dwYCountChars = 25;
   180 
   181   sec_attrs.nLength = sizeof (sec_attrs);
   182   sec_attrs.lpSecurityDescriptor = NULL;
   183   sec_attrs.bInheritHandle = FALSE;
   184 
   185   if (CreateProcess (NULL, new_cmdline, &sec_attrs, NULL, TRUE, priority_class,
   186                      NULL, NULL, &start, &child))
   187     {
   188       if (wait_for_child)
   189         {
   190           WaitForSingleObject (child.hProcess, INFINITE);
   191           GetExitCodeProcess (child.hProcess, &ret_code);
   192         }
   193       CloseHandle (child.hThread);
   194       CloseHandle (child.hProcess);
   195     }
   196   else
   197     goto error;
   198   return (int) ret_code;
   199 
   200 error:
   201   MessageBox (NULL, "Could not start Emacs.", "Error", MB_ICONSTOP);
   202   return 1;
   203 }
   204 
   205 void
   206 set_user_model_id (void)
   207 {
   208   HMODULE shell;
   209   HRESULT (WINAPI * set_user_model) (const wchar_t * id);
   210 
   211   /* On Windows 7 and later, we need to set the user model ID
   212      to associate emacsclient launched files with Emacs frames
   213      in the UI.  */
   214   shell = LoadLibrary ("shell32.dll");
   215   if (shell)
   216     {
   217       set_user_model
   218         = (void *) GetProcAddress (shell,
   219                                    "SetCurrentProcessExplicitAppUserModelID");
   220 
   221       /* If the function is defined, then we are running on Windows 7
   222          or newer, and the UI uses this to group related windows
   223          together.  Since emacs, runemacs, emacsclient are related, we
   224          want them grouped even though the executables are different,
   225          so we need to set a consistent ID between them.  */
   226       if (set_user_model)
   227         set_user_model (L"GNU.Emacs");
   228 
   229       FreeLibrary (shell);
   230     }
   231 }
   232 
   233 static int
   234 ensure_unicows_dll (void)
   235 {
   236   OSVERSIONINFO os_ver;
   237   HMODULE h;
   238 
   239   ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
   240   os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
   241   if (!GetVersionEx (&os_ver))
   242     return 0;
   243 
   244   if (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
   245     {
   246       h = LoadLibrary ("Unicows.dll");
   247       if (!h)
   248         {
   249           int button;
   250 
   251           button = MessageBox (NULL,
   252                                "Emacs cannot load the UNICOWS.DLL library.\n"
   253                                "This library is essential for using Emacs\n"
   254                                "on this system.  You need to install it.\n\n"
   255                                "Emacs will exit when you click OK.",
   256                                "Emacs cannot load UNICOWS.DLL",
   257                                MB_ICONERROR | MB_TASKMODAL
   258                                | MB_SETFOREGROUND | MB_OK);
   259           switch (button)
   260             {
   261               case IDOK:
   262               default:
   263                 return 0;
   264             }
   265         }
   266       FreeLibrary (h);
   267       return 1;
   268     }
   269   return 1;
   270 }

/* [<][>][^][v][top][bottom][index][help] */