Put Makefile comments at start of line.
[geany-mirror.git] / src / win32.c
blobb4574a4f1acb9415d60e8fabc9304cf84e648c24
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 #include "geany.h"
28 #ifdef G_OS_WIN32
30 #define VC_EXTRALEAN
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #include <commdlg.h>
34 #include <shlobj.h>
35 #include <io.h>
36 #include <fcntl.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <math.h>
41 #include <stdlib.h>
43 #include <glib/gstdio.h>
44 #include <gdk/gdkwin32.h>
46 #include "win32.h"
48 #include "document.h"
49 #include "support.h"
50 #include "utils.h"
51 #include "ui_utils.h"
52 #include "sciwrappers.h"
53 #include "dialogs.h"
54 #include "filetypes.h"
55 #include "project.h"
56 #include "editor.h"
58 #define BUFSIZE 4096
59 #define CMDSIZE 32768
61 struct _geany_win32_spawn
63 HANDLE hChildStdinRd;
64 HANDLE hChildStdinWr;
65 HANDLE hChildStdoutRd;
66 HANDLE hChildStdoutWr;
67 HANDLE hChildStderrRd;
68 HANDLE hChildStderrWr;
69 HANDLE hInputFile;
70 HANDLE hStdout;
71 HANDLE hStderr;
72 HANDLE processId;
73 DWORD dwExitCode;
75 typedef struct _geany_win32_spawn geany_win32_spawn;
77 static gboolean GetContentFromHandle(HANDLE hFile, gchar **content, GError **error);
78 static HANDLE GetTempFileHandle(GError **error);
79 static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline,
80 const TCHAR *dir, GError **error);
81 static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile, GError **error);
84 static wchar_t *get_file_filters(void)
86 gchar *string;
87 guint i, j, len;
88 static wchar_t title[4096];
89 GString *str = g_string_sized_new(100);
90 GString *all_patterns = g_string_sized_new(100);
91 GSList *node;
92 gchar *tmp;
94 /* create meta file filter "All files" */
95 g_string_append_printf(str, "%s\t*\t", _("All files"));
96 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
97 for (i = GEANY_FILETYPES_NONE + 1; i < filetypes_array->len; i++)
99 for (j = 0; filetypes[i]->pattern[j] != NULL; j++)
101 g_string_append(all_patterns, filetypes[i]->pattern[j]);
102 g_string_append_c(all_patterns, ';');
105 g_string_append_printf(str, "%s\t%s\t", _("All Source"), all_patterns->str);
106 g_string_free(all_patterns, TRUE);
107 /* add 'usual' filetypes */
108 foreach_slist(node, filetypes_by_title)
110 GeanyFiletype *ft = node->data;
112 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
113 continue;
114 tmp = g_strjoinv(";", ft->pattern);
115 g_string_append_printf(str, "%s\t%s\t", ft->title, tmp);
116 g_free(tmp);
118 g_string_append_c(str, '\t'); /* the final \0 byte to mark the end of the string */
119 string = str->str;
120 g_string_free(str, FALSE);
122 /* replace all "\t"s by \0 */
123 len = strlen(string);
124 g_strdelimit(string, "\t", '\0');
125 g_assert(string[len - 1] == 0x0);
126 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
127 g_free(string);
129 return title;
133 static wchar_t *get_file_filter_all_files(void)
135 guint len;
136 static wchar_t title[4096];
137 gchar *filter;
139 /* create meta file filter "All files" */
140 filter = g_strdup_printf("%s\t*\t", _("All files"));
142 len = strlen(filter);
143 g_strdelimit(filter, "\t", '\0');
144 g_assert(filter[len - 1] == 0x0);
145 MultiByteToWideChar(CP_UTF8, 0, filter, len, title, G_N_ELEMENTS(title));
146 g_free(filter);
148 return title;
152 static wchar_t *get_filters(gboolean project_files)
154 gchar *string;
155 gint len;
156 static wchar_t title[1024];
158 if (project_files)
160 string = g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT, "\t",
161 _("All files"), "\t", "*", "\t", NULL);
163 else
165 string = g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
166 _("All files"), "\t", "*", "\t", NULL);
169 /* replace all "\t"s by \0 */
170 len = strlen(string);
171 g_strdelimit(string, "\t", '\0');
172 g_assert(string[len - 1] == 0x0);
173 MultiByteToWideChar(CP_UTF8, 0, string, len, title, G_N_ELEMENTS(title));
174 g_free(string);
176 return title;
180 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
181 * returns the directory part of the given filename. */
182 static wchar_t *get_dir_for_path(const gchar *utf8_filename)
184 static wchar_t w_dir[MAX_PATH];
185 gchar *result;
187 if (g_file_test(utf8_filename, G_FILE_TEST_IS_DIR))
188 result = (gchar*) utf8_filename;
189 else
190 result = g_path_get_dirname(utf8_filename);
192 MultiByteToWideChar(CP_UTF8, 0, result, -1, w_dir, G_N_ELEMENTS(w_dir));
194 if (result != utf8_filename)
195 g_free(result);
197 return w_dir;
201 /* Callback function for setting the initial directory of the folder open dialog. This could also
202 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
203 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
204 * folder when the dialog is initialised. Yeah, I like Windows. */
205 INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
207 switch (uMsg)
209 case BFFM_INITIALIZED:
211 SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, pData);
212 break;
214 case BFFM_SELCHANGED:
216 /* set the status window to the currently selected path. */
217 static wchar_t szDir[MAX_PATH];
218 if (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir))
220 SendMessageW(hwnd, BFFM_SETSTATUSTEXTW, 0, (LPARAM) szDir);
222 break;
225 return 0;
229 /* Shows a folder selection dialog.
230 * initial_dir is expected in UTF-8
231 * The selected folder name is returned. */
232 gchar *win32_show_folder_dialog(GtkWidget *parent, const gchar *title, const gchar *initial_dir)
234 BROWSEINFOW bi;
235 LPCITEMIDLIST pidl;
236 gchar *result = NULL;
237 wchar_t fname[MAX_PATH];
238 wchar_t w_title[512];
240 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
242 if (parent == NULL)
243 parent = main_widgets.window;
245 memset(&bi, 0, sizeof bi);
246 bi.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
247 bi.pidlRoot = NULL;
248 bi.lpszTitle = w_title;
249 bi.lpfn = BrowseCallbackProc;
250 bi.lParam = (LPARAM) get_dir_for_path(initial_dir);
251 bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
253 pidl = SHBrowseForFolderW(&bi);
255 /* convert the strange Windows folder list item something into an usual path string ;-) */
256 if (pidl != 0)
258 if (SHGetPathFromIDListW(pidl, fname))
260 result = g_malloc0(MAX_PATH * 2);
261 WideCharToMultiByte(CP_UTF8, 0, fname, -1, result, MAX_PATH * 2, NULL, NULL);
263 /* SHBrowseForFolder() probably leaks memory here, but how to free the allocated memory? */
265 return result;
269 /* Shows a file open dialog.
270 * If allow_new_file is set, the file to be opened doesn't have to exist.
271 * initial_dir is expected in UTF-8
272 * The selected file name is returned.
273 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
274 * files, a filter for executables otherwise. */
275 gchar *win32_show_project_open_dialog(GtkWidget *parent, const gchar *title,
276 const gchar *initial_dir, gboolean allow_new_file,
277 gboolean project_file_filter)
279 OPENFILENAMEW of;
280 gint retval;
281 wchar_t fname[MAX_PATH];
282 wchar_t w_title[512];
283 gchar *filename;
285 fname[0] = '\0';
287 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
289 if (parent == NULL)
290 parent = main_widgets.window;
292 /* initialise file dialog info struct */
293 memset(&of, 0, sizeof of);
294 #ifdef OPENFILENAME_SIZE_VERSION_400
295 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
296 #else
297 of.lStructSize = sizeof of;
298 #endif
299 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
300 of.lpstrFilter = get_filters(project_file_filter);
302 of.lpstrCustomFilter = NULL;
303 of.nFilterIndex = 0;
304 of.lpstrFile = fname;
305 of.lpstrInitialDir = get_dir_for_path(initial_dir);
306 of.nMaxFile = 2048;
307 of.lpstrFileTitle = NULL;
308 of.lpstrTitle = w_title;
309 of.lpstrDefExt = L"";
310 of.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY;
311 if (! allow_new_file)
312 of.Flags |= OFN_FILEMUSTEXIST;
314 retval = GetOpenFileNameW(&of);
316 if (! retval)
318 if (CommDlgExtendedError())
320 gchar *error;
321 error = g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
322 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
323 g_free(error);
325 return NULL;
327 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
328 filename = g_malloc0(MAX_PATH * 2);
329 WideCharToMultiByte(CP_UTF8, 0, fname, -1, filename, MAX_PATH * 2, NULL, NULL);
331 return filename;
335 /* initial_dir can be NULL to use the current working directory.
336 * Returns: TRUE if the dialog was not cancelled. */
337 gboolean win32_show_document_open_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_dir)
339 OPENFILENAMEW of;
340 gint retval;
341 guint x;
342 gchar tmp[MAX_PATH];
343 wchar_t fname[MAX_PATH];
344 wchar_t w_dir[MAX_PATH];
345 wchar_t w_title[512];
347 fname[0] = '\0';
349 if (initial_dir != NULL)
350 MultiByteToWideChar(CP_UTF8, 0, initial_dir, -1, w_dir, G_N_ELEMENTS(w_dir));
352 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
354 /* initialise file dialog info struct */
355 memset(&of, 0, sizeof of);
356 #ifdef OPENFILENAME_SIZE_VERSION_400
357 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
358 #else
359 of.lStructSize = sizeof of;
360 #endif
361 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
362 of.lpstrFilter = get_file_filters();
364 of.lpstrCustomFilter = NULL;
365 of.nFilterIndex = GEANY_FILETYPES_NONE + 1;
366 of.lpstrFile = fname;
367 of.lpstrInitialDir = (initial_dir != NULL) ? w_dir : NULL;
368 of.nMaxFile = 2048;
369 of.lpstrFileTitle = NULL;
370 of.lpstrTitle = w_title;
371 of.lpstrDefExt = L"";
372 of.Flags = OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_EXPLORER;
374 retval = GetOpenFileNameW(&of);
376 if (!retval)
378 if (CommDlgExtendedError())
380 gchar error[100];
381 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
382 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
384 return FALSE;
387 x = of.nFileOffset - 1;
388 if (x != wcslen(fname))
389 { /* open a single file */
390 WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
391 document_open_file(tmp, of.Flags & OFN_READONLY, NULL, NULL);
393 else
394 { /* open multiple files */
395 gchar file_name[MAX_PATH];
396 gchar dir_name[MAX_PATH];
398 WideCharToMultiByte(CP_UTF8, 0, fname, of.nFileOffset,
399 dir_name, sizeof(dir_name), NULL, NULL);
400 for (; ;)
402 if (! fname[x])
404 if (! fname[x + 1])
405 break;
407 WideCharToMultiByte(CP_UTF8, 0, fname + x + 1, -1,
408 tmp, sizeof(tmp), NULL, NULL);
409 g_snprintf(file_name, 511, "%s\\%s", dir_name, tmp);
411 /* convert the resulting filename into UTF-8 */
412 document_open_file(file_name, of.Flags & OFN_READONLY, NULL, NULL);
414 x++;
417 return (retval != 0);
421 gchar *win32_show_document_save_as_dialog(GtkWindow *parent, const gchar *title,
422 GeanyDocument *doc)
424 OPENFILENAMEW of;
425 gint retval;
426 gchar tmp[MAX_PATH];
427 wchar_t w_file[MAX_PATH];
428 wchar_t w_title[512];
429 int n;
431 w_file[0] = '\0';
433 /* Convert the name of the file for of.lpstrFile */
434 n = MultiByteToWideChar(CP_UTF8, 0, DOC_FILENAME(doc), -1, w_file, G_N_ELEMENTS(w_file));
436 /* If creating a new file name, convert and append the extension if any */
437 if (! doc->file_name && doc->file_type && doc->file_type->extension && n + 1 < G_N_ELEMENTS(w_file))
439 w_file[n - 1] = L'.';
440 MultiByteToWideChar(CP_UTF8, 0, doc->file_type->extension, -1, &w_file[n],
441 G_N_ELEMENTS(w_file) - n - 1);
444 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
446 /* initialise file dialog info struct */
447 memset(&of, 0, sizeof of);
448 #ifdef OPENFILENAME_SIZE_VERSION_400
449 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
450 #else
451 of.lStructSize = sizeof of;
452 #endif
453 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
455 of.lpstrFilter = get_file_filter_all_files();
456 of.lpstrCustomFilter = NULL;
457 of.nFilterIndex = 0;
459 of.lpstrFile = w_file;
460 of.nMaxFile = 2048;
461 of.lpstrFileTitle = NULL;
462 of.lpstrTitle = w_title;
463 of.lpstrDefExt = L"";
464 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;
465 retval = GetSaveFileNameW(&of);
467 if (! retval)
469 if (CommDlgExtendedError())
471 gchar *error = g_strdup_printf(
472 "File dialog box error (%x)", (gint) CommDlgExtendedError());
473 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
474 g_free(error);
476 return NULL;
479 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
481 return g_strdup(tmp);
485 /* initial_dir can be NULL to use the current working directory.
486 * Returns: the selected filename */
487 gchar *win32_show_file_dialog(GtkWindow *parent, const gchar *title, const gchar *initial_file)
489 OPENFILENAMEW of;
490 gint retval;
491 gchar tmp[MAX_PATH];
492 wchar_t w_file[MAX_PATH];
493 wchar_t w_title[512];
495 w_file[0] = '\0';
497 if (initial_file != NULL)
498 MultiByteToWideChar(CP_UTF8, 0, initial_file, -1, w_file, G_N_ELEMENTS(w_file));
500 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
502 /* initialise file dialog info struct */
503 memset(&of, 0, sizeof of);
504 #ifdef OPENFILENAME_SIZE_VERSION_400
505 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
506 #else
507 of.lStructSize = sizeof of;
508 #endif
509 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent)));
511 of.lpstrFile = w_file;
512 of.nMaxFile = 2048;
513 of.lpstrFileTitle = NULL;
514 of.lpstrTitle = w_title;
515 of.lpstrDefExt = L"";
516 of.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER;
517 retval = GetOpenFileNameW(&of);
519 if (! retval)
521 if (CommDlgExtendedError())
523 gchar *error = g_strdup_printf(
524 "File dialog box error (%x)", (gint) CommDlgExtendedError());
525 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
526 g_free(error);
528 return NULL;
531 WideCharToMultiByte(CP_UTF8, 0, w_file, -1, tmp, sizeof(tmp), NULL, NULL);
533 return g_strdup(tmp);
537 void win32_show_font_dialog(void)
539 gint retval;
540 CHOOSEFONT cf;
541 LOGFONT lf; /* logical font structure */
543 memset(&lf, 0, sizeof lf);
544 /* TODO: init lf members */
546 memset(&cf, 0, sizeof cf);
547 cf.lStructSize = sizeof cf;
548 cf.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
549 cf.lpLogFont = &lf;
550 /* support CF_APPLY? */
551 cf.Flags = CF_NOSCRIPTSEL | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
553 retval = ChooseFont(&cf);
555 if (retval)
557 gchar *editorfont = g_strdup_printf("%s %d", lf.lfFaceName, (cf.iPointSize / 10));
558 ui_set_editor_font(editorfont);
559 g_free(editorfont);
564 void win32_show_color_dialog(const gchar *colour)
566 CHOOSECOLOR cc;
567 static COLORREF acr_cust_clr[16];
568 static DWORD rgb_current;
569 gchar *hex = g_malloc0(12);
570 GeanyDocument *doc = document_get_current();
572 /* Initialize CHOOSECOLOR */
573 memset(&cc, 0, sizeof cc);
574 cc.lStructSize = sizeof(cc);
575 cc.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
576 cc.lpCustColors = (LPDWORD) acr_cust_clr;
577 cc.rgbResult = (colour != NULL) ? utils_parse_color_to_bgr(colour) : 0;
578 cc.Flags = CC_FULLOPEN | CC_RGBINIT;
580 if (ChooseColor(&cc))
582 rgb_current = cc.rgbResult;
583 g_snprintf(hex, 11, "#%02X%02X%02X",
584 (guint) (utils_scale_round(GetRValue(rgb_current), 255)),
585 (guint) (utils_scale_round(GetGValue(rgb_current), 255)),
586 (guint) (utils_scale_round(GetBValue(rgb_current), 255)));
588 editor_insert_color(doc->editor, hex);
590 g_free(hex);
594 void win32_show_pref_file_dialog(GtkEntry *item)
596 OPENFILENAMEW of;
597 gint retval, len;
598 wchar_t fname[MAX_PATH];
599 gchar tmp[MAX_PATH];
600 gchar **field, *filename;
602 fname[0] = '\0';
604 /* cut the options from the command line */
605 field = g_strsplit(gtk_entry_get_text(GTK_ENTRY(item)), " ", 2);
606 if (field[0])
608 filename = g_find_program_in_path(field[0]);
609 if (filename != NULL && g_file_test(filename, G_FILE_TEST_EXISTS))
611 MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname, G_N_ELEMENTS(fname));
612 g_free(filename);
616 /* initialize file dialog info struct */
617 memset(&of, 0, sizeof of);
618 #ifdef OPENFILENAME_SIZE_VERSION_400
619 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
620 #else
621 of.lStructSize = sizeof of;
622 #endif
623 of.hwndOwner = GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets.prefs_dialog));
625 of.lpstrFilter = get_filters(FALSE);
626 of.lpstrCustomFilter = NULL;
627 of.nFilterIndex = 1;
629 of.lpstrFile = fname;
630 of.nMaxFile = 2048;
631 of.lpstrFileTitle = NULL;
632 /*of.lpstrInitialDir = g_get_home_dir();*/
633 of.lpstrInitialDir = NULL;
634 of.lpstrTitle = NULL;
635 of.lpstrDefExt = L"exe";
636 of.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_EXPLORER;
637 retval = GetOpenFileNameW(&of);
639 if (!retval)
641 if (CommDlgExtendedError())
643 gchar error[100];
644 g_snprintf(error, sizeof error, "File dialog box error (%x)", (int)CommDlgExtendedError());
645 win32_message_dialog(NULL, GTK_MESSAGE_ERROR, error);
647 g_strfreev(field);
648 return;
651 len = WideCharToMultiByte(CP_UTF8, 0, fname, -1, tmp, sizeof(tmp), NULL, NULL);
652 if ((of.nFileOffset - 1) != len)
654 if (g_strv_length(field) > 1)
655 /* add the command line args of the old command */
656 /** TODO this fails badly when the old command contained spaces, we need quoting here */
657 filename = g_strconcat(tmp, " ", field[1], NULL);
658 else
660 filename = g_strdup(tmp);
662 gtk_entry_set_text(GTK_ENTRY(item), filename);
663 g_free(filename);
665 g_strfreev(field);
669 /* Creates a native Windows message box of the given type and returns always TRUE
670 * or FALSE representing th pressed Yes or No button.
671 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
672 gboolean win32_message_dialog(GtkWidget *parent, GtkMessageType type, const gchar *msg)
674 gboolean ret = TRUE;
675 gint rc;
676 guint t;
677 const gchar *title;
678 HWND parent_hwnd = NULL;
679 static wchar_t w_msg[512];
680 static wchar_t w_title[512];
682 switch (type)
684 case GTK_MESSAGE_ERROR:
686 t = MB_OK | MB_ICONERROR;
687 title = _("Error");
688 break;
690 case GTK_MESSAGE_QUESTION:
692 t = MB_YESNO | MB_ICONQUESTION;
693 title = _("Question");
694 break;
696 case GTK_MESSAGE_WARNING:
698 t = MB_OK | MB_ICONWARNING;
699 title = _("Warning");
700 break;
702 default:
704 t = MB_OK | MB_ICONINFORMATION;
705 title = _("Information");
706 break;
710 /* convert the Unicode chars to wide chars */
711 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
712 MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, G_N_ELEMENTS(w_msg));
713 MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, G_N_ELEMENTS(w_title));
715 if (parent != NULL)
716 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(parent));
717 else if (main_widgets.window != NULL)
718 parent_hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window));
720 /* display the message box */
721 rc = MessageBoxW(parent_hwnd, w_msg, w_title, t);
723 if (type == GTK_MESSAGE_QUESTION && rc != IDYES)
724 ret = FALSE;
726 return ret;
730 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
731 gint win32_check_write_permission(const gchar *dir)
733 static wchar_t w_dir[MAX_PATH];
734 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, G_N_ELEMENTS(w_dir));
735 if (_waccess(w_dir, R_OK | W_OK) != 0)
736 return errno;
737 else
738 return 0;
742 /* Just a simple wrapper function to open a browser window */
743 void win32_open_browser(const gchar *uri)
745 if (strncmp(uri, "file://", 7) == 0)
747 uri += 7;
748 if (strchr(uri, ':') != NULL)
750 while (*uri == '/')
751 uri++;
754 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
758 /* Returns TRUE if the command, which child_pid refers to, returned with a successful exit code,
759 * otherwise FALSE. */
760 gboolean win32_get_exit_status(GPid child_pid)
762 DWORD exit_code;
763 GetExitCodeProcess(child_pid, &exit_code);
765 return (exit_code == 0);
769 static FILE *open_std_handle(DWORD handle, const char *mode)
771 HANDLE lStdHandle;
772 int hConHandle;
773 FILE *fp;
775 lStdHandle = GetStdHandle(handle);
776 if (lStdHandle == INVALID_HANDLE_VALUE)
778 gchar *err = g_win32_error_message(GetLastError());
779 g_warning("GetStdHandle(%ld) failed: %s", (long)handle, err);
780 g_free(err);
781 return NULL;
783 hConHandle = _open_osfhandle((long)lStdHandle, _O_TEXT);
784 if (hConHandle == -1)
786 gchar *err = g_win32_error_message(GetLastError());
787 g_warning("_open_osfhandle(%ld, _O_TEXT) failed: %s", (long)lStdHandle, err);
788 g_free(err);
789 return NULL;
791 fp = _fdopen(hConHandle, mode);
792 if (! fp)
794 gchar *err = g_win32_error_message(GetLastError());
795 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle, mode, err);
796 g_free(err);
797 return NULL;
799 if (setvbuf(fp, NULL, _IONBF, 0) != 0)
801 gchar *err = g_win32_error_message(GetLastError());
802 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp, err);
803 g_free(err);
804 fclose(fp);
805 return NULL;
808 return fp;
812 static void debug_setup_console(void)
814 static const WORD MAX_CONSOLE_LINES = 500;
815 CONSOLE_SCREEN_BUFFER_INFO coninfo;
816 FILE *fp;
818 /* allocate a console for this app */
819 AllocConsole();
821 /* set the screen buffer to be big enough to let us scroll text */
822 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
823 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
824 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
826 /* redirect unbuffered STDOUT to the console */
827 fp = open_std_handle(STD_OUTPUT_HANDLE, "w");
828 if (fp)
829 *stdout = *fp;
831 /* redirect unbuffered STDERR to the console */
832 fp = open_std_handle(STD_ERROR_HANDLE, "w");
833 if (fp)
834 *stderr = *fp;
836 /* redirect unbuffered STDIN to the console */
837 fp = open_std_handle(STD_INPUT_HANDLE, "r");
838 if (fp)
839 *stdin = *fp;
843 void win32_init_debug_code(void)
845 if (app->debug_mode)
847 /* create a console window to get log messages on Windows,
848 * especially useful when generating tags files */
849 debug_setup_console();
850 /* Enable GLib process spawn debug mode when Geany was started with the debug flag */
851 g_setenv("G_SPAWN_WIN32_DEBUG", "1", FALSE);
856 static gchar *create_temp_file(void)
858 gchar *name;
859 gint fd;
861 fd = g_file_open_tmp("tmp_XXXXXX", &name, NULL);
862 if (fd == -1)
863 name = NULL;
864 else
865 close(fd);
867 return name;
871 /* Sometimes this blocks for 30s before aborting when there are several
872 * pages of (error) output and sometimes hangs - see the FIXME.
873 * Also gw_spawn.dwExitCode seems to be not set properly. */
874 /* Process spawning implementation for Windows, by Pierre Joye.
875 * Don't call this function directly, use utils_spawn_[a]sync() instead. */
876 static
877 gboolean _broken_win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
878 gchar **std_out, gchar **std_err, gint *exit_status, GError **error)
880 TCHAR buffer[CMDSIZE]=TEXT("");
881 TCHAR cmdline[CMDSIZE] = TEXT("");
882 TCHAR* lpPart[CMDSIZE]={NULL};
883 DWORD retval = 0;
884 gint argc = 0, i;
885 gint cmdpos = 0;
887 SECURITY_ATTRIBUTES saAttr;
888 BOOL fSuccess;
889 geany_win32_spawn gw_spawn;
891 /* Temp file */
892 HANDLE hStdoutTempFile = NULL;
893 HANDLE hStderrTempFile = NULL;
895 gchar *stdout_content = NULL;
896 gchar *stderr_content = NULL;
898 while (argv[argc])
900 ++argc;
902 g_return_val_if_fail (std_out == NULL ||
903 !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
904 g_return_val_if_fail (std_err == NULL ||
905 !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
907 if (flags & G_SPAWN_SEARCH_PATH)
909 retval = SearchPath(NULL, argv[0], ".exe", sizeof(buffer), buffer, lpPart);
910 if (retval > 0)
911 g_snprintf(cmdline, sizeof(cmdline), "\"%s\"", buffer);
912 else
913 g_strlcpy(cmdline, argv[0], sizeof(cmdline));
914 cmdpos = 1;
917 for (i = cmdpos; i < argc; i++)
919 g_snprintf(cmdline, sizeof(cmdline), "%s %s", cmdline, argv[i]);
920 /*MessageBox(NULL, cmdline, cmdline, MB_OK);*/
923 if (std_err != NULL)
925 hStderrTempFile = GetTempFileHandle(error);
926 if (hStderrTempFile == INVALID_HANDLE_VALUE)
928 gchar *msg = g_win32_error_message(GetLastError());
929 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint) GetLastError());
930 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
931 g_free(msg);
932 return FALSE;
936 if (std_out != NULL)
938 hStdoutTempFile = GetTempFileHandle(error);
939 if (hStdoutTempFile == INVALID_HANDLE_VALUE)
941 gchar *msg = g_win32_error_message(GetLastError());
942 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint) GetLastError());
943 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
944 g_free(msg);
945 return FALSE;
949 /* Set the bInheritHandle flag so pipe handles are inherited. */
950 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
951 saAttr.bInheritHandle = TRUE;
952 saAttr.lpSecurityDescriptor = NULL;
954 /* Get the handle to the current STDOUT and STDERR. */
955 gw_spawn.hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
956 gw_spawn.hStderr = GetStdHandle(STD_ERROR_HANDLE);
957 gw_spawn.dwExitCode = 0;
959 /* Create a pipe for the child process's STDOUT. */
960 if (! CreatePipe(&(gw_spawn.hChildStdoutRd), &(gw_spawn.hChildStdoutWr), &saAttr, 0))
962 gchar *msg = g_win32_error_message(GetLastError());
963 geany_debug("win32_spawn: Stdout pipe creation failed (%d)", (gint) GetLastError());
964 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
965 g_free(msg);
966 return FALSE;
969 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
970 SetHandleInformation(gw_spawn.hChildStdoutRd, HANDLE_FLAG_INHERIT, 0);
972 /* Create a pipe for the child process's STDERR. */
973 if (! CreatePipe(&(gw_spawn.hChildStderrRd), &(gw_spawn.hChildStderrWr), &saAttr, 0))
975 gchar *msg = g_win32_error_message(GetLastError());
976 geany_debug("win32_spawn: Stderr pipe creation failed");
977 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
978 g_free(msg);
979 return FALSE;
982 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
983 SetHandleInformation(gw_spawn.hChildStderrRd, HANDLE_FLAG_INHERIT, 0);
985 /* Create a pipe for the child process's STDIN. */
986 if (! CreatePipe(&(gw_spawn.hChildStdinRd), &(gw_spawn.hChildStdinWr), &saAttr, 0))
988 gchar *msg = g_win32_error_message(GetLastError());
989 geany_debug("win32_spawn: Stdin pipe creation failed");
990 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
991 g_free(msg);
992 return FALSE;
995 /* Ensure that the write handle to the child process's pipe for STDIN is not inherited. */
996 SetHandleInformation(gw_spawn.hChildStdinWr, HANDLE_FLAG_INHERIT, 0);
998 /* Now create the child process. */
999 fSuccess = CreateChildProcess(&gw_spawn, cmdline, dir, error);
1000 if (exit_status)
1002 *exit_status = gw_spawn.dwExitCode;
1005 if (! fSuccess)
1007 geany_debug("win32_spawn: Create process failed");
1008 return FALSE;
1011 /* Read from pipe that is the standard output for child process. */
1012 if (std_out != NULL)
1014 ReadFromPipe(gw_spawn.hChildStdoutRd, gw_spawn.hChildStdoutWr, hStdoutTempFile, error);
1015 if (! GetContentFromHandle(hStdoutTempFile, &stdout_content, error))
1017 return FALSE;
1019 *std_out = stdout_content;
1022 if (std_err != NULL)
1024 ReadFromPipe(gw_spawn.hChildStderrRd, gw_spawn.hChildStderrWr, hStderrTempFile, error);
1025 if (! GetContentFromHandle(hStderrTempFile, &stderr_content, error))
1027 return FALSE;
1029 *std_err = stderr_content;
1031 return TRUE;
1035 /* Note: g_spawn is broken for receiving both stdio and stderr e.g. when
1036 * running make and there are compile errors. See glib/giowin32.c header
1037 * comment about Windows bugs, e.g. #338943 */
1038 /* Simple replacement for _broken_win32_spawn().
1039 * flags is ignored, G_SPAWN_SEARCH_PATH is implied.
1040 * Don't call this function directly, use utils_spawn_[a]sync() instead.
1041 * Adapted from tm_workspace_create_global_tags(). */
1042 gboolean win32_spawn(const gchar *dir, gchar **argv, gchar **env, GSpawnFlags flags,
1043 gchar **std_out, gchar **std_err, gint *exit_status, GError **error)
1045 gint ret;
1046 gboolean fail;
1047 gchar *tmp_file = create_temp_file();
1048 gchar *tmp_errfile = create_temp_file();
1049 gchar *command;
1050 gchar *locale_command;
1052 if (env != NULL)
1054 return _broken_win32_spawn(dir, argv, env, flags, std_out, std_err,
1055 exit_status, error);
1057 if (!tmp_file || !tmp_errfile)
1059 g_warning("%s: Could not create temporary files!", G_STRFUNC);
1060 return FALSE;
1062 command = g_strjoinv(" ", argv);
1063 SETPTR(command, g_strdup_printf("cmd.exe /S /C \"%s >%s 2>%s\"",
1064 command, tmp_file, tmp_errfile));
1065 locale_command = g_locale_from_utf8(command, -1, NULL, NULL, NULL);
1066 if (! locale_command)
1067 locale_command = g_strdup(command);
1068 geany_debug("WIN32: actually running command:\n%s", command);
1069 g_chdir(dir);
1070 errno = 0;
1071 ret = system(locale_command);
1072 /* the command can return -1 as an exit code, so check errno also */
1073 fail = ret == -1 && errno;
1074 if (!fail)
1076 if (std_out != NULL)
1077 g_file_get_contents(tmp_file, std_out, NULL, NULL);
1078 if (std_err != NULL)
1079 g_file_get_contents(tmp_errfile, std_err, NULL, NULL);
1081 else if (error)
1082 g_set_error_literal(error, G_SPAWN_ERROR, errno, g_strerror(errno));
1084 g_free(command);
1085 g_free(locale_command);
1086 g_unlink(tmp_file);
1087 g_free(tmp_file);
1088 g_unlink(tmp_errfile);
1089 g_free(tmp_errfile);
1090 if (exit_status)
1091 *exit_status = ret;
1093 return !fail;
1097 static gboolean GetContentFromHandle(HANDLE hFile, gchar **content, GError **error)
1099 DWORD filesize;
1100 gchar * buffer;
1101 DWORD dwRead;
1103 filesize = GetFileSize(hFile, NULL);
1104 if (filesize < 1)
1106 *content = NULL;
1107 return TRUE;
1110 buffer = g_malloc(filesize + 1);
1111 if (! buffer)
1113 gchar *msg = g_win32_error_message(GetLastError());
1114 geany_debug("GetContentFromHandle: Alloc failed");
1115 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR, "%s", msg);
1116 g_free(msg);
1117 return FALSE;
1120 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
1121 if (! ReadFile(hFile, buffer, filesize, &dwRead, NULL) || dwRead == 0)
1123 gchar *msg = g_win32_error_message(GetLastError());
1124 geany_debug("GetContentFromHandle: Cannot read tempfile");
1125 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1126 g_free(msg);
1127 return FALSE;
1130 if (! CloseHandle(hFile))
1132 gchar *msg = g_win32_error_message(GetLastError());
1133 geany_debug("GetContentFromHandle: CloseHandle failed (%d)", (gint) GetLastError());
1134 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1135 g_free(msg);
1136 g_free(buffer);
1137 *content = NULL;
1138 return FALSE;
1140 buffer[filesize] = '\0';
1141 *content = buffer;
1142 return TRUE;
1146 gchar *win32_expand_environment_variables(const gchar *str)
1148 gchar expCmdline[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
1150 if (ExpandEnvironmentStrings((LPCTSTR) str, (LPTSTR) expCmdline, sizeof(expCmdline)) != 0)
1151 return g_strdup(expCmdline);
1152 else
1153 return g_strdup(str);
1157 static gboolean CreateChildProcess(geany_win32_spawn *gw_spawn, TCHAR *szCmdline,
1158 const TCHAR *dir, GError **error)
1160 PROCESS_INFORMATION piProcInfo;
1161 STARTUPINFOW siStartInfo;
1162 BOOL bFuncRetn = FALSE;
1163 gchar *expandedCmdline;
1164 wchar_t w_commandline[CMDSIZE];
1165 wchar_t w_dir[MAX_PATH];
1167 /* Set up members of the PROCESS_INFORMATION structure. */
1168 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1170 /* Set up members of the STARTUPINFO structure.*/
1171 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
1173 siStartInfo.cb = sizeof(STARTUPINFO);
1174 siStartInfo.hStdError = gw_spawn->hChildStderrWr;
1175 siStartInfo.hStdOutput = gw_spawn->hChildStdoutWr;
1176 siStartInfo.hStdInput = gw_spawn->hChildStdinRd;
1177 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1179 /* Expand environment variables like %blah%. */
1180 expandedCmdline = win32_expand_environment_variables(szCmdline);
1182 MultiByteToWideChar(CP_UTF8, 0, expandedCmdline, -1, w_commandline, G_N_ELEMENTS(w_commandline));
1183 MultiByteToWideChar(CP_UTF8, 0, dir, -1, w_dir, G_N_ELEMENTS(w_dir));
1185 /* Create the child process. */
1186 bFuncRetn = CreateProcessW(NULL,
1187 w_commandline, /* command line */
1188 NULL, /* process security attributes */
1189 NULL, /* primary thread security attributes */
1190 TRUE, /* handles are inherited */
1191 CREATE_NO_WINDOW, /* creation flags */
1192 NULL, /* use parent's environment */
1193 w_dir, /* use parent's current directory */
1194 &siStartInfo, /* STARTUPINFO pointer */
1195 &piProcInfo); /* receives PROCESS_INFORMATION */
1197 g_free(expandedCmdline);
1199 if (bFuncRetn == 0)
1201 gchar *msg = g_win32_error_message(GetLastError());
1202 geany_debug("CreateChildProcess: CreateProcess failed (%s)", msg);
1203 g_set_error(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, "%s", msg);
1204 g_free(msg);
1205 return FALSE;
1207 else
1209 gint i;
1210 gsize ms = 30*1000;
1212 /* FIXME: this seems to timeout when there are many lines
1213 * to read - maybe because the child's pipe is full */
1214 for (i = 0; i < 2 &&
1215 WaitForSingleObject(piProcInfo.hProcess, ms) == WAIT_TIMEOUT; i++)
1217 ui_set_statusbar(FALSE, _("Process timed out after %.02f s!"), ms / 1000.0F);
1218 geany_debug("CreateChildProcess: timed out");
1219 TerminateProcess(piProcInfo.hProcess, WAIT_TIMEOUT); /* NOTE: This will not kill grandkids. */
1222 if (!GetExitCodeProcess(piProcInfo.hProcess, &gw_spawn->dwExitCode))
1224 gchar *msg = g_win32_error_message(GetLastError());
1225 geany_debug("GetExitCodeProcess failed: %s", msg);
1226 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_FAILED, "%s", msg);
1227 g_free(msg);
1229 CloseHandle(piProcInfo.hProcess);
1230 CloseHandle(piProcInfo.hThread);
1231 return bFuncRetn;
1233 return FALSE;
1237 static VOID ReadFromPipe(HANDLE hRead, HANDLE hWrite, HANDLE hFile, GError **error)
1239 DWORD dwRead, dwWritten;
1240 CHAR chBuf[BUFSIZE];
1242 /* Close the write end of the pipe before reading from the
1243 read end of the pipe. */
1244 if (! CloseHandle(hWrite))
1246 gchar *msg = g_win32_error_message(GetLastError());
1247 geany_debug("ReadFromPipe: Closing handle failed");
1248 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR_PIPE, "%s", msg);
1249 g_free(msg);
1250 return;
1253 /* Read output from the child process, and write to parent's STDOUT. */
1254 for (;;)
1256 if (! ReadFile(hRead, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0)
1257 break;
1259 if (! WriteFile(hFile, chBuf, dwRead, &dwWritten, NULL))
1260 break;
1265 static HANDLE GetTempFileHandle(GError **error)
1267 /* Temp file */
1268 DWORD dwBufSize = BUFSIZE;
1269 UINT uRetVal;
1270 TCHAR szTempName[BUFSIZE];
1271 TCHAR lpPathBuffer[BUFSIZE];
1272 DWORD dwRetVal;
1273 HANDLE hTempFile;
1275 /* Get the temp path. */
1276 dwRetVal = GetTempPath(dwBufSize, /* length of the buffer*/
1277 lpPathBuffer); /* buffer for path */
1279 if (dwRetVal > dwBufSize || (dwRetVal == 0))
1281 gchar *msg = g_win32_error_message(GetLastError());
1282 geany_debug("GetTempFileHandle: GetTempPath failed (%d)", (gint) GetLastError());
1283 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1284 g_free(msg);
1285 return NULL;
1288 /* Create a temporary file for STDOUT. */
1289 uRetVal = GetTempFileName(lpPathBuffer, /* directory for tmp files */
1290 TEXT("GEANY_VCDIFF_"), /* temp file name prefix */
1291 0, /* create unique name */
1292 szTempName); /* buffer for name */
1293 if (uRetVal == 0)
1295 gchar *msg = g_win32_error_message(GetLastError());
1296 geany_debug("GetTempFileName failed (%d)", (gint) GetLastError());
1297 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1298 g_free(msg);
1299 return NULL;
1302 hTempFile = CreateFile((LPTSTR) szTempName, /* file name */
1303 GENERIC_READ | GENERIC_WRITE, /* open r-w */
1304 0, /* do not share */
1305 NULL, /* default security */
1306 CREATE_ALWAYS, /* overwrite existing */
1307 FILE_ATTRIBUTE_NORMAL,/* normal file */
1308 NULL); /* no template */
1310 if (hTempFile == INVALID_HANDLE_VALUE)
1312 gchar *msg = g_win32_error_message(GetLastError());
1313 geany_debug("GetTempFileHandle: Second CreateFile failed (%d)", (gint) GetLastError());
1314 g_set_error(error, G_SPAWN_ERROR, G_FILE_ERROR, "%s", msg);
1315 g_free(msg);
1316 return NULL;
1318 return hTempFile;
1322 /* From GDK (they got it from MS Knowledge Base article Q130698) */
1323 static gboolean resolve_link(HWND hWnd, wchar_t *link, gchar **lpszPath)
1325 WIN32_FILE_ATTRIBUTE_DATA wfad;
1326 HRESULT hres;
1327 IShellLinkW *pslW = NULL;
1328 IPersistFile *ppf = NULL;
1329 LPVOID pslWV = NULL;
1330 LPVOID ppfV = NULL;
1332 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
1333 * with an empty file and returns an empty "link target". (#524151) */
1334 if (!GetFileAttributesExW(link, GetFileExInfoStandard, &wfad) ||
1335 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1337 return FALSE;
1340 /* Assume failure to start with: */
1341 *lpszPath = 0;
1343 CoInitialize(NULL);
1345 hres = CoCreateInstance(
1346 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &pslWV);
1348 if (SUCCEEDED(hres))
1350 /* The IShellLink interface supports the IPersistFile interface.
1351 * Get an interface pointer to it. */
1352 pslW = (IShellLinkW*) pslWV;
1353 hres = pslW->lpVtbl->QueryInterface(pslW, &IID_IPersistFile, &ppfV);
1356 if (SUCCEEDED(hres))
1358 /* Load the file. */
1359 ppf = (IPersistFile*) ppfV;
1360 hres = ppf->lpVtbl->Load(ppf, link, STGM_READ);
1363 if (SUCCEEDED(hres))
1365 /* Resolve the link by calling the Resolve() interface function. */
1366 hres = pslW->lpVtbl->Resolve(pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1369 if (SUCCEEDED(hres))
1371 wchar_t wtarget[MAX_PATH];
1373 hres = pslW->lpVtbl->GetPath(pslW, wtarget, MAX_PATH, NULL, 0);
1374 if (SUCCEEDED(hres))
1375 *lpszPath = g_utf16_to_utf8(wtarget, -1, NULL, NULL, NULL);
1378 if (ppf)
1379 ppf->lpVtbl->Release(ppf);
1381 if (pslW)
1382 pslW->lpVtbl->Release(pslW);
1384 return SUCCEEDED(hres);
1388 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
1389 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
1390 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
1391 gchar *win32_get_shortcut_target(const gchar *file_name)
1393 gchar *path = NULL;
1394 wchar_t *wfilename = g_utf8_to_utf16(file_name, -1, NULL, NULL, NULL);
1396 resolve_link(GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets.window)), wfilename, &path);
1397 g_free(wfilename);
1399 if (path == NULL)
1400 return g_strdup(file_name);
1401 else
1402 return path;
1406 void win32_set_working_directory(const gchar *dir)
1408 SetCurrentDirectory(dir);
1412 gchar *win32_get_installation_dir(void)
1414 return g_win32_get_package_installation_directory_of_module(NULL);
1418 #endif