2 * win32.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005 The Geany contributors
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
11 * This program 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 along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * Special functions for the win32 platform, to provide native dialogs.
29 /* Need Windows XP for SHGetFolderPathAndSubDirW */
30 #define _WIN32_WINNT 0x0501
31 /* Needed for SHGFP_TYPE */
32 #define _WIN32_IE 0x0500
41 #include "filetypes.h"
59 #include <glib/gstdio.h>
60 #include <gdk/gdkwin32.h>
63 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
64 gint
win32_check_write_permission(const gchar
*dir
)
66 static wchar_t w_dir
[MAX_PATH
];
67 MultiByteToWideChar(CP_UTF8
, 0, dir
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
68 if (_waccess(w_dir
, R_OK
| W_OK
) != 0)
75 /* Just a simple wrapper function to open a browser window */
76 void win32_open_browser(const gchar
*uri
)
79 if (strncmp(uri
, "file://", 7) == 0)
82 if (strchr(uri
, ':') != NULL
)
88 ret
= (gint
) ShellExecute(NULL
, "open", uri
, NULL
, NULL
, SW_SHOWNORMAL
);
91 gchar
*err
= g_win32_error_message(GetLastError());
92 ui_set_statusbar(TRUE
, _("Failed to open URI \"%s\": %s"), uri
, err
);
93 g_warning("ShellExecute failed opening \"%s\" (code %d): %s", uri
, ret
, err
);
99 static FILE *open_std_handle(DWORD handle
, const char *mode
)
105 lStdHandle
= GetStdHandle(handle
);
106 if (lStdHandle
== INVALID_HANDLE_VALUE
)
108 gchar
*err
= g_win32_error_message(GetLastError());
109 g_warning("GetStdHandle(%ld) failed: %s", (long)handle
, err
);
113 hConHandle
= _open_osfhandle((intptr_t)lStdHandle
, _O_TEXT
);
114 if (hConHandle
== -1)
116 gchar
*err
= g_win32_error_message(GetLastError());
117 g_warning("_open_osfhandle(handle(%ld), _O_TEXT) failed: %s", (long)handle
, err
);
121 fp
= _fdopen(hConHandle
, mode
);
124 gchar
*err
= g_win32_error_message(GetLastError());
125 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle
, mode
, err
);
129 if (setvbuf(fp
, NULL
, _IONBF
, 0) != 0)
131 gchar
*err
= g_win32_error_message(GetLastError());
132 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp
, err
);
142 static void debug_setup_console(void)
144 static const WORD MAX_CONSOLE_LINES
= 500;
145 CONSOLE_SCREEN_BUFFER_INFO coninfo
;
148 /* allocate a console for this app */
151 /* set the screen buffer to be big enough to let us scroll text */
152 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &coninfo
);
153 coninfo
.dwSize
.Y
= MAX_CONSOLE_LINES
;
154 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE
), coninfo
.dwSize
);
156 /* redirect unbuffered STDOUT to the console */
157 fp
= open_std_handle(STD_OUTPUT_HANDLE
, "w");
161 /* redirect unbuffered STDERR to the console */
162 fp
= open_std_handle(STD_ERROR_HANDLE
, "w");
166 /* redirect unbuffered STDIN to the console */
167 fp
= open_std_handle(STD_INPUT_HANDLE
, "r");
173 void win32_init_debug_code(void)
177 /* create a console window to get log messages on Windows,
178 * especially useful when generating tags files */
179 debug_setup_console();
184 /* expands environment placeholders in @str. input and output is in UTF-8 */
185 gchar
*win32_expand_environment_variables(const gchar
*str
)
187 wchar_t *cmdline
= g_utf8_to_utf16(str
, -1, NULL
, NULL
, NULL
);
188 wchar_t expCmdline
[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
189 gchar
*expanded
= NULL
;
191 if (cmdline
&& ExpandEnvironmentStringsW(cmdline
, expCmdline
, sizeof(expCmdline
)) != 0)
192 expanded
= g_utf16_to_utf8(expCmdline
, -1, NULL
, NULL
, NULL
);
196 return expanded
? expanded
: g_strdup(str
);
200 /* From GDK (they got it from MS Knowledge Base article Q130698) */
201 static gboolean
resolve_link(HWND hWnd
, wchar_t *link
, gchar
**lpszPath
)
203 WIN32_FILE_ATTRIBUTE_DATA wfad
;
205 IShellLinkW
*pslW
= NULL
;
206 IPersistFile
*ppf
= NULL
;
210 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
211 * with an empty file and returns an empty "link target". (#524151) */
212 if (!GetFileAttributesExW(link
, GetFileExInfoStandard
, &wfad
) ||
213 (wfad
.nFileSizeHigh
== 0 && wfad
.nFileSizeLow
== 0))
218 /* Assume failure to start with: */
223 hres
= CoCreateInstance(
224 &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IShellLinkW
, &pslWV
);
228 /* The IShellLink interface supports the IPersistFile interface.
229 * Get an interface pointer to it. */
230 pslW
= (IShellLinkW
*) pslWV
;
231 hres
= pslW
->lpVtbl
->QueryInterface(pslW
, &IID_IPersistFile
, &ppfV
);
237 ppf
= (IPersistFile
*) ppfV
;
238 hres
= ppf
->lpVtbl
->Load(ppf
, link
, STGM_READ
);
243 /* Resolve the link by calling the Resolve() interface function. */
244 hres
= pslW
->lpVtbl
->Resolve(pslW
, hWnd
, SLR_ANY_MATCH
| SLR_NO_UI
);
249 wchar_t wtarget
[MAX_PATH
];
251 hres
= pslW
->lpVtbl
->GetPath(pslW
, wtarget
, MAX_PATH
, NULL
, 0);
253 *lpszPath
= g_utf16_to_utf8(wtarget
, -1, NULL
, NULL
, NULL
);
257 ppf
->lpVtbl
->Release(ppf
);
260 pslW
->lpVtbl
->Release(pslW
);
262 return SUCCEEDED(hres
);
266 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
267 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
268 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
269 gchar
*win32_get_shortcut_target(const gchar
*file_name
)
272 wchar_t *wfilename
= g_utf8_to_utf16(file_name
, -1, NULL
, NULL
, NULL
);
275 if (main_widgets
.window
!= NULL
)
277 GdkWindow
*window
= gtk_widget_get_window(main_widgets
.window
);
279 hWnd
= GDK_WINDOW_HWND(window
);
282 resolve_link(hWnd
, wfilename
, &path
);
286 return g_strdup(file_name
);
292 void win32_set_working_directory(const gchar
*dir
)
294 SetCurrentDirectory(dir
);
298 gchar
*win32_get_installation_dir(void)
300 return g_win32_get_package_installation_directory_of_module(NULL
);
304 gchar
*win32_get_user_config_dir(void)
307 wchar_t path
[MAX_PATH
];
309 hr
= SHGetFolderPathAndSubDirW(NULL
, CSIDL_APPDATA
| CSIDL_FLAG_CREATE
, NULL
, SHGFP_TYPE_CURRENT
, L
"geany", path
);
312 // GLib always uses UTF-8 for filename encoding on Windows
313 int u8_size
= WideCharToMultiByte(CP_UTF8
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
316 gchar
*u8_path
= g_malloc0(u8_size
+ 1);
317 if (u8_path
!= NULL
&&
318 WideCharToMultiByte(CP_UTF8
, 0, path
, -1, u8_path
, u8_size
, NULL
, NULL
))
326 g_warning("Failed to retrieve Windows config dir, falling back to default");
327 return g_build_filename(g_get_user_config_dir(), "geany", NULL
);