Shell.cs :  » GUI » Paint.net » PaintDotNet » SystemLayer » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » GUI » Paint.net 
Paint.net » PaintDotNet » SystemLayer » Shell.cs
/////////////////////////////////////////////////////////////////////////////////
// Paint.NET                                                                   //
// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors.     //
// Portions Copyright (C) Microsoft Corporation. All Rights Reserved.          //
// See src/Resources/Files/License.txt for full licensing and attribution      //
// details.                                                                    //
// .                                                                           //
/////////////////////////////////////////////////////////////////////////////////

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace PaintDotNet.SystemLayer{
    public static class Shell
    {
        /// <summary>
        /// Repairs the installation of Paint.NET by replacing any files that have gone missing.
        /// This method should only be called after it has been determined that the files are missing,
        /// and not as a way to determine which files are missing.
        /// This is used, for instance, if the resource files, such as PaintDotNet.Strings.3.resources,
        /// cannot be found. This is actually a top support issue, and by automatically repairing
        /// this problem we save a lot of people a lot of trouble.
        /// </summary>
        /// <param name="missingFiles">
        /// Friendly names for the files that are missing. These will not be used as part of the
        /// repair process but rather as part of any UI presented to the user, or in an exception that 
        /// will be thrown in the case of an error.
        /// </param>
        /// <returns>
        /// true if everything was successful, false if the user cancelled or does not have administrator
        /// privilege (and cannot elevate). An exception is thrown for errors.
        /// </returns>
        /// <remarks>
        /// Note to implementors: This may be implemented as a no-op. Just return true in this case.
        /// </remarks>
        public static bool ReplaceMissingFiles(string[] missingFiles)
        {
            // Generate a friendly, comma separated list of the missing file names
            StringBuilder missingFilesSB = new StringBuilder();

            for (int i = 0; i < missingFiles.Length; ++i)
            {
                missingFilesSB.Append(missingFiles[i]);

                if (i != missingFiles.Length - 1)
                {
                    missingFilesSB.Append(", ");
                }
            }

            try
            {
                // If they are not an admin and have no possibility of elevating, such as for a standard User
                // in XP, then give them an error. Unfortunately we do not know if we can even load text
                // resources at this point, and so must provide an English-only error message.
                if (!Security.IsAdministrator && !Security.CanElevateToAdministrator)
                {
                    MessageBox.Show(
                        null,
                        "Paint.NET has detected that some important installation files are missing. Repairing " +
                        "this requires administrator privilege. Please run the 'PdnRepair.exe' program in the installation " +
                        "directory after logging in with a user that has administrator privilege." + Environment.NewLine + 
                        Environment.NewLine + 
                        "The missing files are: " + missingFilesSB.ToString(),
                        "Paint.NET",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);

                    return false;
                }

                const int hMargin = 8;
                const int vMargin = 8;
                Form form = new Form();
                form.Text = "Paint.NET";
                form.ClientSize = new Size(400, 10);
                form.StartPosition = FormStartPosition.CenterScreen;

                Label infoLabel = new Label();
                form.Controls.Add(infoLabel);
                infoLabel.Text = 
                    "Paint.NET has detected that some important installation files are missing. If you click " +
                    "the Repair button it will attempt to repair this and then continue loading." + Environment.NewLine + 
                    Environment.NewLine +
                    "The missing files are: " + missingFilesSB.ToString();

#if DEBUG
                infoLabel.Text += Environment.NewLine + Environment.NewLine + 
                    "*** Since this is a DEBUG build, you should probably add /skipRepairAttempt to the command-line.";
#endif

                infoLabel.Location = new Point(hMargin, vMargin);
                infoLabel.Width = form.ClientSize.Width - hMargin * 2;
                infoLabel.Height = infoLabel.GetPreferredSize(new Size(infoLabel.Width, 1)).Height;

                Button repairButton = new Button();
                form.Controls.Add(repairButton);
                repairButton.Text = "&Repair";

                Exception exception = null;

                repairButton.Click +=
                    delegate(object sender, EventArgs e)
                    {
                        form.DialogResult = DialogResult.Yes;
                        repairButton.Enabled = false;

                        try
                        {
                            Shell.Execute(form, "PdnRepair.exe", "/noPause", ExecutePrivilege.AsInvokerOrAsManifest, ExecuteWaitType.WaitForExit);
                        }

                        catch (Exception ex)
                        {
                            exception = ex;
                        }
                    };

                repairButton.AutoSize = true;
                repairButton.PerformLayout();
                repairButton.Width += 20;
                repairButton.Location = new Point((form.ClientSize.Width - repairButton.Width) / 2, infoLabel.Bottom + vMargin * 2);
                repairButton.FlatStyle = FlatStyle.System;
                UI.EnableShield(repairButton, true);
                
                form.FormBorderStyle = FormBorderStyle.FixedDialog;
                form.MinimizeBox = false;
                form.MaximizeBox = false;
                form.ShowInTaskbar = true;
                form.Icon = null;
                form.ClientSize = new Size(form.ClientRectangle.Width, repairButton.Bottom + vMargin);

                DialogResult result = form.ShowDialog(null);
                form.Dispose();
                form = null;

                if (result == DialogResult.Yes)
                {
                    return true;
                }
                else if (exception == null)
                {
                    return false;
                }
                else
                {
                    throw new Exception("Error while attempting to repair", exception);
                }                
            }

            catch (Exception ex)
            {
                throw new Exception("Could not repair installation after it was determined that the following files are missing: " +
                    missingFilesSB.ToString(), ex);
            }
        }

        /// <summary>
        /// Opens the requested directory in the shell's file/folder browser.
        /// </summary>
        /// <param name="parent">The window that is currently in the foreground.</param>
        /// <param name="folderPath">The folder to open.</param>
        /// <remarks>
        /// This UI is presented modelessly, in another process, and in the foreground.
        /// Error handling and messaging (error dialogs) will be handled by the shell,
        /// and these errors will not be communicated to the caller of this method.
        /// </remarks>
        public static void BrowseFolder(IWin32Window parent, string folderPath)
        {
            NativeStructs.SHELLEXECUTEINFO sei = new NativeStructs.SHELLEXECUTEINFO();

            sei.cbSize = (uint)Marshal.SizeOf(typeof(NativeStructs.SHELLEXECUTEINFO));
            sei.fMask = NativeConstants.SEE_MASK_NO_CONSOLE;
            sei.lpVerb = "open";
            sei.lpFile = folderPath;
            sei.nShow = NativeConstants.SW_SHOWNORMAL;
            sei.hwnd = parent.Handle;

            bool bResult = NativeMethods.ShellExecuteExW(ref sei);

            if (bResult)
            {
                if (sei.hProcess != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(sei.hProcess);
                    sei.hProcess = IntPtr.Zero;
                }
            }
            else
            {
                NativeMethods.ThrowOnWin32Error("ShellExecuteW returned FALSE");
            }

            GC.KeepAlive(parent);
        }

#if false
        [Obsolete("Do not use this method.", true)]
        public static void Execute(
            IWin32Window parent,
            string exePath,
            string args,
            bool requireAdmin)
        {
            Execute(parent, exePath, args, requireAdmin ? ExecutePrivilege.RequireAdmin : ExecutePrivilege.AsInvokerOrAsManifest, ExecuteWaitType.ReturnImmediately);
        }
#endif

        private const string updateExeFileName = "UpdateMonitor.exe";

        private delegate int ExecuteHandOff(IWin32Window parent, string exePath, string args, out IntPtr hProcess);

        /// <summary>
        /// Uses the shell to execute the command. This method must only be used by Paint.NET
        /// and not by plugins.
        /// </summary>
        /// <param name="parent">
        /// The window that is currently in the foreground. This may be null if requireAdmin 
        /// is false and the executable that exePath refers to is not marked (e.g. via a 
        /// manifest) as requiring administrator privilege.
        /// </param>
        /// <param name="exePath">
        /// The path to the executable to launch.
        /// </param>
        /// <param name="args">
        /// The command-line arguments for the executable.
        /// </param>
        /// <param name="execPrivilege">
        /// The privileges to execute the new process with.
        /// If the executable is already marked as requiring administrator privilege
        /// (e.g. via a "requiresAdministrator" UAC manifest), this parameter should be 
        /// set to AsInvokerOrAsManifest.
        /// </param>
        /// <remarks>
        /// If administrator privilege is required, a consent UI may be displayed asking the
        /// user to approve the action. A parent window must be provided in this case so that
        /// the consent UI will know where to position itself. Administrator privilege is
        /// required if execPrivilege is set to RequireAdmin, or if the executable being launched
        /// has a manifest declaring that it requires this privilege and if the operating
        /// system recognizes the manifest.
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// execPrivilege was RequireAdmin, but parent was null.
        /// </exception>
        /// <exception cref="SecurityException">
        /// execPrivilege was RequireAdmin, but the user does not have this privilege, nor do they 
        /// have the ability to acquire or elevate to obtain this privilege.
        /// </exception>
        /// <exception cref="Win32Exception">
        /// There was an error launching the program.
        /// </exception>
        public static void Execute(
            IWin32Window parent, 
            string exePath, 
            string args,
            ExecutePrivilege execPrivilege, 
            ExecuteWaitType execWaitType)
        {
            if (exePath == null)
            {
                throw new ArgumentNullException("exePath");
            }

            if (execPrivilege == ExecutePrivilege.RequireAdmin && parent == null)
            {
                throw new ArgumentException("If requireAdmin is true, a parent window must be provided");
            }

            // If this action requires admin privilege, but the user does not have this
            // privilege and is not capable of acquiring this privilege, then we will
            // throw an exception.
            if (execPrivilege == ExecutePrivilege.RequireAdmin && 
                !Security.IsAdministrator && 
                !Security.CanElevateToAdministrator)
            {
                throw new SecurityException("Executable requires administrator privilege, but user is not an administrator and cannot elevate");
            }

            ExecuteHandOff executeHandOff = null;
            switch (execPrivilege)
            {
                case ExecutePrivilege.AsInvokerOrAsManifest:
                    executeHandOff = new ExecuteHandOff(ExecAsInvokerOrAsManifest);
                    break;

                case ExecutePrivilege.RequireAdmin:
                    executeHandOff = new ExecuteHandOff(ExecRequireAdmin);
                    break;

                case ExecutePrivilege.RequireNonAdminIfPossible:
                    if (Security.CanLaunchNonAdminProcess)
                    {
                        executeHandOff = new ExecuteHandOff(ExecRequireNonAdmin);
                    }
                    else
                    {
                        executeHandOff = new ExecuteHandOff(ExecAsInvokerOrAsManifest);
                    }
                    break;

                default:
                    throw new InvalidEnumArgumentException("ExecutePrivilege");
            }

            string updateMonitorExePath = null;
            if (execWaitType == ExecuteWaitType.RelaunchPdnOnExit)
            {
                RelaunchPdnHelperPart1(out updateMonitorExePath);
            }

            IntPtr hProcess = IntPtr.Zero;
            int nResult = executeHandOff(parent, exePath, args, out hProcess);

            if (nResult == NativeConstants.ERROR_SUCCESS)
            {
                if (execWaitType == ExecuteWaitType.WaitForExit)
                {
                    SafeNativeMethods.WaitForSingleObject(hProcess, NativeConstants.INFINITE);
                }
                else if (execWaitType == ExecuteWaitType.RelaunchPdnOnExit)
                {
                    bool bResult2 = SafeNativeMethods.SetHandleInformation(
                        hProcess, 
                        NativeConstants.HANDLE_FLAG_INHERIT, 
                        NativeConstants.HANDLE_FLAG_INHERIT);

                    RelaunchPdnHelperPart2(updateMonitorExePath, hProcess);

                    // Ensure that we don't close the process handle right away in the next few lines of code.
                    // It must be inherited by the child process. Yes, this is technically a leak but we are
                    // planning to terminate in just a moment anyway.
                    hProcess = IntPtr.Zero; 
                }
                else if (execWaitType == ExecuteWaitType.ReturnImmediately)
                {
                }

                if (hProcess != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(hProcess);
                    hProcess = IntPtr.Zero;
                }
            }
            else
            {
                if (nResult == NativeConstants.ERROR_CANCELLED ||
                    nResult == NativeConstants.ERROR_TIMEOUT)
                {
                    // no problem
                }
                else
                {
                    NativeMethods.ThrowOnWin32Error("ExecuteHandoff failed", nResult);
                }

                if (updateMonitorExePath != null)
                {
                    try
                    {
                        File.Delete(updateMonitorExePath);
                    }

                    catch (Exception)
                    {
                    }

                    updateMonitorExePath = null;
                }
            }

            GC.KeepAlive(parent);
        }

        private static int ExecAsInvokerOrAsManifest(IWin32Window parent, string exePath, string args, out IntPtr hProcess)
        {
            return ExecShellExecuteEx(parent, exePath, args, null, out hProcess);
        }

        private static int ExecRequireAdmin(IWin32Window parent, string exePath, string args, out IntPtr hProcess)
        {
            const string runAs = "runas";
            string verb;

            if (Security.IsAdministrator)
            {
                verb = null;
            }
            else
            {
                verb = runAs;
            }

            return ExecShellExecuteEx(parent, exePath, args, verb, out hProcess);
        }

        private static int ExecRequireNonAdmin(IWin32Window parent, string exePath, string args, out IntPtr hProcess)
        {
            int nError = NativeConstants.ERROR_SUCCESS;
            string commandLine = "\"" + exePath + "\"" + (args == null ? "" : (" " + args));

            string dir;

            try
            {
                dir = Path.GetDirectoryName(exePath);
            }

            catch (Exception)
            {
                dir = null;
            }

            IntPtr hWndShell = IntPtr.Zero;
            IntPtr hShellProcess = IntPtr.Zero;
            IntPtr hShellProcessToken = IntPtr.Zero;
            IntPtr hTokenCopy = IntPtr.Zero;
            IntPtr bstrExePath = IntPtr.Zero;
            IntPtr bstrCommandLine = IntPtr.Zero;
            IntPtr bstrDir = IntPtr.Zero;
            NativeStructs.PROCESS_INFORMATION procInfo = new NativeStructs.PROCESS_INFORMATION();

            try
            {
                hWndShell = SafeNativeMethods.FindWindowW("Progman", null);
                if (hWndShell == IntPtr.Zero)
                {
                    NativeMethods.ThrowOnWin32Error("FindWindowW() returned NULL");
                }

                uint dwPID;
                uint dwThreadId = SafeNativeMethods.GetWindowThreadProcessId(hWndShell, out dwPID);
                if (0 == dwPID)
                {
                    NativeMethods.ThrowOnWin32Error("GetWindowThreadProcessId returned 0", NativeErrors.ERROR_FILE_NOT_FOUND);
                }

                hShellProcess = NativeMethods.OpenProcess(NativeConstants.PROCESS_QUERY_INFORMATION, false, dwPID);
                if (IntPtr.Zero == hShellProcess)
                {
                    NativeMethods.ThrowOnWin32Error("OpenProcess() returned NULL");
                }

                bool optResult = NativeMethods.OpenProcessToken(
                    hShellProcess,
                    NativeConstants.TOKEN_ASSIGN_PRIMARY | NativeConstants.TOKEN_DUPLICATE | NativeConstants.TOKEN_QUERY,
                    out hShellProcessToken);

                if (!optResult)
                {
                    NativeMethods.ThrowOnWin32Error("OpenProcessToken() returned FALSE");
                }

                bool dteResult = NativeMethods.DuplicateTokenEx(
                    hShellProcessToken,
                    NativeConstants.MAXIMUM_ALLOWED,
                    IntPtr.Zero,
                    NativeConstants.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                    NativeConstants.TOKEN_TYPE.TokenPrimary,
                    out hTokenCopy);

                if (!dteResult)
                {
                    NativeMethods.ThrowOnWin32Error("DuplicateTokenEx() returned FALSE");
                }

                bstrExePath = Marshal.StringToBSTR(exePath);
                bstrCommandLine = Marshal.StringToBSTR(commandLine);
                bstrDir = Marshal.StringToBSTR(dir);

                bool cpwtResult = NativeMethods.CreateProcessWithTokenW(
                    hTokenCopy,
                    0,
                    bstrExePath,
                    bstrCommandLine,
                    0,
                    IntPtr.Zero,
                    bstrDir,
                    IntPtr.Zero,
                    out procInfo);

                if (cpwtResult)
                {
                    hProcess = procInfo.hProcess;
                    procInfo.hProcess = IntPtr.Zero;
                    nError = NativeConstants.ERROR_SUCCESS;
                }
                else
                {
                    hProcess = IntPtr.Zero;
                    nError = Marshal.GetLastWin32Error();
                }
            }

            catch (Win32Exception ex)
            {
                Tracing.Ping(ex.ToString());
                nError = ex.ErrorCode;
                hProcess = IntPtr.Zero;
            }

            finally
            {
                if (bstrExePath != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrExePath);
                    bstrExePath = IntPtr.Zero;
                }

                if (bstrCommandLine != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrCommandLine);
                    bstrCommandLine = IntPtr.Zero;
                }

                if (bstrDir != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrDir);
                    bstrDir = IntPtr.Zero;
                }

                if (hShellProcess != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(hShellProcess);
                    hShellProcess = IntPtr.Zero;
                }
                
                if (hShellProcessToken != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(hShellProcessToken);
                    hShellProcessToken = IntPtr.Zero;
                }

                if (hTokenCopy != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(hTokenCopy);
                    hTokenCopy = IntPtr.Zero;
                }

                if (procInfo.hThread != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(procInfo.hThread);
                    procInfo.hThread = IntPtr.Zero;
                }

                if (procInfo.hProcess != IntPtr.Zero)
                {
                    SafeNativeMethods.CloseHandle(procInfo.hProcess);
                    procInfo.hProcess = IntPtr.Zero;
                }
            }

            return nError;
        }

        private static int ExecShellExecuteEx(IWin32Window parent, string exePath, string args, string verb, out IntPtr hProcess)
        {
            string dir;

            try
            {
                dir = Path.GetDirectoryName(exePath);
            }

            catch (Exception)
            {
                dir = null;
            }

            NativeStructs.SHELLEXECUTEINFO sei = new NativeStructs.SHELLEXECUTEINFO();
            sei.cbSize = (uint)Marshal.SizeOf(typeof(NativeStructs.SHELLEXECUTEINFO));

            sei.fMask =
                NativeConstants.SEE_MASK_NOCLOSEPROCESS |
                NativeConstants.SEE_MASK_NO_CONSOLE |
                NativeConstants.SEE_MASK_FLAG_DDEWAIT;

            sei.lpVerb = verb;
            sei.lpDirectory = dir;
            sei.lpFile = exePath;
            sei.lpParameters = args;

            sei.nShow = NativeConstants.SW_SHOWNORMAL;

            if (parent != null)
            {
                sei.hwnd = parent.Handle;
            }

            bool bResult = NativeMethods.ShellExecuteExW(ref sei);
            hProcess = sei.hProcess;
            sei.hProcess = IntPtr.Zero;

            int nResult = NativeConstants.ERROR_SUCCESS;

            if (!bResult)
            {
                nResult = Marshal.GetLastWin32Error();    
            }

            return nResult;
        }

        private static void RelaunchPdnHelperPart1(out string updateMonitorExePath)
        {
            string srcDir = Application.StartupPath;
            string srcPath = Path.Combine(srcDir, updateExeFileName);
            string srcPath2 = srcPath + ".config";

            string dstDir = Environment.ExpandEnvironmentVariables(@"%TEMP%\PdnSetup");
            string dstPath = Path.Combine(dstDir, updateExeFileName);
            string dstPath2 = dstPath + ".config";

            if (!Directory.Exists(dstDir))
            {
                Directory.CreateDirectory(dstDir);
            }

            File.Copy(srcPath, dstPath, true);
            File.Copy(srcPath2, dstPath2, true);
            updateMonitorExePath = dstPath;
        }

        private static void RelaunchPdnHelperPart2(string updateMonitorExePath, IntPtr hProcess)
        {
            string args = hProcess.ToInt64().ToString(CultureInfo.InstalledUICulture);
            ProcessStartInfo psi = new ProcessStartInfo(updateMonitorExePath, args);
            psi.UseShellExecute = false;
            psi.WindowStyle = ProcessWindowStyle.Hidden;
            Process process = Process.Start(psi);
            process.Dispose();
        }

        /// <summary>
        /// Asynchronously restarts Paint.NET.
        /// </summary>
        /// <remarks>
        /// This method does not restart Paint.NET immediately. Instead, it waits
        /// for Paint.NET to terminate and then restarts it. It does not perform
        /// the termination or shutdown.
        /// </remarks>
        public static void RestartApplication()
        {
            string srcDir = Application.StartupPath;
            string updateExePath = Path.Combine(srcDir, updateExeFileName);

            Process thisProcess = Process.GetCurrentProcess();
            IntPtr hProcess = thisProcess.Handle;

            bool bResult = SafeNativeMethods.SetHandleInformation(
                hProcess,
                NativeConstants.HANDLE_FLAG_INHERIT,
                NativeConstants.HANDLE_FLAG_INHERIT);

            if (!bResult)
            {
                NativeMethods.ThrowOnWin32Error("SetHandleInformation() returned false");
            }

            RelaunchPdnHelperPart2(updateExePath, hProcess);
        }

        /// <summary>
        /// Launches the default browser and opens the given URL.
        /// </summary>
        /// <param name="url">The URL to show. The maximum length is 512 characters.</param>
        /// <remarks>
        /// This method will not present an error dialog if the URL could not be launched.
        /// Note: This method must only be used by Paint.NET, and not any plugins. It may
        /// change or be removed in future versions.
        /// </remarks>
        public static bool LaunchUrl(IWin32Window owner, string url)
        {
            if (url.Length > 512)
            {
                throw new ArgumentOutOfRangeException("url.Length must be <= 512");
            }

            bool success = false;
            string quotedUrl = "\"" + url + "\"";
            ExecutePrivilege executePrivilege;

            if (!Security.IsAdministrator || (Security.IsAdministrator && !Security.CanLaunchNonAdminProcess))
            {
                executePrivilege = ExecutePrivilege.AsInvokerOrAsManifest;
            }
            else
            {
                executePrivilege = ExecutePrivilege.RequireNonAdminIfPossible;
            }

            // Method 1. Just launch the url, and hope that the shell figures out the association correctly. 
            // This method will not work with ExecutePrivilege.RequireNonAdmin though.
            if (!success && executePrivilege != ExecutePrivilege.RequireNonAdminIfPossible)
            {
                try
                {
                    Execute(owner, quotedUrl, null, executePrivilege, ExecuteWaitType.ReturnImmediately);
                    success = true;
                }

                catch (Exception ex)
                {
                    Tracing.Ping("Exception while using method 1 to launch url, " + quotedUrl + ", :" + ex.ToString());
                    success = false;
                }
            }

            // Method 2. Launch the url through explorer
            if (!success)
            {
                const string shellFileLoc = @"%WINDIR%\explorer.exe";
                string shellExePath = "(n/a)";

                try
                {
                    shellExePath = Environment.ExpandEnvironmentVariables(shellFileLoc);
                    Execute(owner, shellExePath, quotedUrl, executePrivilege, ExecuteWaitType.ReturnImmediately);
                    success = true;
                }

                catch (Exception ex)
                {
                    Tracing.Ping("Exception while using method 2 to launch url through '" + shellExePath + "', " + quotedUrl + ", : " + ex.ToString());
                    success = false;
                }
            }

            return success;
        }

        [Obsolete("Use PdnInfo.OpenUrl() instead. Shell.LaunchUrl() must only be used by Paint.NET code, not by plugins.", true)]
        public static bool OpenUrl(IWin32Window owner, string url)
        {
            return LaunchUrl(owner, url);
        }

        public static void AddToRecentDocumentsList(string fileName)
        {
            // Apparently SHAddToRecentDocs can block for a very long period of time when certain
            // conditions are met: so we just stick it on "the backburner."
            ThreadPool.QueueUserWorkItem(new WaitCallback(AddToRecentDocumentsListImpl), fileName);
        }

        private static void AddToRecentDocumentsListImpl(object fileNameObj)
        {
            string fileName = (string)fileNameObj;
            IntPtr bstrFileName = IntPtr.Zero;

            try
            {
                bstrFileName = Marshal.StringToBSTR(fileName);
                NativeMethods.SHAddToRecentDocs(NativeConstants.SHARD_PATHW, bstrFileName);
            }

            finally
            {
                if (bstrFileName != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(bstrFileName);
                    bstrFileName = IntPtr.Zero;
                }
            }
        }

        // TODO: convert to extension method in the 4.0 codebase, which can use .NET 3.5
        private static T2 Map<T1, T2>(T1 mapFrom, Pair<T1, T2>[] mappings)
        {
            foreach (Pair<T1, T2> mapping in mappings)
            {
                if (mapping.First.Equals(mapFrom))
                {
                    return mapping.Second;
                }
            }

            throw new KeyNotFoundException();
        }

        private static string GetCSIDLPath(int csidl, bool tryCreateIfAbsent)
        {
            // First, try calling SHGetFolderPathW with the "CSIDL_FLAG_CREATE" flag. However, if it 
            // returns an error then ignore it. We've had some crash logs with "access denied" coming
            // from this function.
            int csidlWithFlags = csidl | (tryCreateIfAbsent ? NativeConstants.CSIDL_FLAG_CREATE : 0);
            StringBuilder sbWithFlags = new StringBuilder(NativeConstants.MAX_PATH);
            Do.TryBool(() => NativeMethods.SHGetFolderPathW(IntPtr.Zero, csidlWithFlags, IntPtr.Zero, NativeConstants.SHGFP_TYPE_CURRENT, sbWithFlags));

            StringBuilder sb = new StringBuilder(NativeConstants.MAX_PATH);
            NativeMethods.SHGetFolderPathW(IntPtr.Zero, csidl, IntPtr.Zero, NativeConstants.SHGFP_TYPE_CURRENT, sb);

            // If we get back something like 'Z:' then we need to put a backslash on it.
            // Otherwise other path-related functions will freak out.
            if (sb.Length == 2 && sb[1] == ':')
            {
                sb.Append(Path.DirectorySeparatorChar);
            }

            string path = sb.ToString();

            return path;
        }

        private static readonly Pair<VirtualFolderName, int>[] pathMappings = new Pair<VirtualFolderName, int>[]
            {
                Pair.Create(VirtualFolderName.SystemProgramFiles, NativeConstants.CSIDL_PROGRAM_FILES),
                Pair.Create(VirtualFolderName.UserDesktop, NativeConstants.CSIDL_DESKTOP_DIRECTORY),
                Pair.Create(VirtualFolderName.UserDocuments, NativeConstants.CSIDL_PERSONAL),
                Pair.Create(VirtualFolderName.UserLocalAppData, NativeConstants.CSIDL_LOCAL_APPDATA),
                Pair.Create(VirtualFolderName.UserPictures, NativeConstants.CSIDL_MYPICTURES),
                Pair.Create(VirtualFolderName.UserRoamingAppData, NativeConstants.CSIDL_APPDATA) 
            };

        public static string GetVirtualPath(VirtualFolderName folderName, bool tryCreateIfAbsent)
        {
            try
            {
                int csidl = Map(folderName, pathMappings);
                string path = GetCSIDLPath(csidl, tryCreateIfAbsent);
                return path;
            }

            catch (KeyNotFoundException)
            {
                throw new InvalidEnumArgumentException("folderName", (int)folderName, typeof(VirtualFolderName));
            }
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.