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.
37 #include "filetypes.h"
51 #define WIN32_LEAN_AND_MEAN
57 #include <glib/gstdio.h>
58 #include <gdk/gdkwin32.h>
63 struct _geany_win32_spawn
67 HANDLE hChildStdoutRd
;
68 HANDLE hChildStdoutWr
;
69 HANDLE hChildStderrRd
;
70 HANDLE hChildStderrWr
;
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 /* The timer handle used to refresh windows below modal native dialogs. If
87 * ever more than one dialog can be shown at a time, this needs to be changed
88 * to be for specific dialogs. */
89 static UINT_PTR dialog_timer
= 0;
92 G_INLINE_FUNC
void win32_dialog_reset_timer(HWND hwnd
)
94 if (G_UNLIKELY(dialog_timer
!= 0))
96 KillTimer(hwnd
, dialog_timer
);
103 win32_dialog_update_main_window(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
107 /* Pump the main window loop a bit, but not enough to lock-up.
108 * The typical `while(gtk_events_pending()) gtk_main_iteration();`
109 * loop causes the entire operating system to lock-up. */
110 for (i
= 0; i
< 4 && gtk_events_pending(); i
++)
111 gtk_main_iteration();
113 /* Cancel any pending timers since we just did an update */
114 win32_dialog_reset_timer(hwnd
);
118 G_INLINE_FUNC UINT_PTR
win32_dialog_queue_main_window_redraw(HWND dlg
, UINT msg
,
119 WPARAM wParam
, LPARAM lParam
, gboolean postpone
)
123 /* Messages that likely mean the window below a dialog needs to be re-drawn. */
124 case WM_WINDOWPOSCHANGED
:
127 case WM_THEMECHANGED
:
130 win32_dialog_reset_timer(dlg
);
131 dialog_timer
= SetTimer(dlg
, 0, 33 /* around 30fps */, win32_dialog_update_main_window
);
134 win32_dialog_update_main_window(dlg
, msg
, wParam
, lParam
);
137 return 0; /* always let the default proc handle it */
141 /* This function is called for OPENFILENAME lpfnHook function and it establishes
142 * a timer that is reset each time which will update the main window loop eventually. */
143 UINT_PTR CALLBACK
win32_dialog_explorer_hook_proc(HWND dlg
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
145 return win32_dialog_queue_main_window_redraw(dlg
, msg
, wParam
, lParam
, TRUE
);
149 /* This function is called for old-school win32 dialogs that accept a proper
150 * lpfnHook function for all messages, it doesn't use a timer. */
151 UINT_PTR CALLBACK
win32_dialog_hook_proc(HWND dlg
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
153 return win32_dialog_queue_main_window_redraw(dlg
, msg
, wParam
, lParam
, FALSE
);
157 static wchar_t *get_file_filters(void)
161 static wchar_t title
[4096];
162 GString
*str
= g_string_sized_new(100);
163 GString
*all_patterns
= g_string_sized_new(100);
167 /* create meta file filter "All files" */
168 g_string_append_printf(str
, "%s\t*\t", _("All files"));
169 /* create meta file filter "All Source" (skip GEANY_FILETYPES_NONE) */
170 for (i
= GEANY_FILETYPES_NONE
+ 1; i
< filetypes_array
->len
; i
++)
172 for (j
= 0; filetypes
[i
]->pattern
[j
] != NULL
; j
++)
174 g_string_append(all_patterns
, filetypes
[i
]->pattern
[j
]);
175 g_string_append_c(all_patterns
, ';');
178 g_string_append_printf(str
, "%s\t%s\t", _("All Source"), all_patterns
->str
);
179 g_string_free(all_patterns
, TRUE
);
180 /* add 'usual' filetypes */
181 foreach_slist(node
, filetypes_by_title
)
183 GeanyFiletype
*ft
= node
->data
;
185 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
187 tmp
= g_strjoinv(";", ft
->pattern
);
188 g_string_append_printf(str
, "%s\t%s\t", ft
->title
, tmp
);
191 g_string_append_c(str
, '\t'); /* the final \0 byte to mark the end of the string */
193 g_string_free(str
, FALSE
);
195 /* replace all "\t"s by \0 */
196 len
= strlen(string
);
197 g_strdelimit(string
, "\t", '\0');
198 g_assert(string
[len
- 1] == 0x0);
199 MultiByteToWideChar(CP_UTF8
, 0, string
, len
, title
, G_N_ELEMENTS(title
));
206 static wchar_t *get_file_filter_all_files(void)
209 static wchar_t title
[4096];
212 /* create meta file filter "All files" */
213 filter
= g_strdup_printf("%s\t*\t", _("All files"));
215 len
= strlen(filter
);
216 g_strdelimit(filter
, "\t", '\0');
217 g_assert(filter
[len
- 1] == 0x0);
218 MultiByteToWideChar(CP_UTF8
, 0, filter
, len
, title
, G_N_ELEMENTS(title
));
225 static wchar_t *get_filters(gboolean project_files
)
229 static wchar_t title
[1024];
233 string
= g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT
, "\t",
234 _("All files"), "\t", "*", "\t", NULL
);
238 string
= g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
239 _("All files"), "\t", "*", "\t", NULL
);
242 /* replace all "\t"s by \0 */
243 len
= strlen(string
);
244 g_strdelimit(string
, "\t", '\0');
245 g_assert(string
[len
- 1] == 0x0);
246 MultiByteToWideChar(CP_UTF8
, 0, string
, len
, title
, G_N_ELEMENTS(title
));
253 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
254 * returns the directory part of the given filename. */
255 static wchar_t *get_dir_for_path(const gchar
*utf8_filename
)
257 static wchar_t w_dir
[MAX_PATH
];
260 if (g_file_test(utf8_filename
, G_FILE_TEST_IS_DIR
))
261 result
= (gchar
*) utf8_filename
;
263 result
= g_path_get_dirname(utf8_filename
);
265 MultiByteToWideChar(CP_UTF8
, 0, result
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
267 if (result
!= utf8_filename
)
274 /* Callback function for setting the initial directory of the folder open dialog. This could also
275 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
276 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
277 * folder when the dialog is initialised. Yeah, I like Windows. */
278 INT CALLBACK
BrowseCallbackProc(HWND hwnd
, UINT uMsg
, LPARAM lp
, LPARAM pData
)
280 win32_dialog_hook_proc(hwnd
, uMsg
, lp
, pData
);
283 case BFFM_INITIALIZED
:
285 SendMessageW(hwnd
, BFFM_SETSELECTIONW
, TRUE
, pData
);
288 case BFFM_SELCHANGED
:
290 /* set the status window to the currently selected path. */
291 static wchar_t szDir
[MAX_PATH
];
292 if (SHGetPathFromIDListW((LPITEMIDLIST
) lp
, szDir
))
294 SendMessageW(hwnd
, BFFM_SETSTATUSTEXTW
, 0, (LPARAM
) szDir
);
303 /* Shows a folder selection dialog.
304 * initial_dir is expected in UTF-8
305 * The selected folder name is returned. */
306 gchar
*win32_show_folder_dialog(GtkWidget
*parent
, const gchar
*title
, const gchar
*initial_dir
)
310 gchar
*result
= NULL
;
311 wchar_t fname
[MAX_PATH
];
312 wchar_t w_title
[512];
314 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
317 parent
= main_widgets
.window
;
319 memset(&bi
, 0, sizeof bi
);
320 bi
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(parent
));
322 bi
.lpszTitle
= w_title
;
323 bi
.lpfn
= BrowseCallbackProc
;
324 bi
.lParam
= (LPARAM
) get_dir_for_path(initial_dir
);
325 bi
.ulFlags
= BIF_DONTGOBELOWDOMAIN
| BIF_RETURNONLYFSDIRS
| BIF_STATUSTEXT
;
327 pidl
= SHBrowseForFolderW(&bi
);
329 /* convert the strange Windows folder list item something into an usual path string ;-) */
332 if (SHGetPathFromIDListW(pidl
, fname
))
334 result
= g_malloc0(MAX_PATH
* 2);
335 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, result
, MAX_PATH
* 2, NULL
, NULL
);
337 /* SHBrowseForFolder() probably leaks memory here, but how to free the allocated memory? */
343 /* Shows a file open dialog.
344 * If allow_new_file is set, the file to be opened doesn't have to exist.
345 * initial_dir is expected in UTF-8
346 * The selected file name is returned.
347 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
348 * files, a filter for executables otherwise. */
349 gchar
*win32_show_project_open_dialog(GtkWidget
*parent
, const gchar
*title
,
350 const gchar
*initial_dir
, gboolean allow_new_file
,
351 gboolean project_file_filter
)
355 wchar_t fname
[MAX_PATH
];
356 wchar_t w_title
[512];
361 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
364 parent
= main_widgets
.window
;
366 /* initialise file dialog info struct */
367 memset(&of
, 0, sizeof of
);
368 #ifdef OPENFILENAME_SIZE_VERSION_400
369 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
371 of
.lStructSize
= sizeof of
;
373 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(parent
));
374 of
.lpstrFilter
= get_filters(project_file_filter
);
376 of
.lpstrCustomFilter
= NULL
;
378 of
.lpstrFile
= fname
;
379 of
.lpstrInitialDir
= get_dir_for_path(initial_dir
);
381 of
.lpstrFileTitle
= NULL
;
382 of
.lpstrTitle
= w_title
;
383 of
.lpstrDefExt
= L
"";
384 of
.Flags
= OFN_PATHMUSTEXIST
| OFN_EXPLORER
| OFN_HIDEREADONLY
| OFN_ENABLEHOOK
;
385 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
386 if (! allow_new_file
)
387 of
.Flags
|= OFN_FILEMUSTEXIST
;
389 retval
= GetOpenFileNameW(&of
);
393 if (CommDlgExtendedError())
396 error
= g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
397 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
402 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
403 filename
= g_malloc0(MAX_PATH
* 2);
404 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, filename
, MAX_PATH
* 2, NULL
, NULL
);
410 /* initial_dir can be NULL to use the current working directory.
411 * Returns: TRUE if the dialog was not cancelled. */
412 gboolean
win32_show_document_open_dialog(GtkWindow
*parent
, const gchar
*title
, const gchar
*initial_dir
)
418 wchar_t fname
[MAX_PATH
];
419 wchar_t w_dir
[MAX_PATH
];
420 wchar_t w_title
[512];
424 if (initial_dir
!= NULL
)
425 MultiByteToWideChar(CP_UTF8
, 0, initial_dir
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
427 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
429 /* initialise file dialog info struct */
430 memset(&of
, 0, sizeof of
);
431 #ifdef OPENFILENAME_SIZE_VERSION_400
432 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
434 of
.lStructSize
= sizeof of
;
436 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent
)));
437 of
.lpstrFilter
= get_file_filters();
439 of
.lpstrCustomFilter
= NULL
;
440 of
.nFilterIndex
= GEANY_FILETYPES_NONE
+ 1;
441 of
.lpstrFile
= fname
;
442 of
.lpstrInitialDir
= (initial_dir
!= NULL
) ? w_dir
: NULL
;
444 of
.lpstrFileTitle
= NULL
;
445 of
.lpstrTitle
= w_title
;
446 of
.lpstrDefExt
= L
"";
447 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
| OFN_ENABLEHOOK
;
448 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
450 retval
= GetOpenFileNameW(&of
);
454 if (CommDlgExtendedError())
457 g_snprintf(error
, sizeof error
, "File dialog box error (%x)", (int)CommDlgExtendedError());
458 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
463 x
= of
.nFileOffset
- 1;
464 if (x
!= wcslen(fname
))
465 { /* open a single file */
466 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
467 document_open_file(tmp
, of
.Flags
& OFN_READONLY
, NULL
, NULL
);
470 { /* open multiple files */
471 gchar file_name
[MAX_PATH
];
472 gchar dir_name
[MAX_PATH
];
474 WideCharToMultiByte(CP_UTF8
, 0, fname
, of
.nFileOffset
,
475 dir_name
, sizeof(dir_name
), NULL
, NULL
);
483 WideCharToMultiByte(CP_UTF8
, 0, fname
+ x
+ 1, -1,
484 tmp
, sizeof(tmp
), NULL
, NULL
);
485 g_snprintf(file_name
, 511, "%s\\%s", dir_name
, tmp
);
487 /* convert the resulting filename into UTF-8 */
488 document_open_file(file_name
, of
.Flags
& OFN_READONLY
, NULL
, NULL
);
493 return (retval
!= 0);
497 gchar
*win32_show_document_save_as_dialog(GtkWindow
*parent
, const gchar
*title
,
503 wchar_t w_file
[MAX_PATH
];
504 wchar_t w_title
[512];
509 /* Convert the name of the file for of.lpstrFile */
510 n
= MultiByteToWideChar(CP_UTF8
, 0, DOC_FILENAME(doc
), -1, w_file
, G_N_ELEMENTS(w_file
));
512 /* If creating a new file name, convert and append the extension if any */
513 if (! doc
->file_name
&& doc
->file_type
&& doc
->file_type
->extension
&&
514 n
+ 1 < (int)G_N_ELEMENTS(w_file
))
516 w_file
[n
- 1] = L
'.';
517 MultiByteToWideChar(CP_UTF8
, 0, doc
->file_type
->extension
, -1, &w_file
[n
],
518 G_N_ELEMENTS(w_file
) - n
- 1);
521 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
523 /* initialise file dialog info struct */
524 memset(&of
, 0, sizeof of
);
525 #ifdef OPENFILENAME_SIZE_VERSION_400
526 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
528 of
.lStructSize
= sizeof of
;
530 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent
)));
532 of
.lpstrFilter
= get_file_filter_all_files();
533 of
.lpstrCustomFilter
= NULL
;
536 of
.lpstrFile
= w_file
;
538 of
.lpstrFileTitle
= NULL
;
539 of
.lpstrTitle
= w_title
;
540 of
.lpstrDefExt
= L
"";
541 of
.Flags
= OFN_OVERWRITEPROMPT
| OFN_EXPLORER
| OFN_ENABLEHOOK
;
542 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
543 retval
= GetSaveFileNameW(&of
);
547 if (CommDlgExtendedError())
549 gchar
*error
= g_strdup_printf(
550 "File dialog box error (%x)", (gint
) CommDlgExtendedError());
551 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
557 WideCharToMultiByte(CP_UTF8
, 0, w_file
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
559 return g_strdup(tmp
);
563 /* initial_dir can be NULL to use the current working directory.
564 * Returns: the selected filename */
565 gchar
*win32_show_file_dialog(GtkWindow
*parent
, const gchar
*title
, const gchar
*initial_file
)
570 wchar_t w_file
[MAX_PATH
];
571 wchar_t w_title
[512];
575 if (initial_file
!= NULL
)
576 MultiByteToWideChar(CP_UTF8
, 0, initial_file
, -1, w_file
, G_N_ELEMENTS(w_file
));
578 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
580 /* initialise file dialog info struct */
581 memset(&of
, 0, sizeof of
);
582 #ifdef OPENFILENAME_SIZE_VERSION_400
583 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
585 of
.lStructSize
= sizeof of
;
587 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(GTK_WIDGET(parent
)));
589 of
.lpstrFile
= w_file
;
591 of
.lpstrFileTitle
= NULL
;
592 of
.lpstrTitle
= w_title
;
593 of
.lpstrDefExt
= L
"";
594 of
.Flags
= OFN_FILEMUSTEXIST
| OFN_EXPLORER
| OFN_ENABLEHOOK
;
595 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
596 retval
= GetOpenFileNameW(&of
);
600 if (CommDlgExtendedError())
602 gchar
*error
= g_strdup_printf(
603 "File dialog box error (%x)", (gint
) CommDlgExtendedError());
604 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
610 WideCharToMultiByte(CP_UTF8
, 0, w_file
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
612 return g_strdup(tmp
);
616 void win32_show_font_dialog(void)
620 LOGFONT lf
; /* logical font structure */
622 memset(&lf
, 0, sizeof lf
);
623 /* TODO: init lf members */
625 memset(&cf
, 0, sizeof cf
);
626 cf
.lStructSize
= sizeof cf
;
627 cf
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets
.window
));
629 /* support CF_APPLY? */
630 cf
.Flags
= CF_NOSCRIPTSEL
| CF_FORCEFONTEXIST
| CF_INITTOLOGFONTSTRUCT
| CF_SCREENFONTS
| CF_ENABLEHOOK
;
631 cf
.lpfnHook
= win32_dialog_hook_proc
;
633 retval
= ChooseFont(&cf
);
637 gchar
*editorfont
= g_strdup_printf("%s %d", lf
.lfFaceName
, (cf
.iPointSize
/ 10));
638 ui_set_editor_font(editorfont
);
644 void win32_show_color_dialog(const gchar
*colour
)
647 static COLORREF acr_cust_clr
[16];
648 static DWORD rgb_current
;
649 gchar
*hex
= g_malloc0(12);
650 GeanyDocument
*doc
= document_get_current();
652 /* Initialize CHOOSECOLOR */
653 memset(&cc
, 0, sizeof cc
);
654 cc
.lStructSize
= sizeof(cc
);
655 cc
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets
.window
));
656 cc
.lpCustColors
= (LPDWORD
) acr_cust_clr
;
657 cc
.rgbResult
= (colour
!= NULL
) ? utils_parse_color_to_bgr(colour
) : 0;
658 cc
.Flags
= CC_FULLOPEN
| CC_RGBINIT
| CC_ENABLEHOOK
;
659 cc
.lpfnHook
= win32_dialog_hook_proc
;
661 if (ChooseColor(&cc
))
663 rgb_current
= cc
.rgbResult
;
664 g_snprintf(hex
, 11, "#%02X%02X%02X",
665 (guint
) (utils_scale_round(GetRValue(rgb_current
), 255)),
666 (guint
) (utils_scale_round(GetGValue(rgb_current
), 255)),
667 (guint
) (utils_scale_round(GetBValue(rgb_current
), 255)));
669 editor_insert_color(doc
->editor
, hex
);
675 void win32_show_pref_file_dialog(GtkEntry
*item
)
679 wchar_t fname
[MAX_PATH
];
681 gchar
**field
, *filename
;
685 /* cut the options from the command line */
686 field
= g_strsplit(gtk_entry_get_text(GTK_ENTRY(item
)), " ", 2);
689 filename
= g_find_program_in_path(field
[0]);
690 if (filename
!= NULL
&& g_file_test(filename
, G_FILE_TEST_EXISTS
))
692 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, fname
, G_N_ELEMENTS(fname
));
697 /* initialize file dialog info struct */
698 memset(&of
, 0, sizeof of
);
699 #ifdef OPENFILENAME_SIZE_VERSION_400
700 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
702 of
.lStructSize
= sizeof of
;
704 of
.hwndOwner
= GDK_WINDOW_HWND(gtk_widget_get_window(ui_widgets
.prefs_dialog
));
706 of
.lpstrFilter
= get_filters(FALSE
);
707 of
.lpstrCustomFilter
= NULL
;
710 of
.lpstrFile
= fname
;
712 of
.lpstrFileTitle
= NULL
;
713 /*of.lpstrInitialDir = g_get_home_dir();*/
714 of
.lpstrInitialDir
= NULL
;
715 of
.lpstrTitle
= NULL
;
716 of
.lpstrDefExt
= L
"exe";
717 of
.Flags
= OFN_HIDEREADONLY
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
| OFN_ENABLEHOOK
;
718 of
.lpfnHook
= win32_dialog_explorer_hook_proc
;
719 retval
= GetOpenFileNameW(&of
);
723 if (CommDlgExtendedError())
726 g_snprintf(error
, sizeof error
, "File dialog box error (%x)", (int)CommDlgExtendedError());
727 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
733 len
= WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
734 if ((of
.nFileOffset
- 1) != len
)
736 if (g_strv_length(field
) > 1)
737 /* add the command line args of the old command */
738 /** TODO this fails badly when the old command contained spaces, we need quoting here */
739 filename
= g_strconcat(tmp
, " ", field
[1], NULL
);
742 filename
= g_strdup(tmp
);
744 gtk_entry_set_text(GTK_ENTRY(item
), filename
);
751 /* Creates a native Windows message box of the given type and returns always TRUE
752 * or FALSE representing th pressed Yes or No button.
753 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
754 gboolean
win32_message_dialog(GtkWidget
*parent
, GtkMessageType type
, const gchar
*msg
)
760 HWND parent_hwnd
= NULL
;
761 static wchar_t w_msg
[512];
762 static wchar_t w_title
[512];
766 case GTK_MESSAGE_ERROR
:
768 t
= MB_OK
| MB_ICONERROR
;
772 case GTK_MESSAGE_QUESTION
:
774 t
= MB_YESNO
| MB_ICONQUESTION
;
775 title
= _("Question");
778 case GTK_MESSAGE_WARNING
:
780 t
= MB_OK
| MB_ICONWARNING
;
781 title
= _("Warning");
786 t
= MB_OK
| MB_ICONINFORMATION
;
787 title
= _("Information");
792 /* convert the Unicode chars to wide chars */
793 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
794 MultiByteToWideChar(CP_UTF8
, 0, msg
, -1, w_msg
, G_N_ELEMENTS(w_msg
));
795 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
798 parent_hwnd
= GDK_WINDOW_HWND(gtk_widget_get_window(parent
));
799 else if (main_widgets
.window
!= NULL
)
800 parent_hwnd
= GDK_WINDOW_HWND(gtk_widget_get_window(main_widgets
.window
));
802 /* display the message box */
803 rc
= MessageBoxW(parent_hwnd
, w_msg
, w_title
, t
);
805 if (type
== GTK_MESSAGE_QUESTION
&& rc
!= IDYES
)
812 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
813 gint
win32_check_write_permission(const gchar
*dir
)
815 static wchar_t w_dir
[MAX_PATH
];
816 MultiByteToWideChar(CP_UTF8
, 0, dir
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
817 if (_waccess(w_dir
, R_OK
| W_OK
) != 0)
824 /* Just a simple wrapper function to open a browser window */
825 void win32_open_browser(const gchar
*uri
)
827 if (strncmp(uri
, "file://", 7) == 0)
830 if (strchr(uri
, ':') != NULL
)
836 ShellExecute(NULL
, "open", uri
, NULL
, NULL
, SW_SHOWNORMAL
);
840 /* Returns TRUE if the command, which child_pid refers to, returned with a successful exit code,
841 * otherwise FALSE. */
842 gboolean
win32_get_exit_status(GPid child_pid
)
845 GetExitCodeProcess(child_pid
, &exit_code
);
847 return (exit_code
== 0);
851 static FILE *open_std_handle(DWORD handle
, const char *mode
)
857 lStdHandle
= GetStdHandle(handle
);
858 if (lStdHandle
== INVALID_HANDLE_VALUE
)
860 gchar
*err
= g_win32_error_message(GetLastError());
861 g_warning("GetStdHandle(%ld) failed: %s", (long)handle
, err
);
865 hConHandle
= _open_osfhandle((long)lStdHandle
, _O_TEXT
);
866 if (hConHandle
== -1)
868 gchar
*err
= g_win32_error_message(GetLastError());
869 g_warning("_open_osfhandle(%ld, _O_TEXT) failed: %s", (long)lStdHandle
, err
);
873 fp
= _fdopen(hConHandle
, mode
);
876 gchar
*err
= g_win32_error_message(GetLastError());
877 g_warning("_fdopen(%d, \"%s\") failed: %s", hConHandle
, mode
, err
);
881 if (setvbuf(fp
, NULL
, _IONBF
, 0) != 0)
883 gchar
*err
= g_win32_error_message(GetLastError());
884 g_warning("setvbuf(%p, NULL, _IONBF, 0) failed: %s", fp
, err
);
894 static void debug_setup_console(void)
896 static const WORD MAX_CONSOLE_LINES
= 500;
897 CONSOLE_SCREEN_BUFFER_INFO coninfo
;
900 /* allocate a console for this app */
903 /* set the screen buffer to be big enough to let us scroll text */
904 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &coninfo
);
905 coninfo
.dwSize
.Y
= MAX_CONSOLE_LINES
;
906 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE
), coninfo
.dwSize
);
908 /* redirect unbuffered STDOUT to the console */
909 fp
= open_std_handle(STD_OUTPUT_HANDLE
, "w");
913 /* redirect unbuffered STDERR to the console */
914 fp
= open_std_handle(STD_ERROR_HANDLE
, "w");
918 /* redirect unbuffered STDIN to the console */
919 fp
= open_std_handle(STD_INPUT_HANDLE
, "r");
925 void win32_init_debug_code(void)
929 /* create a console window to get log messages on Windows,
930 * especially useful when generating tags files */
931 debug_setup_console();
932 /* Enable GLib process spawn debug mode when Geany was started with the debug flag */
933 g_setenv("G_SPAWN_WIN32_DEBUG", "1", FALSE
);
938 static gchar
*create_temp_file(void)
943 fd
= g_file_open_tmp("tmp_XXXXXX", &name
, NULL
);
953 /* Sometimes this blocks for 30s before aborting when there are several
954 * pages of (error) output and sometimes hangs - see the FIXME.
955 * Also gw_spawn.dwExitCode seems to be not set properly. */
956 /* Process spawning implementation for Windows, by Pierre Joye.
957 * Don't call this function directly, use utils_spawn_[a]sync() instead. */
959 gboolean
_broken_win32_spawn(const gchar
*dir
, gchar
**argv
, gchar
**env
, GSpawnFlags flags
,
960 gchar
**std_out
, gchar
**std_err
, gint
*exit_status
, GError
**error
)
962 TCHAR buffer
[CMDSIZE
]=TEXT("");
963 TCHAR cmdline
[CMDSIZE
] = TEXT("");
964 TCHAR
* lpPart
[CMDSIZE
]={NULL
};
969 SECURITY_ATTRIBUTES saAttr
;
971 geany_win32_spawn gw_spawn
;
974 HANDLE hStdoutTempFile
= NULL
;
975 HANDLE hStderrTempFile
= NULL
;
977 gchar
*stdout_content
= NULL
;
978 gchar
*stderr_content
= NULL
;
984 g_return_val_if_fail (std_out
== NULL
||
985 !(flags
& G_SPAWN_STDOUT_TO_DEV_NULL
), FALSE
);
986 g_return_val_if_fail (std_err
== NULL
||
987 !(flags
& G_SPAWN_STDERR_TO_DEV_NULL
), FALSE
);
989 if (flags
& G_SPAWN_SEARCH_PATH
)
991 retval
= SearchPath(NULL
, argv
[0], ".exe", sizeof(buffer
), buffer
, lpPart
);
993 g_snprintf(cmdline
, sizeof(cmdline
), "\"%s\"", buffer
);
995 g_strlcpy(cmdline
, argv
[0], sizeof(cmdline
));
999 for (i
= cmdpos
; i
< argc
; i
++)
1001 g_snprintf(cmdline
, sizeof(cmdline
), "%s %s", cmdline
, argv
[i
]);
1002 /*MessageBox(NULL, cmdline, cmdline, MB_OK);*/
1005 if (std_err
!= NULL
)
1007 hStderrTempFile
= GetTempFileHandle(error
);
1008 if (hStderrTempFile
== INVALID_HANDLE_VALUE
)
1010 gchar
*msg
= g_win32_error_message(GetLastError());
1011 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint
) GetLastError());
1012 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1018 if (std_out
!= NULL
)
1020 hStdoutTempFile
= GetTempFileHandle(error
);
1021 if (hStdoutTempFile
== INVALID_HANDLE_VALUE
)
1023 gchar
*msg
= g_win32_error_message(GetLastError());
1024 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint
) GetLastError());
1025 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1031 /* Set the bInheritHandle flag so pipe handles are inherited. */
1032 saAttr
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1033 saAttr
.bInheritHandle
= TRUE
;
1034 saAttr
.lpSecurityDescriptor
= NULL
;
1036 /* Get the handle to the current STDOUT and STDERR. */
1037 gw_spawn
.hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
1038 gw_spawn
.hStderr
= GetStdHandle(STD_ERROR_HANDLE
);
1039 gw_spawn
.dwExitCode
= 0;
1041 /* Create a pipe for the child process's STDOUT. */
1042 if (! CreatePipe(&(gw_spawn
.hChildStdoutRd
), &(gw_spawn
.hChildStdoutWr
), &saAttr
, 0))
1044 gchar
*msg
= g_win32_error_message(GetLastError());
1045 geany_debug("win32_spawn: Stdout pipe creation failed (%d)", (gint
) GetLastError());
1046 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
1051 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
1052 SetHandleInformation(gw_spawn
.hChildStdoutRd
, HANDLE_FLAG_INHERIT
, 0);
1054 /* Create a pipe for the child process's STDERR. */
1055 if (! CreatePipe(&(gw_spawn
.hChildStderrRd
), &(gw_spawn
.hChildStderrWr
), &saAttr
, 0))
1057 gchar
*msg
= g_win32_error_message(GetLastError());
1058 geany_debug("win32_spawn: Stderr pipe creation failed");
1059 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
1064 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
1065 SetHandleInformation(gw_spawn
.hChildStderrRd
, HANDLE_FLAG_INHERIT
, 0);
1067 /* Create a pipe for the child process's STDIN. */
1068 if (! CreatePipe(&(gw_spawn
.hChildStdinRd
), &(gw_spawn
.hChildStdinWr
), &saAttr
, 0))
1070 gchar
*msg
= g_win32_error_message(GetLastError());
1071 geany_debug("win32_spawn: Stdin pipe creation failed");
1072 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
1077 /* Ensure that the write handle to the child process's pipe for STDIN is not inherited. */
1078 SetHandleInformation(gw_spawn
.hChildStdinWr
, HANDLE_FLAG_INHERIT
, 0);
1080 /* Now create the child process. */
1081 fSuccess
= CreateChildProcess(&gw_spawn
, cmdline
, dir
, error
);
1084 *exit_status
= gw_spawn
.dwExitCode
;
1089 geany_debug("win32_spawn: Create process failed");
1093 /* Read from pipe that is the standard output for child process. */
1094 if (std_out
!= NULL
)
1096 ReadFromPipe(gw_spawn
.hChildStdoutRd
, gw_spawn
.hChildStdoutWr
, hStdoutTempFile
, error
);
1097 if (! GetContentFromHandle(hStdoutTempFile
, &stdout_content
, error
))
1101 *std_out
= stdout_content
;
1104 if (std_err
!= NULL
)
1106 ReadFromPipe(gw_spawn
.hChildStderrRd
, gw_spawn
.hChildStderrWr
, hStderrTempFile
, error
);
1107 if (! GetContentFromHandle(hStderrTempFile
, &stderr_content
, error
))
1111 *std_err
= stderr_content
;
1117 /* Note: g_spawn is broken for receiving both stdio and stderr e.g. when
1118 * running make and there are compile errors. See glib/giowin32.c header
1119 * comment about Windows bugs, e.g. #338943 */
1120 /* Simple replacement for _broken_win32_spawn().
1121 * flags is ignored, G_SPAWN_SEARCH_PATH is implied.
1122 * Don't call this function directly, use utils_spawn_[a]sync() instead.
1123 * Adapted from tm_workspace_create_global_tags(). */
1124 gboolean
win32_spawn(const gchar
*dir
, gchar
**argv
, gchar
**env
, GSpawnFlags flags
,
1125 gchar
**std_out
, gchar
**std_err
, gint
*exit_status
, GError
**error
)
1129 gchar
*tmp_file
= create_temp_file();
1130 gchar
*tmp_errfile
= create_temp_file();
1132 gchar
*locale_command
;
1136 return _broken_win32_spawn(dir
, argv
, env
, flags
, std_out
, std_err
,
1137 exit_status
, error
);
1139 if (!tmp_file
|| !tmp_errfile
)
1141 g_warning("%s: Could not create temporary files!", G_STRFUNC
);
1144 command
= g_strjoinv(" ", argv
);
1145 SETPTR(command
, g_strdup_printf("cmd.exe /S /C \"%s >%s 2>%s\"",
1146 command
, tmp_file
, tmp_errfile
));
1147 locale_command
= g_locale_from_utf8(command
, -1, NULL
, NULL
, NULL
);
1148 if (! locale_command
)
1149 locale_command
= g_strdup(command
);
1150 geany_debug("WIN32: actually running command:\n%s", command
);
1153 ret
= system(locale_command
);
1154 /* the command can return -1 as an exit code, so check errno also */
1155 fail
= ret
== -1 && errno
;
1158 if (std_out
!= NULL
)
1159 g_file_get_contents(tmp_file
, std_out
, NULL
, NULL
);
1160 if (std_err
!= NULL
)
1161 g_file_get_contents(tmp_errfile
, std_err
, NULL
, NULL
);
1164 g_set_error_literal(error
, G_SPAWN_ERROR
, errno
, g_strerror(errno
));
1167 g_free(locale_command
);
1170 g_unlink(tmp_errfile
);
1171 g_free(tmp_errfile
);
1179 static gboolean
GetContentFromHandle(HANDLE hFile
, gchar
**content
, GError
**error
)
1185 filesize
= GetFileSize(hFile
, NULL
);
1192 buffer
= g_malloc(filesize
+ 1);
1195 gchar
*msg
= g_win32_error_message(GetLastError());
1196 geany_debug("GetContentFromHandle: Alloc failed");
1197 g_set_error(error
, G_SPAWN_ERROR
, G_SPAWN_ERROR
, "%s", msg
);
1202 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
1203 if (! ReadFile(hFile
, buffer
, filesize
, &dwRead
, NULL
) || dwRead
== 0)
1205 gchar
*msg
= g_win32_error_message(GetLastError());
1206 geany_debug("GetContentFromHandle: Cannot read tempfile");
1207 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_FAILED
, "%s", msg
);
1212 if (! CloseHandle(hFile
))
1214 gchar
*msg
= g_win32_error_message(GetLastError());
1215 geany_debug("GetContentFromHandle: CloseHandle failed (%d)", (gint
) GetLastError());
1216 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_FAILED
, "%s", msg
);
1222 buffer
[filesize
] = '\0';
1228 gchar
*win32_expand_environment_variables(const gchar
*str
)
1230 gchar expCmdline
[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
1232 if (ExpandEnvironmentStrings((LPCTSTR
) str
, (LPTSTR
) expCmdline
, sizeof(expCmdline
)) != 0)
1233 return g_strdup(expCmdline
);
1235 return g_strdup(str
);
1239 static gboolean
CreateChildProcess(geany_win32_spawn
*gw_spawn
, TCHAR
*szCmdline
,
1240 const TCHAR
*dir
, GError
**error
)
1242 PROCESS_INFORMATION piProcInfo
;
1243 STARTUPINFOW siStartInfo
;
1244 BOOL bFuncRetn
= FALSE
;
1245 gchar
*expandedCmdline
;
1246 wchar_t w_commandline
[CMDSIZE
];
1247 wchar_t w_dir
[MAX_PATH
];
1249 /* Set up members of the PROCESS_INFORMATION structure. */
1250 ZeroMemory(&piProcInfo
, sizeof(PROCESS_INFORMATION
));
1252 /* Set up members of the STARTUPINFO structure.*/
1253 ZeroMemory(&siStartInfo
, sizeof(STARTUPINFO
));
1255 siStartInfo
.cb
= sizeof(STARTUPINFO
);
1256 siStartInfo
.hStdError
= gw_spawn
->hChildStderrWr
;
1257 siStartInfo
.hStdOutput
= gw_spawn
->hChildStdoutWr
;
1258 siStartInfo
.hStdInput
= gw_spawn
->hChildStdinRd
;
1259 siStartInfo
.dwFlags
|= STARTF_USESTDHANDLES
;
1261 /* Expand environment variables like %blah%. */
1262 expandedCmdline
= win32_expand_environment_variables(szCmdline
);
1264 MultiByteToWideChar(CP_UTF8
, 0, expandedCmdline
, -1, w_commandline
, G_N_ELEMENTS(w_commandline
));
1265 MultiByteToWideChar(CP_UTF8
, 0, dir
, -1, w_dir
, G_N_ELEMENTS(w_dir
));
1267 /* Create the child process. */
1268 bFuncRetn
= CreateProcessW(NULL
,
1269 w_commandline
, /* command line */
1270 NULL
, /* process security attributes */
1271 NULL
, /* primary thread security attributes */
1272 TRUE
, /* handles are inherited */
1273 CREATE_NO_WINDOW
, /* creation flags */
1274 NULL
, /* use parent's environment */
1275 w_dir
, /* use parent's current directory */
1276 &siStartInfo
, /* STARTUPINFO pointer */
1277 &piProcInfo
); /* receives PROCESS_INFORMATION */
1279 g_free(expandedCmdline
);
1283 gchar
*msg
= g_win32_error_message(GetLastError());
1284 geany_debug("CreateChildProcess: CreateProcess failed (%s)", msg
);
1285 g_set_error(error
, G_SPAWN_ERROR
, G_SPAWN_ERROR_FAILED
, "%s", msg
);
1294 /* FIXME: this seems to timeout when there are many lines
1295 * to read - maybe because the child's pipe is full */
1296 for (i
= 0; i
< 2 &&
1297 WaitForSingleObject(piProcInfo
.hProcess
, ms
) == WAIT_TIMEOUT
; i
++)
1299 ui_set_statusbar(FALSE
, _("Process timed out after %.02f s!"), ms
/ 1000.0F
);
1300 geany_debug("CreateChildProcess: timed out");
1301 TerminateProcess(piProcInfo
.hProcess
, WAIT_TIMEOUT
); /* NOTE: This will not kill grandkids. */
1304 if (!GetExitCodeProcess(piProcInfo
.hProcess
, &gw_spawn
->dwExitCode
))
1306 gchar
*msg
= g_win32_error_message(GetLastError());
1307 geany_debug("GetExitCodeProcess failed: %s", msg
);
1308 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_FAILED
, "%s", msg
);
1311 CloseHandle(piProcInfo
.hProcess
);
1312 CloseHandle(piProcInfo
.hThread
);
1319 static VOID
ReadFromPipe(HANDLE hRead
, HANDLE hWrite
, HANDLE hFile
, GError
**error
)
1321 DWORD dwRead
, dwWritten
;
1322 CHAR chBuf
[BUFSIZE
];
1324 /* Close the write end of the pipe before reading from the
1325 read end of the pipe. */
1326 if (! CloseHandle(hWrite
))
1328 gchar
*msg
= g_win32_error_message(GetLastError());
1329 geany_debug("ReadFromPipe: Closing handle failed");
1330 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
1335 /* Read output from the child process, and write to parent's STDOUT. */
1338 if (! ReadFile(hRead
, chBuf
, BUFSIZE
, &dwRead
, NULL
) || dwRead
== 0)
1341 if (! WriteFile(hFile
, chBuf
, dwRead
, &dwWritten
, NULL
))
1347 static HANDLE
GetTempFileHandle(GError
**error
)
1350 DWORD dwBufSize
= BUFSIZE
;
1352 TCHAR szTempName
[BUFSIZE
];
1353 TCHAR lpPathBuffer
[BUFSIZE
];
1357 /* Get the temp path. */
1358 dwRetVal
= GetTempPath(dwBufSize
, /* length of the buffer*/
1359 lpPathBuffer
); /* buffer for path */
1361 if (dwRetVal
> dwBufSize
|| (dwRetVal
== 0))
1363 gchar
*msg
= g_win32_error_message(GetLastError());
1364 geany_debug("GetTempFileHandle: GetTempPath failed (%d)", (gint
) GetLastError());
1365 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1370 /* Create a temporary file for STDOUT. */
1371 uRetVal
= GetTempFileName(lpPathBuffer
, /* directory for tmp files */
1372 TEXT("GEANY_VCDIFF_"), /* temp file name prefix */
1373 0, /* create unique name */
1374 szTempName
); /* buffer for name */
1377 gchar
*msg
= g_win32_error_message(GetLastError());
1378 geany_debug("GetTempFileName failed (%d)", (gint
) GetLastError());
1379 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1384 hTempFile
= CreateFile((LPTSTR
) szTempName
, /* file name */
1385 GENERIC_READ
| GENERIC_WRITE
, /* open r-w */
1386 0, /* do not share */
1387 NULL
, /* default security */
1388 CREATE_ALWAYS
, /* overwrite existing */
1389 FILE_ATTRIBUTE_NORMAL
,/* normal file */
1390 NULL
); /* no template */
1392 if (hTempFile
== INVALID_HANDLE_VALUE
)
1394 gchar
*msg
= g_win32_error_message(GetLastError());
1395 geany_debug("GetTempFileHandle: Second CreateFile failed (%d)", (gint
) GetLastError());
1396 g_set_error(error
, G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1404 /* From GDK (they got it from MS Knowledge Base article Q130698) */
1405 static gboolean
resolve_link(HWND hWnd
, wchar_t *link
, gchar
**lpszPath
)
1407 WIN32_FILE_ATTRIBUTE_DATA wfad
;
1409 IShellLinkW
*pslW
= NULL
;
1410 IPersistFile
*ppf
= NULL
;
1411 LPVOID pslWV
= NULL
;
1414 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
1415 * with an empty file and returns an empty "link target". (#524151) */
1416 if (!GetFileAttributesExW(link
, GetFileExInfoStandard
, &wfad
) ||
1417 (wfad
.nFileSizeHigh
== 0 && wfad
.nFileSizeLow
== 0))
1422 /* Assume failure to start with: */
1427 hres
= CoCreateInstance(
1428 &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IShellLinkW
, &pslWV
);
1430 if (SUCCEEDED(hres
))
1432 /* The IShellLink interface supports the IPersistFile interface.
1433 * Get an interface pointer to it. */
1434 pslW
= (IShellLinkW
*) pslWV
;
1435 hres
= pslW
->lpVtbl
->QueryInterface(pslW
, &IID_IPersistFile
, &ppfV
);
1438 if (SUCCEEDED(hres
))
1440 /* Load the file. */
1441 ppf
= (IPersistFile
*) ppfV
;
1442 hres
= ppf
->lpVtbl
->Load(ppf
, link
, STGM_READ
);
1445 if (SUCCEEDED(hres
))
1447 /* Resolve the link by calling the Resolve() interface function. */
1448 hres
= pslW
->lpVtbl
->Resolve(pslW
, hWnd
, SLR_ANY_MATCH
| SLR_NO_UI
);
1451 if (SUCCEEDED(hres
))
1453 wchar_t wtarget
[MAX_PATH
];
1455 hres
= pslW
->lpVtbl
->GetPath(pslW
, wtarget
, MAX_PATH
, NULL
, 0);
1456 if (SUCCEEDED(hres
))
1457 *lpszPath
= g_utf16_to_utf8(wtarget
, -1, NULL
, NULL
, NULL
);
1461 ppf
->lpVtbl
->Release(ppf
);
1464 pslW
->lpVtbl
->Release(pslW
);
1466 return SUCCEEDED(hres
);
1470 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
1471 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
1472 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
1473 gchar
*win32_get_shortcut_target(const gchar
*file_name
)
1476 wchar_t *wfilename
= g_utf8_to_utf16(file_name
, -1, NULL
, NULL
, NULL
);
1479 if (main_widgets
.window
!= NULL
)
1481 GdkWindow
*window
= gtk_widget_get_window(main_widgets
.window
);
1483 hWnd
= GDK_WINDOW_HWND(window
);
1486 resolve_link(hWnd
, wfilename
, &path
);
1490 return g_strdup(file_name
);
1496 void win32_set_working_directory(const gchar
*dir
)
1498 SetCurrentDirectory(dir
);
1502 gchar
*win32_get_installation_dir(void)
1504 return g_win32_get_package_installation_directory_of_module(NULL
);
1508 gchar
*win32_get_user_config_dir(void)
1511 wchar_t path
[MAX_PATH
];
1513 hr
= SHGetFolderPathAndSubDirW(NULL
, CSIDL_APPDATA
| CSIDL_FLAG_CREATE
, NULL
, SHGFP_TYPE_CURRENT
, L
"geany", path
);
1516 // GLib always uses UTF-8 for filename encoding on Windows
1517 int u8_size
= WideCharToMultiByte(CP_UTF8
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
1520 gchar
*u8_path
= g_malloc0(u8_size
+ 1);
1521 if (u8_path
!= NULL
&&
1522 WideCharToMultiByte(CP_UTF8
, 0, path
, -1, u8_path
, u8_size
, NULL
, NULL
))
1530 g_warning("Failed to retrieve Windows config dir, falling back to default");
1531 return g_build_filename(g_get_user_config_dir(), "geany", NULL
);