tags:

views:

87

answers:

3

In my program I need to open a file using an external application (Bentley MicroStation, if it's important). I need application with the opened file to be invisible to user (I will perform some operations over it).

The way to open a file using Process seems fine except for one thing - tests over "example.txt" or "example.doc" show that even when ProcessStartInfo property WindowStyle is set to Minimized or Hidden nevertheless you see the application with opened file.

So, how can I open file without showing my operations to user? Of course, it is possible to hide the app window afterwards, but that's not what I need...

Upd. I work with Bentley MicroStation XM edition. It has a splash screen that can be disabled.

The main problem: when starting MicroStation via Process and ProcessStartInfo with command line argument (name of file to open) and various settings like WindowsStyle (Hidden), UseShellExecute (false), CreateNoWindow (true) application window is present and visible. Simple test with win api WinExec with uCmdShow set to 0 (hidden) gives the same result.

Any solution for starting application in background mode is accepted. Thanks in advance.

+1  A: 

Try setting CreateNoWindow to true to hide the window.

Giorgi
@Giorgi I've tried it already. I don't know if it's important - application has a splash screen.
26071986
A: 

I had the same issue with MS Office splash screens. I failed to hide splash screen programmatically. But MS Office products define command line flags to start without splash screen (for example, /q for Word). So, I can't suggest you universal workaround for all of the applications with splash screen. One of the possible options is to find the way to disable splash screens.
I have never used Bentley Microstation, but one the first google results bring me to this page. Hope this helps.

MAKKAM
@MAKKAM Thank you for the link. But nevertheless I can't get how I can "hide" the application. Was your task to open MS Office files silently? Because no matter if I use MicroStation or WinWord (both without splashes) I see them when the file is opened.
26071986
+2  A: 

It seems to me the most simple way to implement your requirements is to start new process on the other windows desktop. Default process pesktop is "WinSta0\Default". With respect of CreateDesktop Function you can create new desktop for example with the name "MyDesktop" and then start proccess on the desktop.

Following code demonstrate the technique:

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace HiddenDesktop {
    class Program {
        static void Main (string[] args) {
            string desktopName = "MyDesktop";
            string appPath = @"%SystemRoot%\system32\cmd.exe";
            string parameters = @"/c dir C:\ >%TEMP%\dir.txt";
            IntPtr hDesktop = IntPtr.Zero;
            NativeMethods.ProcessInformation processInfo = new NativeMethods.ProcessInformation ();
            bool isSuccess;

            try {
                hDesktop = NativeMethods.CreateDesktop (desktopName, null, null, 0,
                        NativeMethods.ACCESS_MASK.MAXIMUM_ALLOWED, null);
                if (hDesktop == IntPtr.Zero) {
                    DisplayLastErrorMessage ();
                    return;
                }

                NativeMethods.StartupInfo startupInfo = new NativeMethods.StartupInfo ();
                NativeMethods.CreateProcessFlags dwCreationFlags = NativeMethods.CreateProcessFlags.NormalPriorityClass;
                startupInfo.cb = Marshal.SizeOf (typeof (NativeMethods.StartupInfo));
                startupInfo.lpDesktop = "WinSta0\\" + desktopName;
                string currentDirectory = Environment.CurrentDirectory;
                appPath = Environment.ExpandEnvironmentVariables (appPath);
                //if (appPath.IndexOf (' ') != -1)
                //    appPath = '\"' + appPath + '\"';
                StringBuilder sbParameters = new StringBuilder (3 + appPath.Length + parameters.Length);
                if (appPath.IndexOf (' ') != -1) {
                    sbParameters.Append ('\"');
                    sbParameters.Append (appPath);
                    sbParameters.Append ('\"');
                }
                else
                    sbParameters.Append (appPath);
                sbParameters.Append (' ');
                sbParameters.Append (parameters);
                // appPath - full path to the exe without having " if the file has blanks in the path
                isSuccess = NativeMethods.CreateProcess (appPath,
                    sbParameters.ToString(), IntPtr.Zero, IntPtr.Zero, false, dwCreationFlags,
                IntPtr.Zero, currentDirectory, ref startupInfo, out processInfo);
                if (!isSuccess) {
                    DisplayLastErrorMessage ();
                    return;
                }
                NativeMethods.CloseHandle (processInfo.hThread);
                processInfo.hThread = IntPtr.Zero;

                if (NativeMethods.WaitForSingleObject (processInfo.hProcess, NativeMethods.Infinite) == NativeMethods.WaitObject0) {
                    Console.WriteLine ("the process is ended");
                }
            }
            finally {
                if (processInfo.hThread != IntPtr.Zero)
                    isSuccess = NativeMethods.CloseHandle (processInfo.hThread);
                if (processInfo.hProcess != IntPtr.Zero)
                    isSuccess = NativeMethods.CloseHandle (processInfo.hProcess);
                if (hDesktop != IntPtr.Zero)
                    isSuccess = NativeMethods.CloseDesktop (hDesktop);
            }
        }
        static void DisplayLastErrorMessage () {
            int errorCode = Marshal.GetLastWin32Error ();
            Win32Exception error32 = new Win32Exception (errorCode);
            Console.WriteLine ("Error {0}: {1}", errorCode, error32.Message);
        }
    }
    internal static class NativeMethods {
        internal const int NoError = 0;
        internal const UInt32 Infinite = 0xFFFFFFFF;

        internal const UInt32 WaitAbandoned = 0x00000080;
        internal const UInt32 WaitObject0 = 0x00000000;
        internal const UInt32 WaitTimeout = 0x00000102;

        [Flags]
        internal enum ACCESS_MASK : uint {
            DELETE = 0x00010000,
            READ_CONTROL = 0x00020000,
            WRITE_DAC = 0x00040000,
            WRITE_OWNER = 0x00080000,
            SYNCHRONIZE = 0x00100000,

            STANDARD_RIGHTS_REQUIRED = 0x000f0000,

            STANDARD_RIGHTS_READ = 0x00020000,
            STANDARD_RIGHTS_WRITE = 0x00020000,
            STANDARD_RIGHTS_EXECUTE = 0x00020000,

            STANDARD_RIGHTS_ALL = 0x001f0000,

            SPECIFIC_RIGHTS_ALL = 0x0000ffff,

            ACCESS_SYSTEM_SECURITY = 0x01000000,

            MAXIMUM_ALLOWED = 0x02000000,

            GENERIC_READ = 0x80000000,
            GENERIC_WRITE = 0x40000000,
            GENERIC_EXECUTE = 0x20000000,
            GENERIC_ALL = 0x10000000,

            DESKTOP_READOBJECTS = 0x00000001,
            DESKTOP_CREATEWINDOW = 0x00000002,
            DESKTOP_CREATEMENU = 0x00000004,
            DESKTOP_HOOKCONTROL = 0x00000008,
            DESKTOP_JOURNALRECORD = 0x00000010,
            DESKTOP_JOURNALPLAYBACK = 0x00000020,
            DESKTOP_ENUMERATE = 0x00000040,
            DESKTOP_WRITEOBJECTS = 0x00000080,
            DESKTOP_SWITCHDESKTOP = 0x00000100,

            WINSTA_ENUMDESKTOPS = 0x00000001,
            WINSTA_READATTRIBUTES = 0x00000002,
            WINSTA_ACCESSCLIPBOARD = 0x00000004,
            WINSTA_CREATEDESKTOP = 0x00000008,
            WINSTA_WRITEATTRIBUTES = 0x00000010,
            WINSTA_ACCESSGLOBALATOMS = 0x00000020,
            WINSTA_EXITWINDOWS = 0x00000040,
            WINSTA_ENUMERATE = 0x00000100,
            WINSTA_READSCREEN = 0x00000200,

            WINSTA_ALL_ACCESS = 0x0000037f
        }

        [StructLayout (LayoutKind.Sequential)]
        public class SECURITY_ATTRIBUTES {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }
        [StructLayout (LayoutKind.Sequential)]
        internal struct ProcessInformation {
            internal IntPtr hProcess;
            internal IntPtr hThread;
            internal int dwProcessId;
            internal int dwThreadId;
        }

        [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct StartupInfo {
            internal Int32 cb;
            internal string lpReserved;
            internal string lpDesktop;
            internal string lpTitle;
            internal Int32 dwX;
            internal Int32 dwY;
            internal Int32 dwXSize;
            internal Int32 dwYSize;
            internal Int32 dwXCountChars;
            internal Int32 dwYCountChars;
            internal Int32 dwFillAttribute;
            internal Int32 dwFlags;
            internal Int16 wShowWindow;
            internal Int16 cbReserved2;
            internal IntPtr lpReserved2;
            internal IntPtr hStdInput;
            internal IntPtr hStdOutput;
            internal IntPtr hStdError;
        }
        [Flags]
        internal enum CreateProcessFlags : uint {
            DebugProcess = 0x00000001,
            DebugOnlyThisProcess = 0x00000002,
            CreateSuspended = 0x00000004,
            DetachedProcess = 0x00000008,
            CreateNewConsole = 0x00000010,
            NormalPriorityClass = 0x00000020,
            IdlePriorityClass = 0x00000040,
            HighPriorityClass = 0x00000080,
            RealtimePriorityClass = 0x00000100,
            CreateNewProcessGroup = 0x00000200,
            CreateUnicodeEnvironment = 0x00000400,
            CreateSeparateWowVdm = 0x00000800,
            CreateSharedWowVdm = 0x00001000,
            CreateForcedos = 0x00002000,
            BelowNormalPriorityClass = 0x00004000,
            AboveNormalPriorityClass = 0x00008000,
            InheritParentAffinity = 0x00010000,
            InheritCallerPriority = 0x00020000,  // Deprecated
            CreateProtectedProcess = 0x00040000,
            ExtendedStartupinfoPresent = 0x00080000,
            ProcessModeBackgroundBegin = 0x00100000,
            ProcessModeBackgroundEnd = 0x00200000,
            CreateBreakawayFromJob = 0x01000000,
            CreatePreserveCodeAuthzLevel = 0x02000000,
            CreateDefaultErrorMode = 0x04000000,
            CreateNoWindow = 0x08000000,
            ProfileUser = 0x10000000,
            ProfileKernel = 0x20000000,
            ProfileServer = 0x40000000,
            CreateIgnoreSystemDefault = 0x80000000,
        }

        [DllImport ("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern IntPtr CreateDesktop (
                        string desktopName,
                        string device, // must be null.
                        string deviceMode, // must be null,
                        int flags,  // use 0
                        ACCESS_MASK accessMask,
                        SECURITY_ATTRIBUTES attributes);
        [DllImport ("user32.dll", SetLastError = true)]
        [return: MarshalAs (UnmanagedType.Bool)]
        internal static extern bool CloseDesktop (IntPtr hDesktop);

        [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        [return: MarshalAs (UnmanagedType.Bool)]
        internal static extern bool CreateProcess (string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
            bool bInheritHandles, CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment,
            string lpCurrentDirectory, ref StartupInfo lpStartupInfo, out ProcessInformation lpProcessInformation);

        [DllImport ("kernel32.dll", SetLastError = true)]
        [return: MarshalAs (UnmanagedType.Bool)]
        internal static extern bool CloseHandle (IntPtr hObject);
        [DllImport ("kernel32.dll", SetLastError = true)]
        internal static extern UInt32 WaitForSingleObject (IntPtr hHandle, UInt32 dwMilliseconds);
    }
}
Oleg
@Oleg Thank you!
26071986