This patch adds a switch, /startfile, to explorer.exe, used as "wine
explorer /startfile filename". It accepts a windows or unix path. It
attempts to mimic what windows explorer does to start files and
provide some visible (gui) feedback when it doesn't work. The idea is
that wine.desktop can then be modified to use this switch instead of
wine, solving the following problems when starting exe files from file
managers:
* File managers typically do not set the working directory to the
directory that contains an exe when they start it. Setting the working
directory is the default behavior for windows explorer, and many
windows programs rely on it.
* File managers naturally use the unix path to start files, and the
wine binary passes the unix path along to the exe it starts, rather
than translating it to a windows path (this can and probably should
also be fixed somewhere else).
* Windows explorer always starts exe files with quotes around the path
in the command-line, even if the path does not contain spaces. The
wine binary does not (and probably should not) add quotes in this
situation. A few apps rely on the quotes. See
http://bugs.winehq.org/show_bug.cgi?id=5224#c14

My last attempt created a new winelib program; apparently this
functionality fits in explorer.

-- 
Vincent Povirk
From 5bbdce396e2e864a259ae825e263ddb456706cb3 Mon Sep 17 00:00:00 2001
From: Vincent Povirk <[EMAIL PROTECTED]>
Date: Mon, 17 Mar 2008 00:32:18 -0400
Subject: explorer: add /startfile switch for starting files from file managers

---
 programs/explorer/En.rc              |   33 +++++
 programs/explorer/Makefile.in        |    5 +-
 programs/explorer/explorer.c         |    4 +
 programs/explorer/explorer_private.h |    1 +
 programs/explorer/resources.h        |   25 ++++
 programs/explorer/rsrc.rc            |   26 ++++
 programs/explorer/startfile.c        |  231 ++++++++++++++++++++++++++++++++++
 7 files changed, 324 insertions(+), 1 deletions(-)
 create mode 100644 programs/explorer/En.rc
 create mode 100644 programs/explorer/resources.h
 create mode 100644 programs/explorer/rsrc.rc
 create mode 100644 programs/explorer/startfile.c

diff --git a/programs/explorer/En.rc b/programs/explorer/En.rc
new file mode 100644
index 0000000..f15c2f1
--- /dev/null
+++ b/programs/explorer/En.rc
@@ -0,0 +1,33 @@
+/*
+ * Explorer
+ * English Language Support
+ *
+ * Copyright 2008 Vincent Povirk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+STRINGTABLE DISCARDABLE LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+{
+STRING_NODOSPATH, "This file is not mapped to a drive in Wine."
+
+STRING_NOASSOC, "There is no application associated with this file."
+
+STRING_DLLINITFAILED, "A DLL needed by this application could not be loaded."
+
+STRING_STARTFILEUSAGE "Usage: explorer.exe /startfile filename"
+}
diff --git a/programs/explorer/Makefile.in b/programs/explorer/Makefile.in
index 9498a0f..130536e 100644
--- a/programs/explorer/Makefile.in
+++ b/programs/explorer/Makefile.in
@@ -5,7 +5,7 @@ VPATH     = @srcdir@
 MODULE    = explorer.exe
 APPMODE   = -mwindows
 IMPORTS   = rpcrt4 user32 gdi32 advapi32 kernel32 ntdll
-DELAYIMPORTS = comctl32
+DELAYIMPORTS = comctl32 shell32 shlwapi
 EXTRADEFS = @HALINCL@
 EXTRALIBS = @DISKARBITRATIONLIB@
 
@@ -15,8 +15,11 @@ C_SRCS = \
 	diskarb.c \
 	explorer.c \
 	hal.c \
+	startfile.c \
 	systray.c
 
+RC_SRCS = rsrc.rc
+
 @MAKE_PROG_RULES@
 
 @DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/programs/explorer/explorer.c b/programs/explorer/explorer.c
index f261e11..1df153a 100644
--- a/programs/explorer/explorer.c
+++ b/programs/explorer/explorer.c
@@ -135,6 +135,10 @@ static void ParseCommandLine(LPSTR commandline,parameters_struct *parameters)
         {
             manage_desktop( p + 7 );  /* the rest of the command line is handled by desktop mode */
         }
+        else if (strncmp(p,"startfile",9)==0)
+        {
+            startfile( p + 9 );
+        }
         p2 = p;
         p = strchr(p,'/');
     }
