Merge PR#113
[geany-mirror.git] / src / win32.c
blob9fa9bc1f60a0f15bcb1f0a8c5a3d8a6adee3a418
1 /*
2 * win32.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program 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 2 of the License, or
10 * (at your option) any later version.
12 * This program 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.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * Special functions for the win32 platform, to provide native dialogs.
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include "win32.h"
32 #ifdef G_OS_WIN32
34 #include "dialogs.h"
35 #include "document.h"
36 #include "editor.h"
37 #include "filetypes.h"
38 #include "project.h"
39 #include "support.h"
40 #include "ui_utils.h"
41 #include "utils.h"
43 #include <ctype.h>
44 #include <fcntl.h>
45 #include <io.h>
46 #include <math.h>
47 #include <stdlib.h>
48 #include <string.h>
50 #define VC_EXTRALEAN
51 #define WIN32_LEAN_AND_MEAN
52 #include <windows.h>
53 #include <commdlg.h>
54 #include <shellapi.h>
55 #include <shlobj.h>
57 #include <glib/gstdio.h>
58 #include <gdk/gdkwin32.h>
60 #define BUFSIZE 4096
61 #define CMDSIZE 32768
63 struct _geany_win32_spawn
65 HANDLE hChildStdinRd;
66 HANDLE hChildStdinWr;
67 HANDLE hChildStdoutRd;
68 HANDLE hChildStdoutWr;
69 HANDLE hChildStderrRd;
70 HANDLE hChildStderrWr;
71 HANDLE hInputFile;
72 HANDLE hStdout;
73 HANDLE hStderr;
74 HANDLE processId;
75 DWORD dwExitCode;
77 typedef struct _geany_win32_spawn geany_win32_spawn;
79 static gboolean GetContentFromHandle(HANDLE hFile, gchar **content, GError **error);
80 static HANDLE GetTempFileHandle(GError **error);
81 static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline,
82 const TCHAR *dir, GError **error);
83 static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile, GError **error);
86 static wchar_t *get_file_filters(void)
88 gchar *string;
89 guint i, j, len;
90 static wchar_t title[4096];
91 GString *str = g_string_sized_new(100);
92 GString *all_patterns = g_string_sized_new(100);
93 GSList *node;
94 gchar *tmp;
96 /* create meta file filter "All files" */
97 g_string_append_printf(str, "%s\t*\t", _("All files"));
98 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
99 for (i = GEANY_FILETYPES_NONE + 1; i < filetypes_array->len; i++)
101 for (j = 0; filetypes[i]->pattern[j] != NULL; j++)
103 g_string_append(all_patterns, filetypes[i]->pattern[j]);
104 g_string_append_c(all_patterns, ';');
107 g_string_append_printf(str, "%s\t%s\t", _("All Source"), all_patterns->str);
108 g_string_free(all_patterns, TRUE);
109 /* add 'usual' filetypes */
110 foreach_slist(node, filetypes_by_title)
112 GeanyFiletype *ft = node->data;
114 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
115 continue;
116 tmp = g_strjoinv(";", ft->pattern);
117 g_string_append_printf(str, "%s\t%s\t", ft->title, tmp);
118 g_free(tmp);
120 g_string_append_c(str, '\t'); /* the final \0 byte to mark the end of the string */
121 string = str->str;
122 g_string_free(str, FALSE);
124 /* replace all "\t"s by \0 */
125 len = strlen(string);
126 g_strdelimit(string, "\t", '\0');
127 g_assert(string[len - 1] == 0x0);
128 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
129 g_free(string);
131 return title;
135 static wchar_t *get_file_filter_all_files(void)
137 guint len;
138 static wchar_t title[4096];
139 gchar *filter;
141 /* create meta file filter "All files" */
142 filter = g_strdup_printf("%s\t*\t", _("All files"));
144 len = strlen(filter);
145 g_strdelimit(filter, "\t", '\0');
146 g_assert(filter[len - 1] == 0x0);
147 MultiByteToWideChar(CP_UTF8, 0, filter, len, title, G_N_ELEMENTS(title));
148 g_free(filter);
150 return title;
154 static wchar_t *get_filters(gboolean project_files)
156 gchar *string;
157 gint len;
158 static wchar_t title[1024];
160 if (project_files)
162 string = g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT, "\t",
163 _("All files"), "\t", "*", "\t", NULL);
165 else
167 string = g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
168 _("All files"), "\t", "*", "\t", NULL);
171 /* replace all "\t"s by \0 */
172 len = strlen(string);
173 g_strdelimit(string, "\t", '\0');
174 g_assert(string[len - 1] == 0x0);
175 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
176 g_free(string);
178 return title;
182 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
183 * returns the directory part of the given filename. */
184 static wchar_t *get_dir_for_path(const gchar *utf8_filename)
186 static wchar_t w_dir[MAX_PATH];
187 gchar *result;
189 if (g_file_test(utf8_filename, G_FILE_TEST_IS_DIR))
190 result = (gchar*) utf8_filename;
191 else
192 result = g_path_get_dirname(utf8_filename);
194 MultiByteToWideChar(CP_UTF8, 0, result, -1, w_dir, G_N_ELEMENTS(w_dir));
196 if (result != utf8_filename)
197 g_free(result);
199 return w_dir;
203 /* Callback function for setting the initial directory of the folder open dialog. This could also
204 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
205 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
206 * folder when the dialog is initialised. Yeah, I like Windows. */
207 INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
209 switch (uMsg)
211 case BFFM_INITIALIZED:
213 SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, pData);
214 break;
216 case BFFM_SELCHANGED:
218 /* set the status window to the currently selected path. */
219 static wchar_t szDir[MAX_PATH];
220 if (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir))
222 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM) szDir);
224 break;
227 return 0;
231 /* Shows a folder selection dialog.
232 * initial_dir is expected in UTF-8
233 * The selected folder name is returned. */
234 gchar *win32_show_folder_dialog(GtkWidget *parent, const gchar *title, const gchar *initial_dir)
236 BROWSEINFOW bi;
237 LPCITEMIDLIST pidl;
238 gchar *result = NULL;
239 wchar_t fname[MAX_PATH];
240 wchar_t w_title[512];
242 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
244 if (parent == NULL)
245 parent = main_widgets.window;
247 memset(&bi, 0, sizeof bi);
248 bi.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
249 bi.pidlRoot = NULL;
250 bi.lpszTitle = w_title;
251 bi.lpfn = BrowseCallbackProc;
252 bi.lParam = (LPARAM) get_dir_for_path(initial_dir);
253 bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
255 pidl = SHBrowseForFolderW(&bi);
257 /* convert the strange Windows folder list item something into an usual path string ;-) */
258 if (pidl != 0)
260 if (SHGetPathFromIDListW(pidl, fname))
262 result = g_malloc0(MAX_PATH * 2);
263 WideCharToMultiByte(CP_UTF8, 0, fname, -1, result, MAX_PATH * 2, NULL, NULL);
265 /* SHBrowseForFolder() probably leaks memory here, but how to free the allocated memory? */
267 return result;
271 /* Shows a file open dialog.
272 * If allow_new_file is set, the file to be opened doesn't have to exist.
273 * initial_dir is expected in UTF-8
274 * The selected file name is returned.
275 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
276 * files, a filter for executables otherwise. */
277 gchar *win32_show_project_open_dialog(GtkWidget *parent, const gchar *title,
278 const gchar *initial_dir, gboolean allow_new_file,
279 gboolean project_file_filter)
281 OPENFILENAMEW of;
282 gint retval;
283 wchar_t fname[MAX_PATH];
284 wchar_t w_title[512];
285 gchar *filename;
287 fname[0] = '\0';
289 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
291 if (parent == NULL)
292 parent = main_widgets.window;
294 /* initialise file dialog info struct */
295 memset(&of, 0, sizeof of);
296 #ifdef OPENFILENAME_SIZE_VERSION_400
297 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
298 #else
299 of.lStructSize = sizeof of;
300 #endif
301 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
302 of.lpstrFilter = get_filters(project_file_filter);
304 of.lpstrCustomFilter = NULL;
305 of.nFilterIndex = 0;
306 of.lpstrFile = fname;
307 of.lpstrInitialDir = get_dir_for_path(initial_dir);
308 of.nMaxFile = 2048;
309 of.lpstrFileTitle = NULL;
310 of.lpstrTitle = w_title;
311 of.lpstrDefExt = L"";
312 of.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY;
313 if (! allow_new_file)
314 of.Flags |= OFN_FILEMUSTEXIST;
316 retval = GetOpenFileNameW(&of);
318 if (! retval)
320 if (CommDlgExtendedError())
322 gchar *error;
323 error = g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
324 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
325 g_free(error);
327 return NULL;
329 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
330 filename = g_malloc0(MAX_PATH * 2);
331 WideCharToMultiByte(CP_UTF8, 0, fname, -1, filename, MAX_PATH * 2, NULL, NULL);
333 return filename;
337 /* initial_dir can be NULL to use the current working directory.
338 * Returns: TRUE if the dialog was not cancelled. */
339 gboolean win32_show_document_open_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_dir)
341 OPENFILENAMEW of;
342 gint retval;
343 guint x;
344 gchar tmp[MAX_PATH];
345 wchar_t fname[MAX_PATH];
346 wchar_t w_dir[MAX_PATH];
347 wchar_t w_title[512];
349 fname[0] = '\0';
351 if (initial_dir != NULL)
352 MultiByteToWideChar(CP_UTF8, 0, initial_dir, -1, w_dir, G_N_ELEMENTS(w_dir));
354 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
356 /* initialise file dialog info struct */
357 memset(&of, 0, sizeof of);
358 #ifdef OPENFILENAME_SIZE_VERSION_400
359 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
360 #else
361 of.lStructSize = sizeof of;
362 #endif
363 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
364 of.lpstrFilter = get_file_filters();
366 of.lpstrCustomFilter = NULL;
367 of.nFilterIndex = GEANY_FILETYPES_NONE + 1;
368 of.lpstrFile = fname;
369 of.lpstrInitialDir = (initial_dir != NULL) ? w_dir : NULL;
370 of.nMaxFile = 2048;
371 of.lpstrFileTitle = NULL;
372 of.lpstrTitle = w_title;
373 of.lpstrDefExt = L"";
374 of.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_EXPLORER;
376 retval = GetOpenFileNameW(&of);
378 if (!retval)
380 if (CommDlgExtendedError())
382 gchar error[100];
383 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
384 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
386 return FALSE;
389 x = of.nFileOffset - 1;
390 if (x != wcslen(fname))
391 { /* open a single file */
392 WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
393 document_open_file(tmp, of.Flags & OFN_READONLY, NULL, NULL);
395 else
396 { /* open multiple files */
397 gchar file_name[MAX_PATH];
398 gchar dir_name[MAX_PATH];
400 WideCharToMultiByte(CP_UTF8, 0, fname, of.nFileOffset,
401 dir_name, sizeof(dir_name), NULL, NULL);
402 for (; ;)
404 if (! fname[x])
406 if (! fname[x + 1])
407 break;
409 WideCharToMultiByte(CP_UTF8, 0, fname + x + 1, -1,
410 tmp, sizeof(tmp), NULL, NULL);
411 g_snprintf(file_name, 511, "%s\\%s", dir_name, tmp);
413 /* convert the resulting filename into UTF-8 */
414 document_open_file(file_name, of.Flags & OFN_READONLY, NULL, NULL);
416 x++;
419 return (retval != 0);
423 gchar *win32_show_document_save_as_dialog(GtkWindow *parent, const gchar *title,
424 GeanyDocument *doc)
426 OPENFILENAMEW of;
427 gint retval;
428 gchar tmp[MAX_PATH];
429 wchar_t w_file[MAX_PATH];
430 wchar_t w_title[512];
431 int n;
433 w_file[0] = '\0';
435 /* Convert the name of the file for of.lpstrFile */
436 n = MultiByteToWideChar(CP_UTF8, 0, DOC_FILENAME(doc), -1, w_file, G_N_ELEMENTS(w_file));
438 /* If creating a new file name, convert and append the extension if any */
439 if (! doc->file_name && doc->file_type && doc->file_type->extension &&
440 n + 1 < (int)G_N_ELEMENTS(w_file))
442 w_file[n - 1] = L'.';
443 MultiByteToWideChar(CP_UTF8, 0, doc->file_type->extension, -1, &w_file[n],
444 G_N_ELEMENTS(w_file) - n - 1);
447 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
449 /* initialise file dialog info struct */
450 memset(&of, 0, sizeof of);
451 #ifdef OPENFILENAME_SIZE_VERSION_400
452 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
453 #else
454 of.lStructSize = sizeof of;
455 #endif
456 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
458 of.lpstrFilter = get_file_filter_all_files();
459 of.lpstrCustomFilter = NULL;
460 of.nFilterIndex = 0;
462 of.lpstrFile = w_file;
463 of.nMaxFile = 2048;
464 of.lpstrFileTitle = NULL;
465 of.lpstrTitle = w_title;
466 of.lpstrDefExt = L"";
467 of.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER;
468 retval = GetSaveFileNameW(&of);
470 if (! retval)
472 if (CommDlgExtendedError())
474 gchar *error = g_strdup_printf(
475 "File dialog box error (%x)", (gint) CommDlgExtendedError());
476 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
477 g_free(error);
479 return NULL;
482 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
484 return g_strdup(tmp);
488 /* initial_dir can be NULL to use the current working directory.
489 * Returns: the selected filename */
490 gchar *win32_show_file_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_file)
492 OPENFILENAMEW of;
493 gint retval;
494 gchar tmp[MAX_PATH];
495 wchar_t w_file[MAX_PATH];
496 wchar_t w_title[512];
498 w_file[0] = '\0';
500 if (initial_file != NULL)
501 MultiByteToWideChar(CP_UTF8, 0, initial_file, -1, w_file, G_N_ELEMENTS(w_file));
503 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
505 /* initialise file dialog info struct */
506 memset(&of, 0, sizeof of);
507 #ifdef OPENFILENAME_SIZE_VERSION_400
508 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
509 #else
510 of.lStructSize = sizeof of;
511 #endif
512 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
514 of.lpstrFile = w_file;
515 of.nMaxFile = 2048;
516 of.lpstrFileTitle = NULL;
517 of.lpstrTitle = w_title;
518 of.lpstrDefExt = L"";
519 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;
520 retval = GetOpenFileNameW(&of);
522 if (! retval)
524 if (CommDlgExtendedError())
526 gchar *error = g_strdup_printf(
527 "File dialog box error (%x)", (gint) CommDlgExtendedError());
528 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
529 g_free(error);
531 return NULL;
534 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
536 return g_strdup(tmp);
540 void win32_show_font_dialog(void)
542 gint retval;
543 CHOOSEFONT cf;
544 LOGFONT lf; /* logical font structure */
546 memset(&lf, 0, sizeof lf);
547 /* TODO: init lf members */
549 memset(&cf, 0, sizeof cf);
550 cf.lStructSize = sizeof cf;
551 cf.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
552 cf.lpLogFont = &lf;
553 /* support CF_APPLY? */
554 cf.Flags = CF_NOSCRIPTSEL | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
556 retval = ChooseFont(&cf);
558 if (retval)
560 gchar *editorfont = g_strdup_printf("%s %d", lf.lfFaceName, (cf.iPointSize / 10));
561 ui_set_editor_font(editorfont);
562 g_free(editorfont);
567 void win32_show_color_dialog(const gchar *colour)
569 CHOOSECOLOR cc;
570 static COLORREF acr_cust_clr[16];
571 static DWORD rgb_current;
572 gchar *hex = g_malloc0(12);
573 GeanyDocument *doc = document_get_current();
575 /* Initialize CHOOSECOLOR */
576 memset(&cc, 0, sizeof cc);
577 cc.lStructSize = sizeof(cc);
578 cc.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
579 cc.lpCustColors = (LPDWORD) acr_cust_clr;
580 cc.rgbResult = (colour != NULL) ? utils_parse_color_to_bgr(colour) : 0;
581 cc.Flags = CC_FULLOPEN | CC_RGBINIT;
583 if (ChooseColor(&cc))
585 rgb_current = cc.rgbResult;
586 g_snprintf(hex, 11, "#%02X%02X%02X",
587 (guint) (utils_scale_round(GetRValue(rgb_current), 255)),
588 (guint) (utils_scale_round(GetGValue(rgb_current), 255)),
589 (guint) (utils_scale_round(GetBValue(rgb_current), 255)));
591 editor_insert_color(doc->editor, hex);
593 g_free(hex);
597 void win32_show_pref_file_dialog(GtkEntry *item)
599 OPENFILENAMEW of;
600 gint retval, len;
601 wchar_t fname[MAX_PATH];
602 gchar tmp[MAX_PATH];
603 gchar **field, *filename;
605 fname[0] = '\0';
607 /* cut the options from the command line */
608 field = g_strsplit(gtk_entry_get_text(GTK_ENTRY(item)), " ", 2);
609 if (field[0])
611 filename = g_find_program_in_path(field[0]);
612 if (filename != NULL && g_file_test(filename, G_FILE_TEST_EXISTS))
614 MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname, G_N_ELEMENTS(fname));
615 g_free(filename);
619 /* initialize file dialog info struct */
620 memset(&of, 0, sizeof of);
621 #ifdef OPENFILENAME_SIZE_VERSION_400
622 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
623 #else
624 of.lStructSize = sizeof of;
625 #endif
626 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets.prefs_dialog));
628 of.lpstrFilter = get_filters(FALSE);
629 of.lpstrCustomFilter = NULL;
630 of.nFilterIndex = 1;
632 of.lpstrFile = fname;
633 of.nMaxFile = 2048;
634 of.lpstrFileTitle = NULL;
635 /*of.lpstrInitialDir = g_get_home_dir();*/
636 of.lpstrInitialDir = NULL;
637 of.lpstrTitle = NULL;
638 of.lpstrDefExt = L"exe";
639 of.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER;
640 retval = GetOpenFileNameW(&of);
642 if (!retval)
644 if (CommDlgExtendedError())
646 gchar error[100];
647 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
648 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
650 g_strfreev(field);
651 return;
654 len = WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
655 if ((of.nFileOffset - 1) != len)
657 if (g_strv_length(field) > 1)
658 /* add the command line args of the old command */
659 /** TODO this fails badly when the old command contained spaces, we need quoting here */
660 filename = g_strconcat(tmp, " ", field[1], NULL);
661 else
663 filename = g_strdup(tmp);
665 gtk_entry_set_text(GTK_ENTRY(item), filename);
666 g_free(filename);
668 g_strfreev(field);
672 /* Creates a native Windows message box of the given type and returns always TRUE
673 * or FALSE representing th pressed Yes or No button.
674 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
675 gboolean win32_message_dialog(GtkWidget *parent, GtkMessageType type, const gchar *msg)
677 gboolean ret = TRUE;
678 gint rc;
679 guint t;
680 const gchar *title;
681 HWND parent_hwnd = NULL;
682 static wchar_t w_msg[512];
683 static wchar_t w_title[512];
685 switch (type)
687 case GTK_MESSAGE_ERROR:
689 t = MB_OK | MB_ICONERROR;
690 title = _("Error");
691 break;
693 case GTK_MESSAGE_QUESTION:
695 t = MB_YESNO | MB_ICONQUESTION;
696 title = _("Question");
697 break;
699 case GTK_MESSAGE_WARNING:
701 t = MB_OK | MB_ICONWARNING;
702 title = _("Warning");
703 break;
705 default:
707 t = MB_OK | MB_ICONINFORMATION;
708 title = _("Information");
709 break;
713 /* convert the Unicode chars to wide chars */
714 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
715 MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, G_N_ELEMENTS(w_msg));
716 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
718 if (parent != NULL)
719 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
720 else if (main_widgets.window != NULL)
721 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
723 /* display the message box */
724 rc = MessageBoxW(parent_hwnd, w_msg, w_title, t);
726 if (type == GTK_MESSAGE_QUESTION && rc != IDYES)
727 ret = FALSE;
729 return ret;
733 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
734 gint win32_check_write_permission(const gchar *dir)
736 static wchar_t w_dir[MAX_PATH];
737 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, G_N_ELEMENTS(w_dir));
738 if (_waccess(w_dir, R_OK | W_OK) != 0)
739 return errno;
740 else
741 return 0;
745 /* Just a simple wrapper function to open a browser window */
746 void win32_open_browser(const gchar *uri)
748 if (strncmp(uri, "file://", 7) == 0)
750 uri += 7;
751 if (strchr(uri, ':') != NULL)
753 while (*uri == '/')
754 uri++;
757 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
761 /* Returns TRUE if the command, which child_pid refers to, returned with a successful exit code,
762 * otherwise FALSE. */
763 gboolean win32_get_exit_status(GPid child_pid)
765 DWORD exit_code;
766 GetExitCodeProcess(child_pid, &exit_code);
768 return (exit_code == 0);
772 static FILE *open_std_handle(DWORD handle, const char *mode)
774 HANDLE lStdHandle;
775 int hConHandle;
776 FILE *fp;
778 lStdHandle = GetStdHandle(handle);
779 if (lStdHandle == INVALID_HANDLE_VALUE)
781 gchar *err = g_win32_error_message(GetLastError());
782 g_warning("GetStdHandle(%ld) failed: %s", (long)handle, err);
783 g_free(err);
784 return NULL;
786 hConHandle = _open_osfhandle((long)lStdHandle, _O_TEXT);
787 if (hConHandle == -1)
789 gchar *err = g_win32_error_message(GetLastError());
790 g_warning("_open_osfhandle(%ld, _O_TEXT) failed: %s", (long)lStdHandle, err);
791 g_free(err);
792 return NULL;
794 fp = _fdopen(hConHandle, mode);
795 if (! fp)
797 gchar *err = g_win32_error_message(GetLastError());
798 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle, mode, err);
799 g_free(err);
800 return NULL;
802 if (setvbuf(fp, NULL, _IONBF, 0) != 0)
804 gchar *err = g_win32_error_message(GetLastError());
805 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp, err);
806 g_free(err);
807 fclose(fp);
808 return NULL;
811 return fp;
815 static void debug_setup_console(void)
817 static const WORD MAX_CONSOLE_LINES = 500;
818 CONSOLE_SCREEN_BUFFER_INFO coninfo;
819 FILE *fp;
821 /* allocate a console for this app */
822 AllocConsole();
824 /* set the screen buffer to be big enough to let us scroll text */
825 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
826 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
827 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
829 /* redirect unbuffered STDOUT to the console */
830 fp = open_std_handle(STD_OUTPUT_HANDLE, "w");
831 if (fp)
832 *stdout = *fp;
834 /* redirect unbuffered STDERR to the console */
835 fp = open_std_handle(STD_ERROR_HANDLE, "w");
836 if (fp)
837 *stderr = *fp;
839 /* redirect unbuffered STDIN to the console */
840 fp = open_std_handle(STD_INPUT_HANDLE, "r");
841 if (fp)
842 *stdin = *fp;
846 void win32_init_debug_code(void)
848 if (app->debug_mode)
850 /* create a console window to get log messages on Windows,
851 * especially useful when generating tags files */
852 debug_setup_console();
853 /* Enable GLib process spawn debug mode when Geany was started with the debug flag */
854 g_setenv("G_SPAWN_WIN32_DEBUG", "1", FALSE);
859 static gchar *create_temp_file(void)
861 gchar *name;
862 gint fd;
864 fd = g_file_open_tmp("tmp_XXXXXX", &name, NULL);
865 if (fd == -1)
866 name = NULL;
867 else
868 close(fd);
870 return name;
874 /* Sometimes this blocks for 30s before aborting when there are several
875 * pages of (error) output and sometimes hangs - see the FIXME.
876 * Also gw_spawn.dwExitCode seems to be not set properly. */
877 /* Process spawning implementation for Windows, by Pierre Joye.
878 * Don't call this function directly, use utils_spawn_[a]sync() instead. */
879 static
880 gboolean _broken_win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
881 gchar **std_out, gchar **std_err, gint *exit_status, GError **error)
883 TCHAR buffer[CMDSIZE]=TEXT("");
884 TCHAR cmdline[CMDSIZE] = TEXT("");
885 TCHAR* lpPart[CMDSIZE]={NULL};
886 DWORD retval = 0;
887 gint argc = 0, i;
888 gint cmdpos = 0;
890 SECURITY_ATTRIBUTES saAttr;
891 BOOL fSuccess;
892 geany_win32_spawn gw_spawn;
894 /* Temp file */
895 HANDLE hStdoutTempFile = NULL;
896 HANDLE hStderrTempFile = NULL;
898 gchar *stdout_content = NULL;
899 gchar *stderr_content = NULL;
901 while (argv[argc])
903 ++argc;
905 g_return_val_if_fail (std_out == NULL ||
906 !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
907 g_return_val_if_fail (std_err == NULL ||
908 !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
910 if (flags & G_SPAWN_SEARCH_PATH)
912 retval = SearchPath(NULL, argv[0], ".exe", sizeof(buffer), buffer, lpPart);
913 if (retval > 0)
914 g_snprintf(cmdline, sizeof(cmdline), "\"%s\"", buffer);
915 else
916 g_strlcpy(cmdline, argv[0], sizeof(cmdline));
917 cmdpos = 1;
920 for (i = cmdpos; i < argc; i++)
922 g_snprintf(cmdline, sizeof(cmdline), "%s %s", cmdline, argv[i]);
923 /*MessageBox(NULL, cmdline, cmdline, MB_OK);*/
926 if (std_err != NULL)
928 hStderrTempFile = GetTempFileHandle(error);
929 if (hStderrTempFile == INVALID_HANDLE_VALUE)
931 gchar *msg = g_win32_error_message(GetLastError());
932 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint) GetLastError());
933 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
934 g_free(msg);
935 return FALSE;
939 if (std_out != NULL)
941 hStdoutTempFile = GetTempFileHandle(error);
942 if (hStdoutTempFile == INVALID_HANDLE_VALUE)
944 gchar *msg = g_win32_error_message(GetLastError());
945 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint) GetLastError());
946 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
947 g_free(msg);
948 return FALSE;
952 /* Set the bInheritHandle flag so pipe handles are inherited. */
953 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
954 saAttr.bInheritHandle = TRUE;
955 saAttr.lpSecurityDescriptor = NULL;
957 /* Get the handle to the current STDOUT and STDERR. */
958 gw_spawn.hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
959 gw_spawn.hStderr = GetStdHandle(STD_ERROR_HANDLE);
960 gw_spawn.dwExitCode = 0;
962 /* Create a pipe for the child process's STDOUT. */
963 if (! CreatePipe(&(gw_spawn.hChildStdoutRd), &(gw_spawn.hChildStdoutWr), &saAttr, 0))
965 gchar *msg = g_win32_error_message(GetLastError());
966 geany_debug("win32_spawn: Stdout pipe creation failed (%d)", (gint) GetLastError());
967 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
968 g_free(msg);
969 return FALSE;
972 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
973 SetHandleInformation(gw_spawn.hChildStdoutRd, HANDLE_FLAG_INHERIT, 0);
975 /* Create a pipe for the child process's STDERR. */
976 if (! CreatePipe(&(gw_spawn.hChildStderrRd), &(gw_spawn.hChildStderrWr), &saAttr, 0))
978 gchar *msg = g_win32_error_message(GetLastError());
979 geany_debug("win32_spawn: Stderr pipe creation failed");
980 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
981 g_free(msg);
982 return FALSE;
985 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
986 SetHandleInformation(gw_spawn.hChildStderrRd, HANDLE_FLAG_INHERIT, 0);
988 /* Create a pipe for the child process's STDIN. */
989 if (! CreatePipe(&(gw_spawn.hChildStdinRd), &(gw_spawn.hChildStdinWr), &saAttr, 0))
991 gchar *msg = g_win32_error_message(GetLastError());
992 geany_debug("win32_spawn: Stdin pipe creation failed");
993 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
994 g_free(msg);
995 return FALSE;
998 /* Ensure that the write handle to the child process's pipe for STDIN is not inherited. */
999 SetHandleInformation(gw_spawn.hChildStdinWr, HANDLE_FLAG_INHERIT, 0);
1001 /* Now create the child process. */
1002 fSuccess = CreateChildProcess(&gw_spawn, cmdline, dir, error);
1003 if (exit_status)
1005 *exit_status = gw_spawn.dwExitCode;
1008 if (! fSuccess)
1010 geany_debug("win32_spawn: Create process failed");
1011 return FALSE;
1014 /* Read from pipe that is the standard output for child process. */
1015 if (std_out != NULL)
1017 ReadFromPipe(gw_spawn.hChildStdoutRd, gw_spawn.hChildStdoutWr, hStdoutTempFile, error);
1018 if (! GetContentFromHandle(hStdoutTempFile, &stdout_content, error))
1020 return FALSE;
1022 *std_out = stdout_content;
1025 if (std_err != NULL)
1027 ReadFromPipe(gw_spawn.hChildStderrRd, gw_spawn.hChildStderrWr, hStderrTempFile, error);
1028 if (! GetContentFromHandle(hStderrTempFile, &stderr_content, error))
1030 return FALSE;
1032 *std_err = stderr_content;
1034 return TRUE;
1038 /* Note: g_spawn is broken for receiving both stdio and stderr e.g. when
1039 * running make and there are compile errors. See glib/giowin32.c header
1040 * comment about Windows bugs, e.g. #338943 */
1041 /* Simple replacement for _broken_win32_spawn().
1042 * flags is ignored, G_SPAWN_SEARCH_PATH is implied.
1043 * Don't call this function directly, use utils_spawn_[a]sync() instead.
1044 * Adapted from tm_workspace_create_global_tags(). */
1045 gboolean win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1046 gchar **std_out, gchar **std_err, gint *exit_status, GError **error)
1048 gint ret;
1049 gboolean fail;
1050 gchar *tmp_file = create_temp_file();
1051 gchar *tmp_errfile = create_temp_file();
1052 gchar *command;
1053 gchar *locale_command;
1055 if (env != NULL)
1057 return _broken_win32_spawn(dir, argv, env, flags, std_out, std_err,
1058 exit_status, error);
1060 if (!tmp_file || !tmp_errfile)
1062 g_warning("%s: Could not create temporary files!", G_STRFUNC);
1063 return FALSE;
1065 command = g_strjoinv(" ", argv);
1066 SETPTR(command, g_strdup_printf("cmd.exe /S /C \"%s >%s 2>%s\"",
1067 command, tmp_file, tmp_errfile));
1068 locale_command = g_locale_from_utf8(command, -1, NULL, NULL, NULL);
1069 if (! locale_command)
1070 locale_command = g_strdup(command);
1071 geany_debug("WIN32: actually running command:\n%s", command);
1072 g_chdir(dir);
1073 errno = 0;
1074 ret = system(locale_command);
1075 /* the command can return -1 as an exit code, so check errno also */
1076 fail = ret == -1 && errno;
1077 if (!fail)
1079 if (std_out != NULL)
1080 g_file_get_contents(tmp_file, std_out, NULL, NULL);
1081 if (std_err != NULL)
1082 g_file_get_contents(tmp_errfile, std_err, NULL, NULL);
1084 else if (error)
1085 g_set_error_literal(error, G_SPAWN_ERROR, errno, g_strerror(errno));
1087 g_free(command);
1088 g_free(locale_command);
1089 g_unlink(tmp_file);
1090 g_free(tmp_file);
1091 g_unlink(tmp_errfile);
1092 g_free(tmp_errfile);
1093 if (exit_status)
1094 *exit_status = ret;
1096 return !fail;
1100 static gboolean GetContentFromHandle(HANDLE hFile, gchar **content, GError **error)
1102 DWORD filesize;
1103 gchar * buffer;
1104 DWORD dwRead;
1106 filesize = GetFileSize(hFile, NULL);
1107 if (filesize < 1)
1109 *content = NULL;
1110 return TRUE;
1113 buffer = g_malloc(filesize + 1);
1114 if (! buffer)
1116 gchar *msg = g_win32_error_message(GetLastError());
1117 geany_debug("GetContentFromHandle: Alloc failed");
1118 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR, "%s", msg);
1119 g_free(msg);
1120 return FALSE;
1123 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
1124 if (! ReadFile(hFile, buffer, filesize, &dwRead, NULL) || dwRead == 0)
1126 gchar *msg = g_win32_error_message(GetLastError());
1127 geany_debug("GetContentFromHandle: Cannot read tempfile");
1128 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1129 g_free(msg);
1130 return FALSE;
1133 if (! CloseHandle(hFile))
1135 gchar *msg = g_win32_error_message(GetLastError());
1136 geany_debug("GetContentFromHandle: CloseHandle failed (%d)", (gint) GetLastError());
1137 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1138 g_free(msg);
1139 g_free(buffer);
1140 *content = NULL;
1141 return FALSE;
1143 buffer[filesize] = '\0';
1144 *content = buffer;
1145 return TRUE;
1149 gchar *win32_expand_environment_variables(const gchar *str)
1151 gchar expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
1153 if (ExpandEnvironmentStrings((LPCTSTR) str, (LPTSTR) expCmdline, sizeof(expCmdline)) != 0)
1154 return g_strdup(expCmdline);
1155 else
1156 return g_strdup(str);
1160 static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline,
1161 const TCHAR *dir, GError **error)
1163 PROCESS_INFORMATION piProcInfo;
1164 STARTUPINFOW siStartInfo;
1165 BOOL bFuncRetn = FALSE;
1166 gchar *expandedCmdline;
1167 wchar_t w_commandline[CMDSIZE];
1168 wchar_t w_dir[MAX_PATH];
1170 /* Set up members of the PROCESS_INFORMATION structure. */
1171 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1173 /* Set up members of the STARTUPINFO structure.*/
1174 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
1176 siStartInfo.cb = sizeof(STARTUPINFO);
1177 siStartInfo.hStdError = gw_spawn->hChildStderrWr;
1178 siStartInfo.hStdOutput = gw_spawn->hChildStdoutWr;
1179 siStartInfo.hStdInput = gw_spawn->hChildStdinRd;
1180 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1182 /* Expand environment variables like %blah%. */
1183 expandedCmdline = win32_expand_environment_variables(szCmdline);
1185 MultiByteToWideChar(CP_UTF8, 0, expandedCmdline, -1, w_commandline, G_N_ELEMENTS(w_commandline));
1186 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, G_N_ELEMENTS(w_dir));
1188 /* Create the child process. */
1189 bFuncRetn = CreateProcessW(NULL,
1190 w_commandline, /* command line */
1191 NULL, /* process security attributes */
1192 NULL, /* primary thread security attributes */
1193 TRUE, /* handles are inherited */
1194 CREATE_NO_WINDOW, /* creation flags */
1195 NULL, /* use parent's environment */
1196 w_dir, /* use parent's current directory */
1197 &siStartInfo, /* STARTUPINFO pointer */
1198 &piProcInfo); /* receives PROCESS_INFORMATION */
1200 g_free(expandedCmdline);
1202 if (bFuncRetn == 0)
1204 gchar *msg = g_win32_error_message(GetLastError());
1205 geany_debug("CreateChildProcess: CreateProcess failed (%s)", msg);
1206 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "%s", msg);
1207 g_free(msg);
1208 return FALSE;
1210 else
1212 gint i;
1213 gsize ms = 30*1000;
1215 /* FIXME: this seems to timeout when there are many lines
1216 * to read - maybe because the child's pipe is full */
1217 for (i = 0; i < 2 &&
1218 WaitForSingleObject(piProcInfo.hProcess, ms) == WAIT_TIMEOUT; i++)
1220 ui_set_statusbar(FALSE, _("Process timed out after %.02f s!"), ms / 1000.0F);
1221 geany_debug("CreateChildProcess: timed out");
1222 TerminateProcess(piProcInfo.hProcess, WAIT_TIMEOUT); /* NOTE: This will not kill grandkids. */
1225 if (!GetExitCodeProcess(piProcInfo.hProcess, &gw_spawn->dwExitCode))
1227 gchar *msg = g_win32_error_message(GetLastError());
1228 geany_debug("GetExitCodeProcess failed: %s", msg);
1229 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1230 g_free(msg);
1232 CloseHandle(piProcInfo.hProcess);
1233 CloseHandle(piProcInfo.hThread);
1234 return bFuncRetn;
1236 return FALSE;
1240 static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile, GError **error)
1242 DWORD dwRead, dwWritten;
1243 CHAR chBuf[BUFSIZE];
1245 /* Close the write end of the pipe before reading from the
1246 read end of the pipe. */
1247 if (! CloseHandle(hWrite))
1249 gchar *msg = g_win32_error_message(GetLastError());
1250 geany_debug("ReadFromPipe: Closing handle failed");
1251 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
1252 g_free(msg);
1253 return;
1256 /* Read output from the child process, and write to parent's STDOUT. */
1257 for (;;)
1259 if (! ReadFile(hRead, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0)
1260 break;
1262 if (! WriteFile(hFile, chBuf, dwRead, &dwWritten, NULL))
1263 break;
1268 static HANDLE GetTempFileHandle(GError **error)
1270 /* Temp file */
1271 DWORD dwBufSize = BUFSIZE;
1272 UINT uRetVal;
1273 TCHAR szTempName[BUFSIZE];
1274 TCHAR lpPathBuffer[BUFSIZE];
1275 DWORD dwRetVal;
1276 HANDLE hTempFile;
1278 /* Get the temp path. */
1279 dwRetVal = GetTempPath(dwBufSize, /* length of the buffer*/
1280 lpPathBuffer); /* buffer for path */
1282 if (dwRetVal > dwBufSize || (dwRetVal == 0))
1284 gchar *msg = g_win32_error_message(GetLastError());
1285 geany_debug("GetTempFileHandle: GetTempPath failed (%d)", (gint) GetLastError());
1286 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1287 g_free(msg);
1288 return NULL;
1291 /* Create a temporary file for STDOUT. */
1292 uRetVal = GetTempFileName(lpPathBuffer, /* directory for tmp files */
1293 TEXT("GEANY_VCDIFF_"), /* temp file name prefix */
1294 0, /* create unique name */
1295 szTempName); /* buffer for name */
1296 if (uRetVal == 0)
1298 gchar *msg = g_win32_error_message(GetLastError());
1299 geany_debug("GetTempFileName failed (%d)", (gint) GetLastError());
1300 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1301 g_free(msg);
1302 return NULL;
1305 hTempFile = CreateFile((LPTSTR) szTempName, /* file name */
1306 GENERIC_READ | GENERIC_WRITE, /* open r-w */
1307 0, /* do not share */
1308 NULL, /* default security */
1309 CREATE_ALWAYS, /* overwrite existing */
1310 FILE_ATTRIBUTE_NORMAL,/* normal file */
1311 NULL); /* no template */
1313 if (hTempFile == INVALID_HANDLE_VALUE)
1315 gchar *msg = g_win32_error_message(GetLastError());
1316 geany_debug("GetTempFileHandle: Second CreateFile failed (%d)", (gint) GetLastError());
1317 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1318 g_free(msg);
1319 return NULL;
1321 return hTempFile;
1325 /* From GDK (they got it from MS Knowledge Base article Q130698) */
1326 static gboolean resolve_link(HWND hWnd, wchar_t *link, gchar **lpszPath)
1328 WIN32_FILE_ATTRIBUTE_DATA wfad;
1329 HRESULT hres;
1330 IShellLinkW *pslW = NULL;
1331 IPersistFile *ppf = NULL;
1332 LPVOID pslWV = NULL;
1333 LPVOID ppfV = NULL;
1335 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
1336 * with an empty file and returns an empty "link target". (#524151) */
1337 if (!GetFileAttributesExW(link, GetFileExInfoStandard, &wfad) ||
1338 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1340 return FALSE;
1343 /* Assume failure to start with: */
1344 *lpszPath = 0;
1346 CoInitialize(NULL);
1348 hres = CoCreateInstance(
1349 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &pslWV);
1351 if (SUCCEEDED(hres))
1353 /* The IShellLink interface supports the IPersistFile interface.
1354 * Get an interface pointer to it. */
1355 pslW = (IShellLinkW*) pslWV;
1356 hres = pslW->lpVtbl->QueryInterface(pslW, &IID_IPersistFile, &ppfV);
1359 if (SUCCEEDED(hres))
1361 /* Load the file. */
1362 ppf = (IPersistFile*) ppfV;
1363 hres = ppf->lpVtbl->Load(ppf, link, STGM_READ);
1366 if (SUCCEEDED(hres))
1368 /* Resolve the link by calling the Resolve() interface function. */
1369 hres = pslW->lpVtbl->Resolve(pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1372 if (SUCCEEDED(hres))
1374 wchar_t wtarget[MAX_PATH];
1376 hres = pslW->lpVtbl->GetPath(pslW, wtarget, MAX_PATH, NULL, 0);
1377 if (SUCCEEDED(hres))
1378 *lpszPath = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
1381 if (ppf)
1382 ppf->lpVtbl->Release(ppf);
1384 if (pslW)
1385 pslW->lpVtbl->Release(pslW);
1387 return SUCCEEDED(hres);
1391 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
1392 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
1393 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
1394 gchar *win32_get_shortcut_target(const gchar *file_name)
1396 gchar *path = NULL;
1397 wchar_t *wfilename = g_utf8_to_utf16(file_name, -1, NULL, NULL, NULL);
1399 resolve_link(GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window)), wfilename, &path);
1400 g_free(wfilename);
1402 if (path == NULL)
1403 return g_strdup(file_name);
1404 else
1405 return path;
1409 void win32_set_working_directory(const gchar *dir)
1411 SetCurrentDirectory(dir);
1415 gchar *win32_get_installation_dir(void)
1417 return g_win32_get_package_installation_directory_of_module(NULL);
1421 #endif