]> code.delx.au - gnu-emacs/blob - nt/addpm.c
* lisp/net/tramp-gvfs.el (tramp-gvfs-mount-spec): Fix typo.
[gnu-emacs] / nt / addpm.c
1 /* Add entries to the GNU Emacs Program Manager folder.
2 Copyright (C) 1995, 2001-2016 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
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.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19 /****************************************************************************
20 *
21 * Program: addpm (adds emacs to the Windows program manager)
22 *
23 * Usage:
24 * argv[1] = install path for emacs
25 *
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.
30 */
31
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>
40
41 /* MinGW64 barfs if _WIN32_IE is defined to anything below 0x500. */
42 #ifndef MINGW_W64
43 #define _WIN32_IE 0x400
44 #endif
45 /* Request C Object macros for COM interfaces. */
46 #define COBJMACROS 1
47
48 #include <windows.h>
49 #include <shlobj.h>
50 #include <ddeml.h>
51
52 #ifndef OLD_PATHS
53 #include "../src/epaths.h"
54 #endif
55
56 HDDEDATA CALLBACK
57 DdeCallback (UINT uType, UINT uFmt, HCONV hconv,
58 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
59 DWORD dwData1, DWORD dwData2)
60 {
61 return ((HDDEDATA) NULL);
62 }
63
64 #define DdeCommand(str) \
65 DdeClientTransaction ((LPBYTE)str, strlen (str)+1, conversation, (HSZ)NULL, \
66 CF_TEXT, XTYP_EXECUTE, 30000, NULL)
67
68 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
69 #define REG_APP_PATH \
70 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\emacs.exe"
71
72 static struct entry
73 {
74 const char *name;
75 const char *value;
76 }
77 env_vars[] =
78 {
79 #ifdef OLD_PATHS
80 {"emacs_dir", NULL},
81 {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp"},
82 {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
83 {"EMACSDATA", "%emacs_dir%/etc"},
84 {"EMACSPATH", "%emacs_dir%/bin"},
85 /* We no longer set INFOPATH because Info-default-directory-list
86 is then ignored. */
87 /* {"INFOPATH", "%emacs_dir%/info"}, */
88 {"EMACSDOC", "%emacs_dir%/etc"},
89 {"TERM", "cmd"}
90 #else /* !OLD_PATHS */
91 {"emacs_dir", NULL},
92 {"EMACSLOADPATH", PATH_SITELOADSEARCH ";" PATH_LOADSEARCH},
93 {"SHELL", PATH_EXEC "/cmdproxy.exe"},
94 {"EMACSDATA", PATH_DATA},
95 {"EMACSPATH", PATH_EXEC},
96 /* We no longer set INFOPATH because Info-default-directory-list
97 is then ignored. */
98 /* {"INFOPATH", "%emacs_dir%/info"}, */
99 {"EMACSDOC", PATH_DOC},
100 {"TERM", "cmd"}
101 #endif
102 };
103
104 void
105 add_registry (const char *path)
106 {
107 HKEY hrootkey = NULL;
108 int i;
109
110 /* Record the location of Emacs to the App Paths key if we have
111 sufficient permissions to do so. This helps Windows find emacs quickly
112 if the user types emacs.exe in the "Run Program" dialog without having
113 emacs on their path. Some applications also use the same registry key
114 to discover the installation directory for programs they are looking for.
115 Multiple installations cannot be handled by this method, but it does not
116 affect the general operation of other installations of Emacs, and we
117 are blindly overwriting the Start Menu entries already.
118 */
119 if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, REG_APP_PATH, 0, NULL,
120 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
121 &hrootkey, NULL) == ERROR_SUCCESS)
122 {
123 int len;
124 char *emacs_path;
125
126 len = strlen (path) + 15; /* \bin\emacs.exe + terminator. */
127 emacs_path = (char *) alloca (len);
128 sprintf (emacs_path, "%s\\bin\\emacs.exe", path);
129
130 RegSetValueEx (hrootkey, NULL, 0, REG_EXPAND_SZ, emacs_path, len);
131 RegCloseKey (hrootkey);
132 }
133
134 /* Previous versions relied on registry settings, but we do not need
135 them any more. If registry settings are installed from a previous
136 version, replace them to ensure they are the current settings.
137 Otherwise, do nothing. */
138
139 /* Check both the current user and the local machine to see if we
140 have any resources. */
141
142 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0,
143 KEY_WRITE | KEY_QUERY_VALUE, &hrootkey) != ERROR_SUCCESS
144 && RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0,
145 KEY_WRITE | KEY_QUERY_VALUE, &hrootkey) != ERROR_SUCCESS)
146 return;
147
148 for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
149 {
150 const char * value = env_vars[i].value ? env_vars[i].value : path;
151
152 /* Replace only those settings that already exist. */
153 if (RegQueryValueEx (hrootkey, env_vars[i].name, NULL,
154 NULL, NULL, NULL) == ERROR_SUCCESS)
155 RegSetValueEx (hrootkey, env_vars[i].name, 0, REG_EXPAND_SZ,
156 value, lstrlen (value) + 1);
157 }
158
159 RegCloseKey (hrootkey);
160 }
161
162 int
163 main (int argc, char *argv[])
164 {
165 char start_folder[MAX_PATH + 1];
166 int shortcuts_created = 0;
167 int com_available = 1;
168 char modname[MAX_PATH];
169 const char *prog_name;
170 const char *emacs_path;
171 char *p;
172 int quiet = 0;
173 HRESULT result;
174 IShellLinkA *shortcut;
175
176 /* If no args specified, use our location to set emacs_path. */
177
178 if (argc > 1
179 && (argv[1][0] == '/' || argv[1][0] == '-')
180 && argv[1][1] == 'q')
181 {
182 quiet = 1;
183 --argc;
184 ++argv;
185 }
186
187 if (argc > 1)
188 emacs_path = argv[1];
189 else
190 {
191 if (!GetModuleFileName (NULL, modname, MAX_PATH) ||
192 (p = strrchr (modname, '\\')) == NULL)
193 {
194 fprintf (stderr, "fatal error");
195 exit (1);
196 }
197 *p = 0;
198
199 /* Set emacs_path to emacs_dir if we are in "%emacs_dir%\bin". */
200 if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0)
201 {
202 *p = 0;
203 emacs_path = modname;
204 }
205 else
206 {
207 fprintf (stderr, "usage: addpm emacs_path\n");
208 exit (1);
209 }
210
211 /* Tell user what we are going to do. */
212 if (!quiet)
213 {
214 int result;
215
216 char msg[ MAX_PATH ];
217 sprintf (msg, "Install Emacs at %s?\n", emacs_path);
218 result = MessageBox (NULL, msg, "Install Emacs",
219 MB_OKCANCEL | MB_ICONQUESTION);
220 if (result != IDOK)
221 {
222 fprintf (stderr, "Install canceled\n");
223 exit (1);
224 }
225 }
226 }
227
228 add_registry (emacs_path);
229 prog_name = "runemacs.exe";
230
231 /* Try to install globally. */
232
233 if (!SUCCEEDED (CoInitialize (NULL))
234 || !SUCCEEDED (CoCreateInstance (&CLSID_ShellLink, NULL,
235 CLSCTX_INPROC_SERVER, &IID_IShellLinkA,
236 (void **) &shortcut)))
237 {
238 com_available = 0;
239 }
240
241 if (com_available
242 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_COMMON_PROGRAMS, 0))
243 {
244 if (strlen (start_folder) < (MAX_PATH - 20))
245 {
246 strcat (start_folder, "\\Gnu Emacs");
247 if (CreateDirectory (start_folder, NULL)
248 || GetLastError () == ERROR_ALREADY_EXISTS)
249 {
250 char full_emacs_path[MAX_PATH + 1];
251 IPersistFile *lnk;
252 strcat (start_folder, "\\Emacs.lnk");
253 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
254 IShellLinkA_SetPath (shortcut, full_emacs_path);
255 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
256 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
257 (void **) &lnk);
258 if (SUCCEEDED (result))
259 {
260 wchar_t unicode_path[MAX_PATH];
261 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
262 unicode_path, MAX_PATH);
263 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
264 shortcuts_created = 1;
265 IPersistFile_Release (lnk);
266 }
267 }
268 }
269 }
270
271 if (!shortcuts_created && com_available
272 && SHGetSpecialFolderPath (NULL, start_folder, CSIDL_PROGRAMS, 0))
273 {
274 /* Ensure there is enough room for "...\GNU Emacs\Emacs.lnk". */
275 if (strlen (start_folder) < (MAX_PATH - 20))
276 {
277 strcat (start_folder, "\\Gnu Emacs");
278 if (CreateDirectory (start_folder, NULL)
279 || GetLastError () == ERROR_ALREADY_EXISTS)
280 {
281 char full_emacs_path[MAX_PATH + 1];
282 IPersistFile *lnk;
283 strcat (start_folder, "\\Emacs.lnk");
284 sprintf (full_emacs_path, "%s\\bin\\%s", emacs_path, prog_name);
285 IShellLinkA_SetPath (shortcut, full_emacs_path);
286 IShellLinkA_SetDescription (shortcut, "GNU Emacs");
287 result = IShellLinkA_QueryInterface (shortcut, &IID_IPersistFile,
288 (void **) &lnk);
289 if (SUCCEEDED (result))
290 {
291 wchar_t unicode_path[MAX_PATH];
292 MultiByteToWideChar (CP_ACP, 0, start_folder, -1,
293 unicode_path, MAX_PATH);
294 if (SUCCEEDED (IPersistFile_Save (lnk, unicode_path, TRUE)))
295 shortcuts_created = 1;
296 IPersistFile_Release (lnk);
297
298 }
299 }
300 }
301 }
302
303 if (com_available)
304 IShellLinkA_Release (shortcut);
305
306 /* Need to call uninitialize, even if ComInitialize failed. */
307 CoUninitialize ();
308
309 /* Fallback on old DDE method if the above failed. */
310 if (!shortcuts_created)
311 {
312 DWORD dde = 0;
313 HCONV conversation;
314 HSZ progman;
315 char add_item[MAX_PATH*2 + 100];
316
317 DdeInitialize (&dde, (PFNCALLBACK) DdeCallback, APPCMD_CLIENTONLY, 0);
318 progman = DdeCreateStringHandle (dde, "PROGMAN", CP_WINANSI);
319 conversation = DdeConnect (dde, progman, progman, NULL);
320 if (conversation)
321 {
322 DdeCommand ("[CreateGroup (\"Gnu Emacs\")]");
323 DdeCommand ("[ReplaceItem (Emacs)]");
324 sprintf (add_item, "[AddItem (\"%s\\bin\\%s\", Emacs)]",
325 emacs_path, prog_name);
326 DdeCommand (add_item);
327
328 DdeDisconnect (conversation);
329 }
330
331 DdeFreeStringHandle (dde, progman);
332 DdeUninitialize (dde);
333 }
334
335 return 0;
336 }