2 * win32.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2009 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
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 * Special functions for the win32 platform, to provide native dialogs.
33 #define WIN32_LEAN_AND_MEAN
45 #include <gdk/gdkwin32.h>
53 #include "sciwrappers.h"
55 #include "filetypes.h"
61 struct _geany_win32_spawn
65 HANDLE hChildStdoutRd
;
66 HANDLE hChildStdoutWr
;
67 HANDLE hChildStderrRd
;
68 HANDLE hChildStderrWr
;
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
, const TCHAR
*dir
, GError
**error
);
80 static VOID
ReadFromPipe(HANDLE hRead
, HANDLE hWrite
, HANDLE hFile
, GError
**error
);
83 static wchar_t *get_file_filters(void)
87 static wchar_t title
[1024];
88 GString
*str
= g_string_sized_new(100);
89 GString
*all_patterns
= g_string_sized_new(100);
92 for (i
= 0; i
< filetypes_array
->len
; i
++)
94 tmp
= g_strjoinv(";", filetypes
[i
]->pattern
);
95 g_string_append_printf(str
, "%s\t%s\t", filetypes
[i
]->title
, tmp
);
98 /* create meta file filter "All Source" */
99 for (i
= 0; 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
);
110 g_string_append_c(str
, '\t'); /* the final \0 byte to mark the end of the string */
112 g_string_free(str
, FALSE
);
114 /* replace all "\t"s by \0 */
115 len
= strlen(string
);
116 for (i
= 0; i
< len
; i
++)
118 if (string
[i
] == '\t') string
[i
] = '\0';
120 MultiByteToWideChar(CP_UTF8
, 0, string
, len
, title
, sizeof(title
));
127 static wchar_t *get_filters(gboolean project_files
)
131 static wchar_t title
[1024];
135 string
= g_strconcat(_("Geany project files"), "\t", "*." GEANY_PROJECT_EXT
, "\t",
136 filetypes
[GEANY_FILETYPES_NONE
]->title
, "\t",
137 filetypes
[GEANY_FILETYPES_NONE
]->pattern
[0], "\t", NULL
);
141 string
= g_strconcat(_("Executables"), "\t", "*.exe;*.bat;*.cmd", "\t",
142 filetypes
[GEANY_FILETYPES_NONE
]->title
, "\t",
143 filetypes
[GEANY_FILETYPES_NONE
]->pattern
[0], "\t", NULL
);
146 /* replace all "\t"s by \0 */
147 len
= strlen(string
);
148 for (i
= 0; i
< len
; i
++)
150 if (string
[i
] == '\t') string
[i
] = '\0';
152 MultiByteToWideChar(CP_UTF8
, 0, string
, len
, title
, sizeof(title
));
159 /* Converts the given UTF-8 filename or directory name into something usable for Windows and
160 * returns the directory part of the given filename. */
161 static wchar_t *get_dir_for_path(const gchar
*utf8_filename
)
163 static wchar_t w_dir
[MAX_PATH
];
166 if (g_file_test(utf8_filename
, G_FILE_TEST_IS_DIR
))
167 result
= (gchar
*) utf8_filename
;
169 result
= g_path_get_dirname(utf8_filename
);
171 MultiByteToWideChar(CP_UTF8
, 0, result
, -1, w_dir
, sizeof(w_dir
));
173 if (result
!= utf8_filename
)
180 /* Callback function for setting the initial directory of the folder open dialog. This could also
181 * be done with BROWSEINFO.pidlRoot and SHParseDisplayName but SHParseDisplayName is not available
182 * on systems below Windows XP. So, we go the hard way by creating a callback which will set up the
183 * folder when the dialog is initialised. Yeah, I like Windows. */
184 INT CALLBACK
BrowseCallbackProc(HWND hwnd
, UINT uMsg
, LPARAM lp
, LPARAM pData
)
188 case BFFM_INITIALIZED
:
190 SendMessageW(hwnd
, BFFM_SETSELECTIONW
, TRUE
, pData
);
193 case BFFM_SELCHANGED
:
195 /* set the status window to the currently selected path. */
196 static wchar_t szDir
[MAX_PATH
];
197 if (SHGetPathFromIDListW((LPITEMIDLIST
) lp
, szDir
))
199 SendMessageW(hwnd
, BFFM_SETSTATUSTEXTW
, 0, (LPARAM
) szDir
);
208 /* Shows a folder selection dialog.
209 * initial_dir is expected in UTF-8
210 * The selected folder name is returned. */
211 gchar
*win32_show_project_folder_dialog(GtkWidget
*parent
, const gchar
*title
,
212 const gchar
*initial_dir
)
216 gchar
*result
= NULL
;
217 wchar_t fname
[MAX_PATH
];
218 wchar_t w_title
[512];
220 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, sizeof(w_title
));
223 parent
= main_widgets
.window
;
225 memset(&bi
, 0, sizeof bi
);
226 bi
.hwndOwner
= GDK_WINDOW_HWND(parent
->window
);
228 bi
.lpszTitle
= w_title
;
229 bi
.lpfn
= BrowseCallbackProc
;
230 bi
.lParam
= (LPARAM
) get_dir_for_path(initial_dir
);
231 bi
.ulFlags
= BIF_DONTGOBELOWDOMAIN
| BIF_RETURNONLYFSDIRS
| BIF_STATUSTEXT
;
233 pidl
= SHBrowseForFolderW(&bi
);
235 /* convert the strange Windows folder list item something into an usual path string ;-) */
238 if (SHGetPathFromIDListW(pidl
, fname
))
240 result
= g_malloc0(MAX_PATH
* 2);
241 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, result
, MAX_PATH
* 2, NULL
, NULL
);
243 /* SHBrowseForFolder() probably leaks memory here, but how to free the allocated memory? */
249 /* Shows a file open dialog.
250 * If allow_new_file is set, the file to be opened doesn't have to exist.
251 * initial_dir is expected in UTF-8
252 * The selected file name is returned.
253 * If project_file_filter is set, the file open dialog will have a file filter for Geany project
254 * files, a filter for executables otherwise. */
255 gchar
*win32_show_project_open_dialog(GtkWidget
*parent
, const gchar
*title
,
256 const gchar
*initial_dir
, gboolean allow_new_file
,
257 gboolean project_file_filter
)
261 wchar_t fname
[MAX_PATH
];
262 wchar_t w_title
[512];
267 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, sizeof(w_title
));
270 parent
= main_widgets
.window
;
272 /* initialise file dialog info struct */
273 memset(&of
, 0, sizeof of
);
274 #ifdef OPENFILENAME_SIZE_VERSION_400
275 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
277 of
.lStructSize
= sizeof of
;
279 of
.hwndOwner
= GDK_WINDOW_HWND(parent
->window
);
280 of
.lpstrFilter
= get_filters(project_file_filter
);
282 of
.lpstrCustomFilter
= NULL
;
284 of
.lpstrFile
= fname
;
285 of
.lpstrInitialDir
= get_dir_for_path(initial_dir
);
287 of
.lpstrFileTitle
= NULL
;
288 of
.lpstrTitle
= w_title
;
289 of
.lpstrDefExt
= L
"";
290 of
.Flags
= OFN_PATHMUSTEXIST
| OFN_EXPLORER
| OFN_HIDEREADONLY
;
291 if (! allow_new_file
)
292 of
.Flags
|= OFN_FILEMUSTEXIST
;
294 retval
= GetOpenFileNameW(&of
);
298 if (CommDlgExtendedError())
301 error
= g_strdup_printf("File dialog box error (%x)", (int)CommDlgExtendedError());
302 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
307 /* convert the resulting filename into UTF-8 (from whatever encoding it has at this moment) */
308 filename
= g_malloc0(MAX_PATH
* 2);
309 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, filename
, MAX_PATH
* 2, NULL
, NULL
);
315 /* initial_dir can be NULL to use the current working directory.
316 * Returns: TRUE if the dialog was not cancelled. */
317 gboolean
win32_show_file_dialog(gboolean file_open
, const gchar
*initial_dir
)
322 wchar_t fname
[MAX_PATH
];
323 wchar_t w_dir
[MAX_PATH
];
327 if (initial_dir
!= NULL
)
328 MultiByteToWideChar(CP_UTF8
, 0, initial_dir
, -1, w_dir
, sizeof(w_dir
));
330 /* initialise file dialog info struct */
331 memset(&of
, 0, sizeof of
);
332 #ifdef OPENFILENAME_SIZE_VERSION_400
333 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
335 of
.lStructSize
= sizeof of
;
337 of
.hwndOwner
= GDK_WINDOW_HWND(main_widgets
.window
->window
);
338 of
.lpstrFilter
= get_file_filters();
340 of
.lpstrCustomFilter
= NULL
;
341 of
.nFilterIndex
= GEANY_FILETYPES_NONE
+ 1;
342 of
.lpstrFile
= fname
;
343 of
.lpstrInitialDir
= (initial_dir
!= NULL
) ? w_dir
: NULL
;
345 of
.lpstrFileTitle
= NULL
;
346 of
.lpstrTitle
= NULL
;
347 of
.lpstrDefExt
= L
"";
350 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
;
351 retval
= GetOpenFileNameW(&of
);
355 of
.Flags
= OFN_OVERWRITEPROMPT
| OFN_PATHMUSTEXIST
;
356 retval
= GetSaveFileNameW(&of
);
361 if (CommDlgExtendedError())
364 g_snprintf(error
, sizeof error
, "File dialog box error (%x)", (int)CommDlgExtendedError());
365 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
374 x
= of
.nFileOffset
- 1;
375 if (x
!= wcslen(fname
))
376 { /* open a single file */
377 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
378 document_open_file(tmp
, of
.Flags
& OFN_READONLY
, NULL
, NULL
);
381 { /* open multiple files */
382 gchar file_name
[MAX_PATH
];
383 gchar dir_name
[MAX_PATH
];
385 WideCharToMultiByte(CP_UTF8
, 0, fname
, of
.nFileOffset
,
386 dir_name
, sizeof(dir_name
), NULL
, NULL
);
394 WideCharToMultiByte(CP_UTF8
, 0, fname
+ x
+ 1, -1,
395 tmp
, sizeof(tmp
), NULL
, NULL
);
396 g_snprintf(file_name
, 511, "%s\\%s", dir_name
, tmp
);
398 /* convert the resulting filename into UTF-8 */
399 document_open_file(file_name
, of
.Flags
& OFN_READONLY
, NULL
, NULL
);
407 GeanyDocument
*doc
= document_get_current();
409 WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
410 document_save_file_as(doc
, tmp
);
412 return (retval
!= 0);
416 void win32_show_font_dialog(void)
420 LOGFONT lf
; /* logical font structure */
422 memset(&cf
, 0, sizeof cf
);
423 cf
.lStructSize
= sizeof cf
;
424 cf
.hwndOwner
= GDK_WINDOW_HWND(main_widgets
.window
->window
);
426 cf
.Flags
= CF_APPLY
| CF_NOSCRIPTSEL
| CF_FORCEFONTEXIST
| CF_INITTOLOGFONTSTRUCT
| CF_SCREENFONTS
;
428 retval
= ChooseFont(&cf
);
432 gchar
*editorfont
= g_strdup_printf("%s %d", lf
.lfFaceName
, (cf
.iPointSize
/ 10));
433 ui_set_editor_font(editorfont
);
439 void win32_show_color_dialog(const gchar
*colour
)
442 static COLORREF acr_cust_clr
[16];
443 static DWORD rgb_current
;
444 gchar
*hex
= g_malloc0(12);
445 GeanyDocument
*doc
= document_get_current();
447 /* Initialize CHOOSECOLOR */
448 memset(&cc
, 0, sizeof cc
);
449 cc
.lStructSize
= sizeof(cc
);
450 cc
.hwndOwner
= GDK_WINDOW_HWND(main_widgets
.window
->window
);
451 cc
.lpCustColors
= (LPDWORD
) acr_cust_clr
;
452 cc
.rgbResult
= (colour
!= NULL
) ? utils_strtod(colour
, NULL
, colour
[0] == '#') : 0;
453 cc
.Flags
= CC_FULLOPEN
| CC_RGBINIT
;
455 if (ChooseColor(&cc
))
457 rgb_current
= cc
.rgbResult
;
458 g_snprintf(hex
, 11, "#%02X%02X%02X",
459 (guint
) (utils_scale_round(GetRValue(rgb_current
), 255)),
460 (guint
) (utils_scale_round(GetGValue(rgb_current
), 255)),
461 (guint
) (utils_scale_round(GetBValue(rgb_current
), 255)));
463 editor_insert_color(doc
->editor
, hex
);
469 void win32_show_pref_file_dialog(GtkEntry
*item
)
473 wchar_t fname
[MAX_PATH
];
475 gchar
**field
, *filename
;
479 /* cut the options from the command line */
480 field
= g_strsplit(gtk_entry_get_text(GTK_ENTRY(item
)), " ", 2);
483 filename
= g_find_program_in_path(field
[0]);
484 if (filename
!= NULL
&& g_file_test(filename
, G_FILE_TEST_EXISTS
))
486 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, fname
, sizeof(fname
));
491 /* initialize file dialog info struct */
492 memset(&of
, 0, sizeof of
);
493 #ifdef OPENFILENAME_SIZE_VERSION_400
494 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
496 of
.lStructSize
= sizeof of
;
498 of
.hwndOwner
= GDK_WINDOW_HWND(ui_widgets
.prefs_dialog
->window
);
500 of
.lpstrFilter
= get_filters(FALSE
);
501 of
.lpstrCustomFilter
= NULL
;
504 of
.lpstrFile
= fname
;
506 of
.lpstrFileTitle
= NULL
;
507 /*of.lpstrInitialDir = g_get_home_dir();*/
508 of
.lpstrInitialDir
= NULL
;
509 of
.lpstrTitle
= NULL
;
510 of
.lpstrDefExt
= L
"exe";
511 of
.Flags
= OFN_HIDEREADONLY
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
;
512 retval
= GetOpenFileNameW(&of
);
516 if (CommDlgExtendedError())
519 g_snprintf(error
, sizeof error
, "File dialog box error (%x)", (int)CommDlgExtendedError());
520 win32_message_dialog(NULL
, GTK_MESSAGE_ERROR
, error
);
526 len
= WideCharToMultiByte(CP_UTF8
, 0, fname
, -1, tmp
, sizeof(tmp
), NULL
, NULL
);
527 if ((of
.nFileOffset
- 1) != len
)
529 if (g_strv_length(field
) > 1)
530 /* add the command line args of the old command */
531 /** TODO this fails badly when the old command contained spaces, we need quoting here */
532 filename
= g_strconcat(tmp
, " ", field
[1], NULL
);
535 filename
= g_strdup(tmp
);
537 gtk_entry_set_text(GTK_ENTRY(item
), filename
);
544 /* Creates a native Windows message box of the given type and returns always TRUE
545 * or FALSE representing th pressed Yes or No button.
546 * If type is not GTK_MESSAGE_QUESTION, it returns always TRUE. */
547 gboolean
win32_message_dialog(GtkWidget
*parent
, GtkMessageType type
, const gchar
*msg
)
553 HWND parent_hwnd
= NULL
;
554 static wchar_t w_msg
[512];
555 static wchar_t w_title
[512];
559 case GTK_MESSAGE_ERROR
:
561 t
= MB_OK
| MB_ICONERROR
;
565 case GTK_MESSAGE_QUESTION
:
567 t
= MB_YESNO
| MB_ICONQUESTION
;
568 title
= _("Question");
571 case GTK_MESSAGE_WARNING
:
573 t
= MB_OK
| MB_ICONWARNING
;
574 title
= _("Warning");
579 t
= MB_OK
| MB_ICONINFORMATION
;
580 title
= _("Information");
585 /* convert the Unicode chars to wide chars */
586 /** TODO test if LANG == C then possibly skip conversion => g_win32_getlocale() */
587 MultiByteToWideChar(CP_UTF8
, 0, msg
, -1, w_msg
, G_N_ELEMENTS(w_msg
));
588 MultiByteToWideChar(CP_UTF8
, 0, title
, -1, w_title
, G_N_ELEMENTS(w_title
));
591 parent_hwnd
= GDK_WINDOW_HWND(parent
->window
);
592 else if (main_widgets
.window
!= NULL
)
593 parent_hwnd
= GDK_WINDOW_HWND(main_widgets
.window
->window
);
595 /* display the message box */
596 rc
= MessageBoxW(parent_hwnd
, w_msg
, w_title
, t
);
598 if (type
== GTK_MESSAGE_QUESTION
&& rc
!= IDYES
)
605 /* Little wrapper for _waccess(), returns errno or 0 if there was no error */
606 gint
win32_check_write_permission(const gchar
*dir
)
608 static wchar_t w_dir
[MAX_PATH
];
609 MultiByteToWideChar(CP_UTF8
, 0, dir
, -1, w_dir
, sizeof w_dir
);
610 if (_waccess(w_dir
, R_OK
| W_OK
) != 0)
617 /* Special dialog to ask for an action when closing an unsaved file */
618 gint
win32_message_dialog_unsaved(const gchar
*msg
)
620 static wchar_t w_msg
[512];
621 static wchar_t w_title
[512];
622 HWND parent_hwnd
= NULL
;
625 /* convert the Unicode chars to wide chars */
626 MultiByteToWideChar(CP_UTF8
, 0, msg
, -1, w_msg
, G_N_ELEMENTS(w_msg
));
627 MultiByteToWideChar(CP_UTF8
, 0, _("Question"), -1, w_title
, G_N_ELEMENTS(w_title
));
629 if (main_widgets
.window
!= NULL
)
630 parent_hwnd
= GDK_WINDOW_HWND(main_widgets
.window
->window
);
632 ret
= MessageBoxW(parent_hwnd
, w_msg
, w_title
, MB_YESNOCANCEL
| MB_ICONQUESTION
);
635 case IDYES
: ret
= GTK_RESPONSE_YES
; break;
636 case IDNO
: ret
= GTK_RESPONSE_NO
; break;
637 case IDCANCEL
: ret
= GTK_RESPONSE_CANCEL
; break;
644 /* Just a simple wrapper function to open a browser window */
645 void win32_open_browser(const gchar
*uri
)
647 if (strncmp(uri
, "file://", 7) == 0)
653 ShellExecute(NULL
, "open", uri
, NULL
, NULL
, SW_SHOWNORMAL
);
657 /* Returns TRUE if the command, which child_pid refers to, returned with a successful exit code,
658 * otherwise FALSE. */
659 gboolean
win32_get_exit_status(GPid child_pid
)
662 GetExitCodeProcess(child_pid
, &exit_code
);
664 return (exit_code
== 0);
668 static void debug_setup_console()
670 static const WORD MAX_CONSOLE_LINES
= 500;
673 CONSOLE_SCREEN_BUFFER_INFO coninfo
;
676 /* allocate a console for this app */
679 /* set the screen buffer to be big enough to let us scroll text */
680 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &coninfo
);
681 coninfo
.dwSize
.Y
= MAX_CONSOLE_LINES
;
682 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE
), coninfo
.dwSize
);
684 /* redirect unbuffered STDOUT to the console */
685 lStdHandle
= (long)GetStdHandle(STD_OUTPUT_HANDLE
);
686 hConHandle
= _open_osfhandle(lStdHandle
, _O_TEXT
);
687 fp
= _fdopen(hConHandle
, "w");
689 setvbuf(stdout
, NULL
, _IONBF
, 0);
691 /* redirect unbuffered STDERR to the console */
692 lStdHandle
= (long)GetStdHandle(STD_ERROR_HANDLE
);
693 hConHandle
= _open_osfhandle(lStdHandle
, _O_TEXT
);
694 fp
= _fdopen(hConHandle
, "w");
696 setvbuf(stderr
, NULL
, _IONBF
, 0);
700 void win32_init_debug_code(void)
704 /* create a console window to get log messages on Windows */
705 /** TODO remove me? */
706 debug_setup_console();
707 /* Enable GLib process spawn debug mode when Geany was started with the debug flag */
708 g_setenv("G_SPAWN_WIN32_DEBUG", "1", FALSE
);
713 gchar
*win32_get_hostname(void)
716 DWORD size
= sizeof(hostname
);
718 if (GetComputerName(hostname
, &size
))
719 return g_strdup(hostname
);
721 return g_strdup("localhost");
725 /* Process spawning implementation for Windows, by Pierre Joye.
726 * Don't call this function directly, use utils_spawn_[a]sync() instead. */
727 gboolean
win32_spawn(const gchar
*dir
, gchar
**argv
, gchar
**env
, GSpawnFlags flags
,
728 gchar
**std_out
, gchar
**std_err
, gint
*exit_status
, GError
**error
)
730 TCHAR buffer
[MAX_PATH
]=TEXT("");
731 TCHAR cmdline
[MAX_PATH
] = TEXT("");
732 TCHAR
* lpPart
[MAX_PATH
]={NULL
};
737 SECURITY_ATTRIBUTES saAttr
;
739 geany_win32_spawn gw_spawn
;
742 HANDLE hStdoutTempFile
= NULL
;
743 HANDLE hStderrTempFile
= NULL
;
745 gchar
*stdout_content
= NULL
;
746 gchar
*stderr_content
= NULL
;
752 g_return_val_if_fail (std_out
== NULL
||
753 !(flags
& G_SPAWN_STDOUT_TO_DEV_NULL
), FALSE
);
754 g_return_val_if_fail (std_err
== NULL
||
755 !(flags
& G_SPAWN_STDERR_TO_DEV_NULL
), FALSE
);
757 if (flags
& G_SPAWN_SEARCH_PATH
)
759 retval
= SearchPath(NULL
, argv
[0], ".exe", MAX_PATH
, buffer
, lpPart
);
761 g_snprintf(cmdline
, MAX_PATH
, "\"%s\"", buffer
);
763 g_strlcpy(cmdline
, argv
[0], sizeof(cmdline
));
767 for (i
= cmdpos
; i
< argc
; i
++)
769 g_snprintf(cmdline
, MAX_PATH
, "%s %s", cmdline
, argv
[i
]);
770 /*MessageBox(NULL, cmdline, cmdline, MB_OK);*/
775 hStderrTempFile
= GetTempFileHandle(error
);
776 if (hStderrTempFile
== INVALID_HANDLE_VALUE
)
778 gchar
*msg
= g_win32_error_message(GetLastError());
779 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint
) GetLastError());
781 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
789 hStdoutTempFile
= GetTempFileHandle(error
);
790 if (hStdoutTempFile
== INVALID_HANDLE_VALUE
)
792 gchar
*msg
= g_win32_error_message(GetLastError());
793 geany_debug("win32_spawn: Second CreateFile failed (%d)", (gint
) GetLastError());
795 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
801 /* Set the bInheritHandle flag so pipe handles are inherited. */
802 saAttr
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
803 saAttr
.bInheritHandle
= TRUE
;
804 saAttr
.lpSecurityDescriptor
= NULL
;
806 /* Get the handle to the current STDOUT and STDERR. */
807 gw_spawn
.hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
808 gw_spawn
.hStderr
= GetStdHandle(STD_ERROR_HANDLE
);
809 gw_spawn
.dwExitCode
= 0;
811 /* Create a pipe for the child process's STDOUT. */
812 if (! CreatePipe(&(gw_spawn
.hChildStdoutRd
), &(gw_spawn
.hChildStdoutWr
), &saAttr
, 0))
814 gchar
*msg
= g_win32_error_message(GetLastError());
815 geany_debug("win32_spawn: Stdout pipe creation failed (%d)", (gint
) GetLastError());
817 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
822 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
823 SetHandleInformation(gw_spawn
.hChildStdoutRd
, HANDLE_FLAG_INHERIT
, 0);
825 /* Create a pipe for the child process's STDERR. */
826 if (! CreatePipe(&(gw_spawn
.hChildStderrRd
), &(gw_spawn
.hChildStderrWr
), &saAttr
, 0))
828 gchar
*msg
= g_win32_error_message(GetLastError());
829 geany_debug("win32_spawn: Stderr pipe creation failed");
831 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
836 /* Ensure that the read handle to the child process's pipe for STDOUT is not inherited.*/
837 SetHandleInformation(gw_spawn
.hChildStderrRd
, HANDLE_FLAG_INHERIT
, 0);
839 /* Create a pipe for the child process's STDIN. */
840 if (! CreatePipe(&(gw_spawn
.hChildStdinRd
), &(gw_spawn
.hChildStdinWr
), &saAttr
, 0))
842 gchar
*msg
= g_win32_error_message(GetLastError());
843 geany_debug("win32_spawn: Stdin pipe creation failed");
845 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
850 /* Ensure that the write handle to the child process's pipe for STDIN is not inherited. */
851 SetHandleInformation(gw_spawn
.hChildStdinWr
, HANDLE_FLAG_INHERIT
, 0);
853 /* Now create the child process. */
854 fSuccess
= CreateChildProcess(&gw_spawn
, cmdline
, dir
, error
);
857 *exit_status
= gw_spawn
.dwExitCode
;
862 geany_debug("win32_spawn: Create process failed");
864 *error
= g_error_new(G_SPAWN_ERROR
, G_SPAWN_ERROR_FAILED
, "Create process failed");
868 /* Read from pipe that is the standard output for child process. */
871 ReadFromPipe(gw_spawn
.hChildStdoutRd
, gw_spawn
.hChildStdoutWr
, hStdoutTempFile
, error
);
872 if (! GetContentFromHandle(hStdoutTempFile
, &stdout_content
, error
))
876 *std_out
= stdout_content
;
881 ReadFromPipe(gw_spawn
.hChildStderrRd
, gw_spawn
.hChildStderrWr
, hStderrTempFile
, error
);
882 if (! GetContentFromHandle(hStderrTempFile
, &stderr_content
, error
))
886 *std_err
= stderr_content
;
892 static gboolean
GetContentFromHandle(HANDLE hFile
, gchar
**content
, GError
**error
)
898 filesize
= GetFileSize(hFile
, NULL
);
905 buffer
= g_malloc(filesize
+ 1);
908 gchar
*msg
= g_win32_error_message(GetLastError());
909 geany_debug("GetContentFromHandle: Alloc failed");
911 *error
= g_error_new(G_SPAWN_ERROR
, G_SPAWN_ERROR
, "%s", msg
);
916 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
917 if (! ReadFile(hFile
, buffer
, filesize
, &dwRead
, NULL
) || dwRead
== 0)
919 gchar
*msg
= g_win32_error_message(GetLastError());
920 geany_debug("GetContentFromHandle: Cannot read tempfile");
922 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_FAILED
, "%s", msg
);
927 if (! CloseHandle(hFile
))
929 gchar
*msg
= g_win32_error_message(GetLastError());
930 geany_debug("GetContentFromHandle: CloseHandle failed (%d)", (gint
) GetLastError());
932 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_FAILED
, "%s", msg
);
938 buffer
[filesize
] = '\0';
944 gchar
*win32_expand_environment_variables(const gchar
*str
)
946 gchar expCmdline
[32768]; /* 32768 is the limit for ExpandEnvironmentStrings() */
948 if (ExpandEnvironmentStrings((LPCTSTR
) str
, (LPTSTR
) expCmdline
, sizeof(expCmdline
)) != 0)
949 return g_strdup(expCmdline
);
951 return g_strdup(str
);
955 static gboolean
CreateChildProcess(geany_win32_spawn
*gw_spawn
, TCHAR
*szCmdline
, const TCHAR
*dir
, GError
**error
)
957 PROCESS_INFORMATION piProcInfo
;
958 STARTUPINFO siStartInfo
;
959 BOOL bFuncRetn
= FALSE
;
960 gchar
*expandedCmdline
;
962 /* Set up members of the PROCESS_INFORMATION structure. */
963 ZeroMemory(&piProcInfo
, sizeof(PROCESS_INFORMATION
));
965 /* Set up members of the STARTUPINFO structure.*/
966 ZeroMemory(&siStartInfo
, sizeof(STARTUPINFO
));
968 siStartInfo
.cb
= sizeof(STARTUPINFO
);
969 siStartInfo
.hStdError
= gw_spawn
->hChildStderrWr
;
970 siStartInfo
.hStdOutput
= gw_spawn
->hChildStdoutWr
;
971 siStartInfo
.hStdInput
= gw_spawn
->hChildStdinRd
;
972 siStartInfo
.dwFlags
|= STARTF_USESTDHANDLES
;
974 /* Expand environment variables like %blah%. */
975 expandedCmdline
= win32_expand_environment_variables(szCmdline
);
977 /* Create the child process. */
978 bFuncRetn
= CreateProcess(NULL
,
979 expandedCmdline
, /* command line */
980 NULL
, /* process security attributes */
981 NULL
, /* primary thread security attributes */
982 TRUE
, /* handles are inherited */
983 CREATE_NO_WINDOW
, /* creation flags */
984 NULL
, /* use parent's environment */
985 dir
, /* use parent's current directory */
986 &siStartInfo
, /* STARTUPINFO pointer */
987 &piProcInfo
); /* receives PROCESS_INFORMATION */
989 g_free(expandedCmdline
);
993 gchar
*msg
= g_win32_error_message(GetLastError());
994 geany_debug("CreateChildProcess: CreateProcess failed (%s)", msg
);
996 *error
= g_error_new(G_SPAWN_ERROR
, G_SPAWN_ERROR_FAILED
, "%s", msg
);
1005 for (i
= 0; i
< 2 && (dwStatus
= WaitForSingleObject(piProcInfo
.hProcess
, 30*1000)) == WAIT_TIMEOUT
; i
++)
1007 geany_debug("CreateChildProcess: CreateProcess failed");
1008 TerminateProcess(piProcInfo
.hProcess
, WAIT_TIMEOUT
); /* NOTE: This will not kill grandkids. */
1011 if (GetExitCodeProcess(piProcInfo
.hProcess
, &gw_spawn
->dwExitCode
) != 0)
1013 gchar
*msg
= g_win32_error_message(GetLastError());
1014 geany_debug("GetExitCodeProcess failed: %s", msg
);
1016 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_FAILED
, "%s", msg
);
1019 CloseHandle(piProcInfo
.hProcess
);
1020 CloseHandle(piProcInfo
.hThread
);
1027 static VOID
ReadFromPipe(HANDLE hRead
, HANDLE hWrite
, HANDLE hFile
, GError
**error
)
1029 DWORD dwRead
, dwWritten
;
1030 CHAR chBuf
[BUFSIZE
];
1032 /* Close the write end of the pipe before reading from the
1033 read end of the pipe. */
1034 if (! CloseHandle(hWrite
))
1036 gchar
*msg
= g_win32_error_message(GetLastError());
1037 geany_debug("ReadFromPipe: Closing handle failed");
1039 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR_PIPE
, "%s", msg
);
1044 /* Read output from the child process, and write to parent's STDOUT. */
1047 if (! ReadFile(hRead
, chBuf
, BUFSIZE
, &dwRead
, NULL
) || dwRead
== 0)
1050 if (! WriteFile(hFile
, chBuf
, dwRead
, &dwWritten
, NULL
))
1056 static HANDLE
GetTempFileHandle(GError
**error
)
1059 DWORD dwBufSize
= BUFSIZE
;
1061 TCHAR szTempName
[BUFSIZE
];
1062 TCHAR lpPathBuffer
[BUFSIZE
];
1066 /* Get the temp path. */
1067 dwRetVal
= GetTempPath(dwBufSize
, /* length of the buffer*/
1068 lpPathBuffer
); /* buffer for path */
1070 if (dwRetVal
> dwBufSize
|| (dwRetVal
== 0))
1072 gchar
*msg
= g_win32_error_message(GetLastError());
1073 geany_debug("GetTempFileHandle: GetTempPath failed (%d)", (gint
) GetLastError());
1075 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1080 /* Create a temporary file for STDOUT. */
1081 uRetVal
= GetTempFileName(lpPathBuffer
, /* directory for tmp files */
1082 TEXT("GEANY_VCDIFF_"), /* temp file name prefix */
1083 0, /* create unique name */
1084 szTempName
); /* buffer for name */
1087 gchar
*msg
= g_win32_error_message(GetLastError());
1088 geany_debug("GetTempFileName failed (%d)", (gint
) GetLastError());
1090 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1095 hTempFile
= CreateFile((LPTSTR
) szTempName
, /* file name */
1096 GENERIC_READ
| GENERIC_WRITE
, /* open r-w */
1097 0, /* do not share */
1098 NULL
, /* default security */
1099 CREATE_ALWAYS
, /* overwrite existing */
1100 FILE_ATTRIBUTE_NORMAL
,/* normal file */
1101 NULL
); /* no template */
1103 if (hTempFile
== INVALID_HANDLE_VALUE
)
1105 gchar
*msg
= g_win32_error_message(GetLastError());
1106 geany_debug("GetTempFileHandle: Second CreateFile failed (%d)", (gint
) GetLastError());
1108 *error
= g_error_new(G_SPAWN_ERROR
, G_FILE_ERROR
, "%s", msg
);
1116 /* From GDK (they got it from MS Knowledge Base article Q130698) */
1117 static gboolean
resolve_link(HWND hWnd
, wchar_t *link
, gchar
**lpszPath
)
1119 WIN32_FILE_ATTRIBUTE_DATA wfad
;
1121 IShellLinkW
*pslW
= NULL
;
1122 IPersistFile
*ppf
= NULL
;
1123 LPVOID pslWV
= NULL
;
1126 /* Check if the file is empty first because IShellLink::Resolve for some reason succeeds
1127 * with an empty file and returns an empty "link target". (#524151) */
1128 if (!GetFileAttributesExW(link
, GetFileExInfoStandard
, &wfad
) ||
1129 (wfad
.nFileSizeHigh
== 0 && wfad
.nFileSizeLow
== 0))
1134 /* Assume failure to start with: */
1139 hres
= CoCreateInstance(
1140 &CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IShellLinkW
, &pslWV
);
1142 if (SUCCEEDED(hres
))
1144 /* The IShellLink interface supports the IPersistFile interface.
1145 * Get an interface pointer to it. */
1146 pslW
= (IShellLinkW
*) pslWV
;
1147 hres
= pslW
->lpVtbl
->QueryInterface(pslW
, &IID_IPersistFile
, &ppfV
);
1150 if (SUCCEEDED(hres
))
1152 /* Load the file. */
1153 ppf
= (IPersistFile
*) ppfV
;
1154 hres
= ppf
->lpVtbl
->Load(ppf
, link
, STGM_READ
);
1157 if (SUCCEEDED(hres
))
1159 /* Resolve the link by calling the Resolve() interface function. */
1160 hres
= pslW
->lpVtbl
->Resolve(pslW
, hWnd
, SLR_ANY_MATCH
| SLR_NO_UI
);
1163 if (SUCCEEDED(hres
))
1165 wchar_t wtarget
[MAX_PATH
];
1167 hres
= pslW
->lpVtbl
->GetPath(pslW
, wtarget
, MAX_PATH
, NULL
, 0);
1168 if (SUCCEEDED(hres
))
1169 *lpszPath
= g_utf16_to_utf8(wtarget
, -1, NULL
, NULL
, NULL
);
1173 ppf
->lpVtbl
->Release(ppf
);
1176 pslW
->lpVtbl
->Release(pslW
);
1178 return SUCCEEDED(hres
);
1182 /* Checks whether file_name is a Windows shortcut. file_name is expected in UTF-8 encoding.
1183 * If file_name is a Windows shortcut, it returns the target in UTF-8 encoding.
1184 * If it is not a shortcut, it returns a newly allocated copy of file_name. */
1185 gchar
*win32_get_shortcut_target(const gchar
*file_name
)
1188 wchar_t *wfilename
= g_utf8_to_utf16(file_name
, -1, NULL
, NULL
, NULL
);
1190 resolve_link(GDK_WINDOW_HWND(main_widgets
.window
->window
), wfilename
, &path
);
1194 return g_strdup(file_name
);
1200 void win32_set_working_directory(const gchar
*dir
)
1202 SetCurrentDirectory(dir
);
1206 gchar
*win32_get_installation_dir(void)
1208 #if GLIB_CHECK_VERSION(2, 16, 0)
1209 return g_win32_get_package_installation_directory_of_module(NULL
);
1211 return g_win32_get_package_installation_directory(NULL
, NULL
);