Allow packages to change the hl-line overlay priority
[emacs.git] / nt / addpm.c
blobf54a6ea9f7c07cd8181008d347620157d1901f13
1 /* Add entries to the GNU Emacs Program Manager folder.
2 Copyright (C) 1995, 2001-2021 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19 /****************************************************************************
21 * Program: addpm (adds emacs to the Windows program manager)
23 * Usage:
24 * argv[1] = install path for emacs
26 * argv[2] used to be an optional argument for setting the icon.
27 * But now Emacs has a professional looking icon of its own.
28 * If users really want to change it, they can go into the settings of
29 * the shortcut that is created and do it there.
32 /* Use parts of shell API that were introduced by the merge of IE4
33 into the desktop shell. If Windows 95 or NT4 users do not have IE4
34 installed, then the DDE fallback for creating icons the Windows 3.1
35 progman way will be used instead, but that is prone to lockups
36 caused by other applications not servicing their message queues. */
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <malloc.h>
41 /* MinGW64 barfs if _WIN32_IE is defined to anything below 0x0500. */
42 #ifndef MINGW_W64
43 # ifdef _WIN32_IE
44 # undef _WIN32_IE
45 # endif
46 #define _WIN32_IE 0x0400
47 #endif
48 /* Request C Object macros for COM interfaces. */
49 #define COBJMACROS 1
51 #include <windows.h>
52 #include <shlobj.h>
53 #include <ddeml.h>
55 #ifndef OLD_PATHS
56 #include "../src/epaths.h"
57 #endif
59 HDDEDATA CALLBACK DdeCallback (UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD_PTR,
60 DWORD_PTR);
62 HDDEDATA CALLBACK
63 DdeCallback (UINT uType, UINT uFmt, HCONV hconv,
64 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
65 DWORD_PTR dwData1, DWORD_PTR dwData2)
67 return ((HDDEDATA) NULL);
70 #define DdeCommand(str) \
71 DdeClientTransaction ((LPBYTE)str, strlen (str)+1, conversation, (HSZ)NULL, \
72 CF_TEXT, XTYP_EXECUTE, 30000, NULL)
74 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
75 #define REG_APP_PATH \
76 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\emacs.exe"
78 static struct entry
80 const char *name;
81 const char *value;
83 env_vars[] =
85 #ifdef OLD_PATHS
86 {"emacs_dir", NULL},
87 {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp"},
88 {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
89 {"EMACSDATA", "%emacs_dir%/etc"},
90 {"EMACSPATH", "%emacs_dir%/bin"},
91 /* We no longer set INFOPATH because Info-default-directory-list
92 is then ignored. */
93 /* {"INFOPATH", "%emacs_dir%/info"}, */
94 {"EMACSDOC", "%emacs_dir%/etc"},
95 {"TERM", "cmd"}
96 #else /* !OLD_PATHS */
97 {"emacs_dir", NULL},
98 {"EMACSLOADPATH", PATH_SITELOADSEARCH ";" PATH_LOADSEARCH},
99 {"SHELL", PATH_EXEC "/cmdproxy.exe"},
100 {"EMACSDATA", PATH_DATA},
101 {"EMACSPATH", PATH_EXEC},
102 /* We no longer set INFOPATH because Info-default-directory-list
103 is then ignored. */
104 /* {"INFOPATH", "%emacs_dir%/info"}, */
105 {"EMACSDOC", PATH_DOC},
106 {"TERM", "cmd"}
107 #endif
110 static void
111 add_registry (const char *path)
113 HKEY hrootkey = NULL;
114 int i;
116 /* Record the location of Emacs to the App Paths key if we have
117 sufficient permissions to do so. This helps Windows find emacs quickly
118 if the user types emacs.exe in the "Run Program" dialog without having
119 emacs on their path. Some applications also use the same registry key
120 to discover the installation directory for programs they are looking for.
121 Multiple installations cannot be handled by this method, but it does not
122 affect the general operation of other installations of Emacs, and we
123 are blindly overwriting the Start Menu entries already.
125 if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, REG_APP_PATH, 0, NULL,
126 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
127 &hrootkey, NULL) == ERROR_SUCCESS)
129 int len;
130 char *emacs_path;
132 len = strlen (path) + 15; /* \bin\emacs.exe + terminator. */
133 emacs_path = (char *) alloca (len);
134 sprintf (emacs_path, "%s\\bin\\emacs.exe", path);
136 RegSetValueEx (hrootkey, NULL, 0, REG_EXPAND_SZ, emacs_path, len);
137 RegCloseKey (hrootkey);
140 /* Previous versions relied on registry settings, but we do not need
141 them any more. If registry settings are installed from a previous
142 version, replace them to ensure they are the current settings.
143 Otherwise, do nothing. */
145 /* Check both the current user and the local machine to see if we
146 have any resources. */
148 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0,
149 KEY_WRITE | KEY_QUERY_VALUE, &hrootkey) != ERROR_SUCCESS
150 && RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0,
151 KEY_WRITE | KEY_QUERY_VALUE, &hrootkey) != ERROR_SUCCESS)
152 return;
154 for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
156 const char * value = env_vars[i].value ? env_vars[i].value : path;
158 /* Replace only those settings that already exist. */
159 if (RegQueryValueEx (hrootkey, env_vars[i].name, NULL,
160 NULL, NULL, NULL) == ERROR_SUCCESS)
161 RegSetValueEx (hrootkey, env_vars[i].name, 0, REG_EXPAND_SZ,
162 value, lstrlen (value) + 1);
165 RegCloseKey (hrootkey);
169 main (int argc, char *argv[])
171 char start_folder[MAX_PATH + 1];
172 int shortcuts_created = 0;
173 int com_available = 1;
174 char modname[MAX_PATH];
175 const char *prog_name;
176 const char *emacs_path;
177 char *p;
178 int quiet = 0;
179 HRESULT result;
180 IShellLinkA *shortcut;
182 /* If no args specified, use our location to set emacs_path. */
184 if (argc > 1
185 && (argv[1][0] == '/' || argv[1][0] == '-')
186 && argv[1][1] == 'q')
188 quiet = 1;
189 --argc;
190 ++argv;
193 if (argc > 1)
194 emacs_path = argv[1];
195 else
197 if (!GetModuleFileName (NULL, modname, MAX_PATH) ||
198 (p = strrchr (modname, '\\')) == NULL)
200 fprintf (stderr, "fatal error");
201 exit (1);
203 *p = 0;
205 /* Set emacs_path to emacs_dir if we are in "%emacs_dir%\bin". */
206 if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0)
208 *p = 0;
209 emacs_path = modname;
211 else
213 fprintf (stderr, "usage: addpm emacs_path\n");
214 exit (1);
217 /* Tell user what we are going to do. */
218 if (!quiet)
220 int result;
222 const char install_msg[] = "Install Emacs at %s?\n";
223 char msg[ MAX_PATH + sizeof (install_msg) ];
224 sprintf (msg, install_msg, emacs_path);
225 result = MessageBox (NULL, msg, "Install Emacs",
226 MB_OKCANCEL | MB_ICONQUESTION);
227 if (result != IDOK)
229 fprintf (stderr, "Install canceled\n");
230 exit (1);
235 add_registry (emacs_path);
236 prog_name = "runemacs.exe";
238 /* Try to install globally. */
240 if (!SUCCEEDED (CoInitialize (NULL))
241 || !SUCCEEDED (CoCreateInstance (&CLSID_ShellLink, NULL,
242 CLSCTX_INPROC_SERVER, &IID_IShellLinkA,
243 (void **) &shortcut)))
245 com_available = 0;
248 if (com_available
249 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_COMMON_PROGRAMS, 0))
251 if (strlen (start_folder) < (MAX_PATH - 20))
253 strcat (start_folder, "\\Gnu Emacs");
254 if (CreateDirectory (start_folder, NULL)
255 || GetLastError () == ERROR_ALREADY_EXISTS)
257 char full_emacs_path[MAX_PATH + 1];
258 IPersistFile *lnk;
259 strcat (start_folder, "\\Emacs.lnk");
260 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
261 IShellLinkA_SetPath (shortcut, full_emacs_path);
262 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
263 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
264 (void **) &lnk);
265 if (SUCCEEDED (result))
267 wchar_t unicode_path[MAX_PATH];
268 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
269 unicode_path, MAX_PATH);
270 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
271 shortcuts_created = 1;
272 IPersistFile_Release (lnk);
278 if (!shortcuts_created && com_available
279 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_PROGRAMS, 0))
281 /* Ensure there is enough room for "...\GNU Emacs\Emacs.lnk". */
282 if (strlen (start_folder) < (MAX_PATH - 20))
284 strcat (start_folder, "\\Gnu Emacs");
285 if (CreateDirectory (start_folder, NULL)
286 || GetLastError () == ERROR_ALREADY_EXISTS)
288 char full_emacs_path[MAX_PATH + 1];
289 IPersistFile *lnk;
290 strcat (start_folder, "\\Emacs.lnk");
291 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
292 IShellLinkA_SetPath (shortcut, full_emacs_path);
293 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
294 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
295 (void **) &lnk);
296 if (SUCCEEDED (result))
298 wchar_t unicode_path[MAX_PATH];
299 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
300 unicode_path, MAX_PATH);
301 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
302 shortcuts_created = 1;
303 IPersistFile_Release (lnk);
310 if (com_available)
311 IShellLinkA_Release (shortcut);
313 /* Need to call uninitialize, even if ComInitialize failed. */
314 CoUninitialize ();
316 /* Fallback on old DDE method if the above failed. */
317 if (!shortcuts_created)
319 DWORD dde = 0;
320 HCONV conversation;
321 HSZ progman;
322 char add_item[MAX_PATH*2 + 100];
324 DdeInitialize (&dde, (PFNCALLBACK) DdeCallback, APPCMD_CLIENTONLY, 0);
325 progman = DdeCreateStringHandle (dde, "PROGMAN", CP_WINANSI);
326 conversation = DdeConnect (dde, progman, progman, NULL);
327 if (conversation)
329 DdeCommand ("[CreateGroup (\"Gnu Emacs\")]");
330 DdeCommand ("[ReplaceItem (Emacs)]");
331 sprintf (add_item, "[AddItem (\"%s\\bin\\%s\", Emacs)]",
332 emacs_path, prog_name);
333 DdeCommand (add_item);
335 DdeDisconnect (conversation);
338 DdeFreeStringHandle (dde, progman);
339 DdeUninitialize (dde);
342 return 0;