Bump Emacs version to 26.2
[emacs.git] / nt / addpm.c
blobf71ce5f2385fdefd2bde1c6b8ad8ace2e0bcaf70
1 /* Add entries to the GNU Emacs Program Manager folder.
2 Copyright (C) 1995, 2001-2019 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,
60 DWORD);
62 HDDEDATA CALLBACK
63 DdeCallback (UINT uType, UINT uFmt, HCONV hconv,
64 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
65 DWORD dwData1, DWORD 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 char msg[ MAX_PATH ];
223 sprintf (msg, "Install Emacs at %s?\n", emacs_path);
224 result = MessageBox (NULL, msg, "Install Emacs",
225 MB_OKCANCEL | MB_ICONQUESTION);
226 if (result != IDOK)
228 fprintf (stderr, "Install canceled\n");
229 exit (1);
234 add_registry (emacs_path);
235 prog_name = "runemacs.exe";
237 /* Try to install globally. */
239 if (!SUCCEEDED (CoInitialize (NULL))
240 || !SUCCEEDED (CoCreateInstance (&CLSID_ShellLink, NULL,
241 CLSCTX_INPROC_SERVER, &IID_IShellLinkA,
242 (void **) &shortcut)))
244 com_available = 0;
247 if (com_available
248 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_COMMON_PROGRAMS, 0))
250 if (strlen (start_folder) < (MAX_PATH - 20))
252 strcat (start_folder, "\\Gnu Emacs");
253 if (CreateDirectory (start_folder, NULL)
254 || GetLastError () == ERROR_ALREADY_EXISTS)
256 char full_emacs_path[MAX_PATH + 1];
257 IPersistFile *lnk;
258 strcat (start_folder, "\\Emacs.lnk");
259 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
260 IShellLinkA_SetPath (shortcut, full_emacs_path);
261 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
262 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
263 (void **) &lnk);
264 if (SUCCEEDED (result))
266 wchar_t unicode_path[MAX_PATH];
267 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
268 unicode_path, MAX_PATH);
269 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
270 shortcuts_created = 1;
271 IPersistFile_Release (lnk);
277 if (!shortcuts_created && com_available
278 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_PROGRAMS, 0))
280 /* Ensure there is enough room for "...\GNU Emacs\Emacs.lnk". */
281 if (strlen (start_folder) < (MAX_PATH - 20))
283 strcat (start_folder, "\\Gnu Emacs");
284 if (CreateDirectory (start_folder, NULL)
285 || GetLastError () == ERROR_ALREADY_EXISTS)
287 char full_emacs_path[MAX_PATH + 1];
288 IPersistFile *lnk;
289 strcat (start_folder, "\\Emacs.lnk");
290 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
291 IShellLinkA_SetPath (shortcut, full_emacs_path);
292 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
293 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
294 (void **) &lnk);
295 if (SUCCEEDED (result))
297 wchar_t unicode_path[MAX_PATH];
298 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
299 unicode_path, MAX_PATH);
300 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
301 shortcuts_created = 1;
302 IPersistFile_Release (lnk);
309 if (com_available)
310 IShellLinkA_Release (shortcut);
312 /* Need to call uninitialize, even if ComInitialize failed. */
313 CoUninitialize ();
315 /* Fallback on old DDE method if the above failed. */
316 if (!shortcuts_created)
318 DWORD dde = 0;
319 HCONV conversation;
320 HSZ progman;
321 char add_item[MAX_PATH*2 + 100];
323 DdeInitialize (&dde, (PFNCALLBACK) DdeCallback, APPCMD_CLIENTONLY, 0);
324 progman = DdeCreateStringHandle (dde, "PROGMAN", CP_WINANSI);
325 conversation = DdeConnect (dde, progman, progman, NULL);
326 if (conversation)
328 DdeCommand ("[CreateGroup (\"Gnu Emacs\")]");
329 DdeCommand ("[ReplaceItem (Emacs)]");
330 sprintf (add_item, "[AddItem (\"%s\\bin\\%s\", Emacs)]",
331 emacs_path, prog_name);
332 DdeCommand (add_item);
334 DdeDisconnect (conversation);
337 DdeFreeStringHandle (dde, progman);
338 DdeUninitialize (dde);
341 return 0;