diff --git a/programs/explorer/explorer_private.h b/programs/explorer/explorer_private.h
index 59667eb..1cef227 100644
--- a/programs/explorer/explorer_private.h
+++ b/programs/explorer/explorer_private.h
@@ -26,6 +26,7 @@ extern BOOL add_dos_device( const char *udi, const char *device,
 extern BOOL remove_dos_device( const char *udi );
 
 extern void manage_desktop( char *arg );
+extern void startfile( char *arg );
 extern void initialize_diskarbitration(void);
 extern void initialize_hal(void);
 extern void initialize_systray(void);
diff --git a/programs/explorer/resources.h b/programs/explorer/resources.h
new file mode 100644
index 0000000..eb3c59c
--- /dev/null
+++ b/programs/explorer/resources.h
@@ -0,0 +1,25 @@
+/*
+ * Resource id definitions
+ *
+ * Copyright 2008 Vincent Povirk
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define STRING_NODOSPATH 101
+#define STRING_NOASSOC 102
+#define STRING_DLLINITFAILED 103
+#define STRING_STARTFILEUSAGE 104
+
diff --git a/programs/explorer/rsrc.rc b/programs/explorer/rsrc.rc
new file mode 100644
index 0000000..0cb5497
--- /dev/null
+++ b/programs/explorer/rsrc.rc
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2008 Vincent Povirk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windef.h>
+
+#include "resources.h"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+#include "En.rc"
+
diff --git a/programs/explorer/startfile.c b/programs/explorer/startfile.c
new file mode 100644
index 0000000..c3dab69
--- /dev/null
+++ b/programs/explorer/startfile.c
@@ -0,0 +1,231 @@
+/*
+ * Start a file like windows explorer would given its unix path
+ *
+ * Copyright 2008 Vincent Povirk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windows.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+
+#include "resources.h"
+
+static void output(WCHAR* message)
+{
+    MessageBoxW(NULL, message, NULL, MB_ICONERROR);
+}
+
+static void output_string(int which)
+{
+	WCHAR msg[2048];
+
+	LoadStringW(GetModuleHandle(NULL), which, msg, sizeof(msg)/sizeof(WCHAR));
+
+	output(msg);
+}
+
+static void output_error()
+{
+	WCHAR msg[2048];
+	int error_code;
+
+    error_code = GetLastError();
+    
+    if (error_code == 0)
+    {
+        output_string(STRING_NOASSOC);
+    }
+    else
+    {
+        FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, 0, msg, sizeof(msg)/sizeof(WCHAR), NULL);
+
+    	output(msg);
+	}
+}
+
+static WCHAR* get_parent_dir(WCHAR* path)
+{
+    WCHAR* last_slash;
+    WCHAR* result;
+    int len;
+
+    last_slash = StrRChrW(path, NULL, '\\');
+    if (last_slash == NULL)
+        len = 1;
+    else
+        len = last_slash - path + 1;
+    result = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    StrCpyNW(result, path, len);
+    return result;
+}
+
+void startfile(char *arg)
+{
+    LPWSTR (*wine_get_dos_file_name_ptr)(LPCSTR) = NULL;
+    char* raw_path;
+    DWORD unquoted_path_size;
+    char* unquoted_path;
+    WCHAR* dos_path;
+    WCHAR* dos_parent;
+	SHELLEXECUTEINFOW sei;
+
+	static const WCHAR openW[] = { 'o', 'p', 'e', 'n', 0 };
+	static const WCHAR quote_format[] = {'"', '%', 's', '"', 0};
+	static const WCHAR exe_ext[] = {'.', 'e', 'x', 'e', 0};
+
+    /* find the start of the filename in the command line */
+    if (*arg == ' ' || *arg == '=' || *arg == ',')
+    {
+        raw_path = arg+1;
+    }
+    else
+    {
+        output_string(STRING_STARTFILEUSAGE);
+        ExitProcess(0);
+    }
+    
+    while (*raw_path && isspace(*raw_path))
+    {
+        raw_path++;
+    }
+    
+    /* get the filename without quotes */
+    if (*raw_path == '"')
+    {
+        raw_path++;
+        
+        unquoted_path_size = StrCSpnA(raw_path, "\"");
+    }
+    else
+        unquoted_path_size = StrCSpnA(raw_path, " ");
+    
+    unquoted_path = HeapAlloc(GetProcessHeap(), 0, unquoted_path_size+1);
+    StrCpyNA(unquoted_path, raw_path, unquoted_path_size+1);
+    
+    if (StrChrA(unquoted_path, '/') &&
+        (wine_get_dos_file_name_ptr = (void*)GetProcAddress(GetModuleHandle("KERNEL32"), "wine_get_dos_file_name")) != NULL)
+    {
+        /* translate from a unix path to a dos path */
+        dos_path = wine_get_dos_file_name_ptr(unquoted_path);
+        
+        if (!dos_path)
+        {
+            output_string(STRING_NODOSPATH);
+            ExitProcess(0);
+        }
+    }
+    else
+    {
+        /* translate a dos path to an absolute path with wide characters */
+        WCHAR* rel_path;
+        DWORD rel_path_len = MultiByteToWideChar(CP_ACP, 0, unquoted_path, -1, NULL, 0);
+        
+        rel_path = HeapAlloc(GetProcessHeap(), 0, rel_path_len * sizeof(WCHAR));
+
+        MultiByteToWideChar(CP_ACP, 0, unquoted_path, -1, rel_path, rel_path_len);
+        
+        dos_path = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
+        
+        GetFullPathNameW(rel_path, MAX_PATH, dos_path, NULL);
+        
+        HeapFree(GetProcessHeap(), 0, rel_path);
+    }
+
+    /* find the directory containing the file */
+    dos_parent = get_parent_dir(dos_path);
+
+    if (StrCmpIW(PathFindExtensionW(dos_path), exe_ext) == 0)
+    {
+        /* start an .exe file */
+        WCHAR quoted_dos_path[MAX_PATH+2];
+        STARTUPINFOW startup_info;
+        PROCESS_INFORMATION process_information;
+        
+        /* add quotes around the filename */
+        wnsprintfW(quoted_dos_path, sizeof(quoted_dos_path)/sizeof(WCHAR), quote_format, dos_path);
+        
+        ZeroMemory(&startup_info, sizeof(startup_info));
+        startup_info.cb = sizeof(startup_info);
+        
+        ZeroMemory(&process_information, sizeof(process_information));
+        
+        if (!CreateProcessW(
+              NULL, /* lpApplicationName */
+              quoted_dos_path, /* lpCommandLine */
+              NULL, /* lpProcessAttributes */
+              NULL, /* lpThreadAttributes */
+              FALSE, /* bInheritHandles */
+              CREATE_NEW_CONSOLE, /* dwCreationFlags */
+              NULL, /* lpEnvironment */
+              dos_parent, /* lpCurrentDirectory */
+              &startup_info, /* lpStartupInfo */
+              &process_information /* lpProcessInformation */ ))
+        {
+            output_error();
+        }
+        else
+        {
+            /* Windows would create a dialog if the process can't load a dll, 
+               but Wine prints this information in the (probably invisible) 
+               console. Unfortunately, there's no way to know what the missing
+               dll was, only whether it failed.
+               
+               It's probably possible to fool this check. */
+            
+            if (WaitForInputIdle(process_information.hProcess, INFINITE) == WAIT_FAILED)
+            {
+                DWORD exit_code=0;
+                
+                GetExitCodeProcess(process_information.hProcess, &exit_code);
+                
+                if (exit_code == 0xC0000135)
+                {
+                    output_string(STRING_DLLINITFAILED);
+                }
+            }
+            
+            CloseHandle(process_information.hThread);
+            CloseHandle(process_information.hProcess);
+        }
+
+        HeapFree(GetProcessHeap(), 0, quoted_dos_path);
+    }
+    else
+    {
+        /* build the SHELLEXECUTEINFOW structure and call ShellExecuteExW */
+    	ZeroMemory(&sei, sizeof(sei));
+    	sei.cbSize = sizeof(sei);
+    	sei.lpVerb = openW;
+    	sei.nShow = SW_SHOWNORMAL;
+        sei.fMask = SEE_MASK_FLAG_DDEWAIT|SEE_MASK_FLAG_NO_UI|SEE_MASK_NO_CONSOLE;
+        
+        sei.lpFile = dos_path;
+        sei.lpDirectory = dos_parent;
+        
+        if (!ShellExecuteExW(&sei))
+        {
+            output_error();
+        }
+    }
+    
+    HeapFree(GetProcessHeap(), 0, unquoted_path);
+    HeapFree(GetProcessHeap(), 0, dos_path);
+    HeapFree(GetProcessHeap(), 0, dos_parent);
+    
+    ExitProcess(0);
+}
+
-- 
1.5.4.4



Reply via email to