FileDialog.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / Microsoft / Win32 / FileDialog.cs / 1 / FileDialog.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//              FileDialog is an abstract class derived from CommonDialog 
//              that implements shared functionality common to both File
//              Open and File Save common dialogs.  It provides a hook 
//              procedure that handles messages received while the dialog
//              is visible and numerous properties to control the appearance
//              and behavior of the dialog.
// 
//              The actual call to display the dialog to GetOpenFileName()
//              or GetSaveFileName() (both functions defined in commdlg.dll) 
//              is implemented in a derived class's RunFileDialog method. 
//
// 
// History:
//              [....]     7/7/2005        Created
//
//--------------------------------------------------------------------------- 

 
namespace Microsoft.Win32 
{
    using MS.Internal; 
    using MS.Internal.PresentationFramework;
    using MS.Win32;

    using System; 
    using System.ComponentModel;
    using System.Collections.Generic; 
    using System.IO; 
    using System.Runtime.InteropServices;
    using System.Security; 
    using System.Security.Permissions;
    using System.Text;
    using System.Threading;
    using System.Runtime.Remoting; 
    using System.Windows;
 
    using CharBuffer = MS.Win32.NativeMethods.CharBuffer; 

    ///  
    ///    Provides a common base class for wrappers around both the
    ///    File Open and File Save common dialog boxes.  Derives from
    ///    CommonDialog.
    /// 
    ///    This class is not intended to be derived from except by
    ///    the OpenFileDialog and SaveFileDialog classes. 
    ///  
    public abstract class FileDialog : CommonDialog
    { 
        //---------------------------------------------------
        //
        // Constructors
        // 
        //---------------------------------------------------
        #region Constructors 
 
        /// 
        /// In an inherited class, initializes a new instance of 
        /// the System.Windows.FileDialog class.
        /// 
        /// 
        ///     Critical: Sets Dialog options, which are critical for set. 
        ///     TreatAsSafe: It is okay to set the options to their defaults.  The
        ///             ctor does not show the dialog. 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        protected FileDialog() 
        {
            // Call Initialize to set defaults for fields
            // and to set defaults for some option flags.
            // Initialize() is also called from the virtual 
            // Reset() function to restore defaults.
            Initialize(); 
        } 

        #endregion Constructors 

        //----------------------------------------------------
        //
        // Public Methods 
        //
        //--------------------------------------------------- 
        #region Public Methods 

        ///  
        ///  Resets all properties to their default values.
        ///  Classes derived from FileDialog are expected to
        ///  call Base.Reset() at the beginning of their
        ///  implementation of Reset() if they choose to 
        ///  override this function.
        ///  
        ///  
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: Sets Dialog options, which are critical for set.
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted)
        ///  
        [SecurityCritical]
        public override void Reset() 
        { 
            SecurityHelper.DemandUnrestrictedFileIOPermission();
 
            Initialize();
        }

        ///  
        ///  Returns a string representation of the file dialog with key information
        ///  for debugging purposes. 
        ///  
        //   We overload ToString() so that we can provide a useful representation of
        //   this object for users' debugging purposes.  It provides the full pathname for 
        //   any files selected.
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: "); 
            sb.Append(FileName);
            return sb.ToString(); 
 
            //
 



            /* 
            catch (System.Security.SecurityException e)
            { 
                sb.Append("<");                          // Coding guidelines require ToString to not throw 
                sb.Append(e.GetType().FullName);         // an exception;  we catch SecurityException so we
                sb.Append(">");                          // can show filenames in full trust and not fail in 
                                                         // reduced permissions scenarios.
            }
            */
        } 

        #endregion Public Methods 
 
        //----------------------------------------------------
        // 
        // Public Properties
        //
        //----------------------------------------------------
        #region Public Properties 

        // 
        //   The behavior governed by this property depends 
        //   on whether CheckFileExists is set and whether the
        //   filter contains a valid extension to use.  For 
        //   details, see the ProcessFileNames function.
        //
        //   It's worth noting that unlike most of these
        //   properties, AddExtension is a custom flag that 
        //   is unique to our implementation.  As such, it is
        //   a constant value in our class, not stored in 
        //   NativeMethods like the other flags. 
        /// 
        ///  Gets or sets a value indicating whether the 
        ///  dialog box automatically adds an extension to a
        ///  file name if the user omits the extension.
        /// 
        ///  
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     Critical: Dialog options are critical for set. (Only critical for set
        ///             because setting options affects the behavior of the FileDialog) 
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted)
        /// 
        public bool AddExtension
        { 
            get
            { 
                return GetOption(OPTION_ADDEXTENSION); 
            }
            [SecurityCritical] 
            set
            {
                SecurityHelper.DemandUnrestrictedFileIOPermission();
 
                SetOption(OPTION_ADDEXTENSION, value);
            } 
        } 

 
        //
        //   OFN_FILEMUSTEXIST is only used for Open dialog
        //   boxes, according to MSDN.  It implies
        //   OFN_PATHMUSTEXIST and "cannot be used" with a 
        //   Save As dialog box...  in practice, it seems
        //   to be ignored when used with Save As boxes 
        ///  
        ///  Gets or sets a value indicating whether
        ///  the dialog box displays a warning if the 
        ///  user specifies a file name that does not exist.
        /// 
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: Dialog options are critical for set. (Only critical for set 
        ///             because setting options affects the behavior of the FileDialog)
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted) 
        /// 
        public virtual bool CheckFileExists
        {
            get 
            {
                return GetOption(NativeMethods.OFN_FILEMUSTEXIST); 
            } 
            [SecurityCritical]
            set 
            {
                SecurityHelper.DemandUnrestrictedFileIOPermission();

                SetOption(NativeMethods.OFN_FILEMUSTEXIST, value); 
            }
        } 
 

        ///  
        ///  Specifies that the user can type only valid paths and file names. If this flag is
        ///  used and the user types an invalid path and file name in the File Name entry field,
        ///  a warning is displayed in a message box.
        ///  
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        ///  
        /// 
        ///     Critical: Dialog options are critical for set. (Only critical for set 
        ///             because setting options affects the behavior of the FileDialog)
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted)
        /// 
        public bool CheckPathExists 
        {
            get 
            { 
                return GetOption(NativeMethods.OFN_PATHMUSTEXIST);
            } 
            [SecurityCritical]
            set
            {
                SecurityHelper.DemandUnrestrictedFileIOPermission(); 

                SetOption(NativeMethods.OFN_PATHMUSTEXIST, value); 
            } 
        }
 
        /// 
        /// The AddExtension property attempts to determine the appropriate extension
        /// by using the selected filter.  The DefaultExt property serves as a fallback -
        ///  if the extension cannot be determined from the filter, DefaultExt will 
        /// be used instead.
        ///  
        public string DefaultExt 
        {
            get 
            {
                // For string properties, it's important to not return null, as an empty
                // string tends to make more sense to beginning developers.
                return _defaultExtension == null ? String.Empty : _defaultExtension; 
            }
 
            set 
            {
                if (value != null) 
                {
                    // Use Ordinal here as per FxCop CA1307
                    if (value.StartsWith(".", StringComparison.Ordinal)) // Allow calling code to provide
                                                                         // extensions like ".ext" - 
                    {
                        value = value.Substring(1);    // but strip out the period to leave only "ext" 
                    } 
                    else if (value.Length == 0)         // Normalize empty strings to null.
                    { 
                        value = null;
                    }
                }
                _defaultExtension = value; 
            }
        } 
 
        //   The actual flag is OFN_NODEREFERENCELINKS (set = do not dereference, unset = deref) -
        //   while we have true = dereference and false=do not dereference.  Because we expose 
        //   the opposite of the Windows flag as a property to be clearer, we need to negate
        //   the value in both the getter and the setter here.
        /// 
        ///  Gets or sets a value indicating whether the dialog box returns the location 
        ///  of the file referenced by the shortcut or whether it returns the location
        ///  of the shortcut (.lnk). 
        ///  
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        /// 
        ///     Critical: Dialog options are critical for set. (Only critical for set
        ///             because setting options affects the behavior of the FileDialog) 
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted)
        ///  
        public bool DereferenceLinks 
        {
            get 
            {
                return !GetOption(NativeMethods.OFN_NODEREFERENCELINKS);
            }
            [SecurityCritical] 
            set
            { 
                SecurityHelper.DemandUnrestrictedFileIOPermission(); 

                SetOption(NativeMethods.OFN_NODEREFERENCELINKS, !value); 
            }
        }

        ///  
        ///  Gets a string containing the filename component of the
        ///  file selected in the dialog box. 
        /// 
        ///  Example:  if FileName = "c:\windows\explorer.exe" ,
        ///              SafeFileName = "explorer.exe" 
        /// 
        /// 
        ///     Critical: Do not want to allow access to raw paths to Parially Trusted Applications.
        ///     PublicOk: Scrubs paths from the file name. 
        /// 
        public string SafeFileName 
        { 
            [SecurityCritical]
            get 
            {
                // Use the FileName property to avoid directly accessing
                // the _fileNames field, then call Path.GetFileName
                // to do the actual work of stripping out the file name 
                // from the path.
                string safeFN = Path.GetFileName(CriticalFileName); 
 
                // Check to make sure Path.GetFileName does not return null.
                // If it does, set safeFN to String.Empty instead to accomodate 
                // programmers that fail to check for null when reading strings.
                if (safeFN == null)
                {
                    safeFN = String.Empty; 
                }
 
                return safeFN; 
            }
        } 

        /// 
        ///  Gets a string array containing the filename of each file selected
        ///  in the dialog box. 
        /// 
        ///  
        ///     Critical: Do not want to allow access to raw paths to Parially Trusted Applications. 
        ///     PublicOk: Scrubs paths from the file names.
        ///  
        public string[] SafeFileNames
        {
            [SecurityCritical]
            get 
            {
                // Retrieve the existing filenames into an array, then make 
                // another array of the same length to hold the safe version. 
                string[] unsafeFileNames = FileNamesInternal;
                string[] safeFileNames = new string[unsafeFileNames.Length]; 

                for (int i = 0; i < unsafeFileNames.Length; i++)
                {
                    // Call Path.GetFileName to retrieve only the filename 
                    // component of the current full path.
                    safeFileNames[i] = Path.GetFileName(unsafeFileNames[i]); 
 
                    // Check to make sure Path.GetFileName does not return null.
                    // If it does, set this filename to String.Empty instead to accomodate 
                    // programmers that fail to check for null when reading strings.
                    if (safeFileNames[i] == null)
                    {
                        safeFileNames[i] = String.Empty; 
                    }
                } 
 
                return safeFileNames;
            } 
        }

        //   If multiple files are selected, we only return the first filename.
        ///  
        ///  Gets or sets a string containing the full path of the file selected in
        ///  the file dialog box. 
        ///  
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        /// 
        ///     Critical: Do not want to allow access to raw paths to Parially Trusted Applications.
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted) 
        /// 
        public string FileName 
        { 
            [SecurityCritical]
            get 
            {
                SecurityHelper.DemandUnrestrictedFileIOPermission();
                return CriticalFileName;
            } 
            [SecurityCritical]
            set 
            { 
                SecurityHelper.DemandUnrestrictedFileIOPermission();
 
                // Allow users to set a filename to stored in _fileNames.
                // If null is passed in, we clear the entire list.
                // If we get a string, we clear the entire list and make a new one-element
                // array with the new string. 
                if (value == null)
                { 
                    _fileNames = null; 
                }
                else 
                {
                    //

                    _fileNames = new string[] { value }; 
                }
            } 
        } 

 
        /// 
        ///     Gets the file names of all selected files in the dialog box.
        /// 
        ///  
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     Critical: Do not want to allow access to raw paths to Parially Trusted Applications.
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted) 
        /// 
        public string[] FileNames
        {
            [SecurityCritical] 
            get
            { 
                SecurityHelper.DemandUnrestrictedFileIOPermission(); 

                // FileNamesInternal is a property we use to clone 
                // the string array before returning it.
                string[] files = FileNamesInternal;
                return files;
            } 
        }
 
        //   The filter string also controls how the AddExtension feature behaves.  For 
        //   details, see the ProcessFileNames method.
        ///  
        ///       Gets or sets the current file name filter string,
        ///       which determines the choices that appear in the "Save as file type" or
        ///       "Files of type" box at the bottom of the dialog box.
        /// 
        ///       This is an example filter string:
        ///       Filter = "Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*" 
        ///  
        /// 
        ///  Thrown in the setter if the new filter string does not have an even number of tokens 
        ///  separated by the vertical bar character '|' (that is, the new filter string is invalid.)
        /// 
        /// 
        ///  If DereferenceLinks is true and the filter string is null, a blank 
        ///  filter string (equivalent to "|*.*") will be automatically substituted to work
        ///  around the issue documented in Knowledge Base article 831559 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        public string Filter 
        {
            get
            {
                // For string properties, it's important to not return null, as an empty 
                // string tends to make more sense to beginning developers.
                return _filter == null ? String.Empty : _filter; 
            } 

            set 
            {
                if (String.CompareOrdinal(value,_filter) != 0)   // different filter than what we have stored already
                {
                    string updatedFilter = value; 

                    if (!String.IsNullOrEmpty(updatedFilter)) 
                    { 
                        // Require the number of segments of the filter string to be even -
                        // in other words, there must only be matched pairs of description and 
                        // file extensions.
                        //
                        // This implicitly requires there to be at least one vertical bar in
                        // the filter string - or else formats.Length will be 1, resulting in an 
                        // ArgumentException.
 
                        string[] formats = updatedFilter.Split('|'); 

                        if (formats.Length % 2 != 0) 
                        {
                            throw new ArgumentException(SR.Get(SRID.FileDialogInvalidFilter));
                        }
                    } 
                    else
                    {   // catch cases like null or "" where the filter string is not invalid but 
                        // also not substantive.  We set value to null so that the assignment 
                        // below picks up null as the new value of _filter.
                        updatedFilter = null; 
                    }

                    _filter = updatedFilter;
                } 
            }
        } 
 
        //   Using 1 as the index of the first filter entry is counterintuitive for C#/C++
        //   developers, but is a side effect of a Win32 feature that allows you to add a template 
        //   filter string that is filled in when the user selects a file for future uses of the dialog.
        //   We don't support that feature, so only values >1 are valid.
        //
        //   For details, see MSDN docs for OPENFILENAME Structure, nFilterIndex 
        /// 
        ///  Gets or sets the index of the filter currently selected in the file dialog box. 
        /// 
        ///  NOTE:  The index of the first filter entry is 1, not 0.
        ///  
        public int FilterIndex
        {
            get
            { 
                return _filterIndex;
            } 
 
            set
            { 
                _filterIndex = value;
            }
        }
 
        /// 
        ///  Gets or sets the initial directory displayed by the file dialog box. 
        ///  
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        /// 
        ///     Critical: Don't want to allow setting of the initial directory in Partial Trust.
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted) 
        /// 
        public string InitialDirectory 
        { 
            get
            { 
                // Avoid returning a null string - return String.Empty instead.
                return _initialDirectory.Value == null ? String.Empty : _initialDirectory.Value;
            }
            [SecurityCritical] 
            set
            { 
                SecurityHelper.DemandUnrestrictedFileIOPermission(); 

                _initialDirectory.Value = value; 
            }
        }

        ///  
        ///  Restores the current directory to its original value if the user
        ///  changed the directory while searching for files. 
        /// 
        ///  This property is only valid for SaveFileDialog;  it has no effect
        ///  when set on an OpenFileDialog. 
        /// 
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: Dialog options are critical for set. 
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted) 
        /// 
        public bool RestoreDirectory 
        {
            get
            {
                return GetOption(NativeMethods.OFN_NOCHANGEDIR); 
            }
            [SecurityCritical] 
            set 
            {
                SecurityHelper.DemandUnrestrictedFileIOPermission(); 

                SetOption(NativeMethods.OFN_NOCHANGEDIR, value);
            }
        } 

        ///  
        ///       Gets or sets a string shown in the title bar of the file dialog. 
        ///       If this property is null, a localized default from the operating
        ///       system itself will be used (typically something like "Save As" or "Open") 
        /// 
        /// 
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: Do not want to allow setting the FileDialog title from a Partial Trust application. 
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted) 
        /// 
        public string Title 
        {
            get
            {
                // Avoid returning a null string - return String.Empty instead. 
                return _title.Value == null ? String.Empty : _title.Value;
            } 
            [SecurityCritical] 
            set
            { 
                SecurityHelper.DemandUnrestrictedFileIOPermission();

                _title.Value = value;
            } 
        }
 
        //   If false, the file dialog boxes will allow invalid characters in the returned file name. 
        //   We are actually responsible for dealing with this flag - it determines whether all of the
        //   processing in ProcessFileNames (which includes things such as the AddExtension feature) 
        //   occurs.
        /// 
        ///  Gets or sets a value indicating whether the dialog box accepts only valid
        ///  Win32 file names. 
        /// 
        ///  
        ///     Callers must have FileIOPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: Dialog options are critical for set. (Only critical for set
        ///             because setting options affects the behavior of the FileDialog)
        ///     PublicOk: Demands FileIOPermission (PermissionState.Unrestricted)
        ///  
        public bool ValidateNames
        { 
            get 
            {
                return !GetOption(NativeMethods.OFN_NOVALIDATE); 
            }
            [SecurityCritical]
            set
            { 
                SecurityHelper.DemandUnrestrictedFileIOPermission();
 
                SetOption(NativeMethods.OFN_NOVALIDATE, !value); 
            }
        } 

        #endregion Public Properties

        //--------------------------------------------------- 
        //
        // Public Events 
        // 
        //----------------------------------------------------
        #region Public Events 

        /// 
        ///  Occurs when the user clicks on the Open or Save button on a file dialog
        ///  box. 
        /// 
        //   We fire this event from DoFileOk. 
        public event CancelEventHandler FileOk; 

        #endregion Public Events 

        //---------------------------------------------------
        //
        // Protected Methods 
        //
        //--------------------------------------------------- 
        #region Protected Methods 

        ///  
        ///  Defines the common dialog box hook procedure that is overridden to add
        ///  specific functionality to the file dialog box.
        /// 
        ///  
        ///  Critical: due to calls to GetParent and PtrToStructure in UnsafeNativeMethods
        ///  as well as a call to DoFileOk, which is SecurityCritical. 
        ///  
        [SecurityCritical]
        protected override IntPtr HookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) 
        {
            // Assume we are successful unless we encounter a problem.
            IntPtr returnValue = IntPtr.Zero;
 
            // Our File Dialogs are Explorer-style dialogs with hook procedure enabled
            // (OFN_ENABLEHOOK | OFN_EXPLORER).  As such, we will get the following 
            // messages:  (as per MSDN) 
            //
            // WM_INITDIALOG 
            // WM_NOTIFY (indicating actions taken by the user or other dialog box events)
            // Messages for any additional controls defined by specifying a child dialog template
            //
            // We only need to handle WM_NOTIFY in this hook procedure. 
            if (msg == NativeMethods.WM_NOTIFY)
            { 
                // Our hookproc is actually the hook procedure for a child template hosted 
                // inside the actual file dialog box.  We want the hwnd of the actual dialog,
                // so we call GetParent on the hwnd passed to the hookproc. 
                _hwndFileDialog = UnsafeNativeMethods.GetParent(new HandleRef(this, hwnd));

                // When we receive WM_NOTIFY, lParam is a pointer to an OFNOTIFY
                // structure that defines the action.  OFNOTIFY is a structure 
                // specific to file open and save dialogs with three members:
                // (defined in Commdlg.h - see MSDN for more details) 
                // 
                // struct _OFNOTIFY {
                //    NMHDR hdr;        // this is a by-value structure; 
                //                      // the implementation in UnsafeNativeMethods breaks it into
                //                    // hdr_hwndFrom (HWND, handle to control sending message),
                //                    // hdr_idFrom (UINT, ID of control sending message) and
                //                    // hdr_code (UINT, one of the CDN_??? notification constants) 
                //
                //    LPOPENFILENAME lpOFN;    // pointer to the OPENFILENAME structure we created in 
                //                    // RunFileDialog when showing this dialog box. 
                //
                //    LPTSTR pszFile;        // if a network sharing violation has occurred, this 
                //                    // is the name of the file affected.  Only valid with
                //                    // hdr_code = CDN_SHAREVIOLATION.
                //    }
                // 
                // Convert the pointer to our OFNOTIFY stored in lparam to an object using PtrToStructure.
                NativeMethods.OFNOTIFY notify = (NativeMethods.OFNOTIFY)UnsafeNativeMethods.PtrToStructure(lParam, typeof(NativeMethods.OFNOTIFY)); 
 
                // WM_NOTIFY indicates that the dialog is sending us a notification message.
                // notify.hdr_code is an int defining which notification is being received. 
                // These codes are integer constants defined originally in commdlg.h.
                switch (notify.hdr_code)
                {
                    case NativeMethods.CDN_INITDONE: 
                        // CDN_INITDONE is sent by Explorer-style file dialogs when the
                        // system has finished arranging the controls in the dialog box. 
                        // 
                        // We use this opportunity to move the dialog box to the center
                        // of the appropriate monitor. 
                        //
                        // As an aside, this only seems to work the first time we show
                        // a dialog - after that, Windows remembers the position of the
                        // dialog.   But that's the Winforms behavior too, so it's fine. 
                        MoveToScreenCenter(new HandleRef(this, _hwndFileDialog),
                                           new HandleRef(this, OwnerWindowHandle)); 
                        break; 

 
                    case NativeMethods.CDN_SELCHANGE:
                        // CDN_SELCHANGE is sent by Explorer-style file dialogs when the
                        // selection changes in the list box that displays the contents
                        // of the currently opened folder or directory. 
                        //
                        // When we get this message, we check to make sure our character 
                        // buffer is big enough to hold all of the filenames that have 
                        // been selected.  If it isn't, we create a new, bigger buffer
                        // and substitute it in the OPENFILENAME structure. 

                        // Retrieve the OPENFILENAME structure from the OFNOTIFY structure
                        // so we can access the CharBuffer inside it.
                        NativeMethods.OPENFILENAME_I ofn = (NativeMethods.OPENFILENAME_I) 
                            UnsafeNativeMethods.PtrToStructure(notify.lpOFN, typeof(NativeMethods.OPENFILENAME_I));
 
 
                        // Get the buffer size required to store the selected file names.
                        // We would like to accomplish this by sending a CDM_GETFILEPATH message 
                        // - to which the file dialog responds with the number of unicode
                        // characters needed to store the file names and paths.
                        //
                        // Windows Forms used CDM_GETSPEC here, but that only retrieves the length 
                        // of the filenames - not of the complete path.  So in cases with network
                        // shortcuts and dereference links enabled, we end up with not enough buffer 
                        // and an FNERR_BUFFERTOOSMALL error. 
                        //
                        // Unfortunately, CDM_GETFILEPATH returns -1 when a bunch of files are 
                        // selected, so changing to it actually makes things worse with very large
                        // cases.  So we'll stick with CDM_GETSPEC plus extra buffer space.
                        //
                        int sizeNeeded = (int)UnsafeNativeMethods.UnsafeSendMessage(_hwndFileDialog,                      // hWnd of window to receive message 
                                                                              NativeMethods.CDM_GETSPEC,                          // Msg (message to send)
                                                                              IntPtr.Zero,                          // wParam (additional info) 
                                                                              IntPtr.Zero);                         // lParam (additional info) 

                        if (sizeNeeded > ofn.nMaxFile) 
                        {
                            // A bigger buffer is required, so we'll allocate a new
                            // CharBuffer and substitute it for the existing one.
 
                            //try
                            //{ 
                                // Make the new buffer equal to the size the dialog told us we needed 
                                // plus a reasonable growth factor.
                                int newBufferSize = sizeNeeded + (FILEBUFSIZE / 4); 

                                // Allocate a new CharBuffer in the appropriate size.
                                CharBuffer charBufferTmp = CharBuffer.CreateBuffer(newBufferSize);
 
                                // Allocate unmanaged memory for the buffer and store the pointer.
                                IntPtr newBuffer = charBufferTmp.AllocCoTaskMem(); 
 
                                // Free the old, smaller buffer stored in ofn.lpstrFile
                                Marshal.FreeCoTaskMem(ofn.lpstrFile); 

                                // Substitute buffer and update the buffer maximum size in
                                // the dialog.
                                ofn.lpstrFile = newBuffer; 
                                ofn.nMaxFile = newBufferSize;
 
                                // Store the reference to the character buffer inside our 
                                // class so we can free it when we're done.
                                this._charBuffer = charBufferTmp; 

                                // Marshal the OPENFILENAME structure back into the
                                // OFNOTIFY structure, then marshal the OFNOTIFY structure
                                // back into lparam to update the dialog. 
                                Marshal.StructureToPtr(ofn, notify.lpOFN, true);
                                Marshal.StructureToPtr(notify, lParam, true); 
                            // } 
                            // Windows Forms had a catch-all exception handler here
                            // but no justification for why it existed.  If exceptions 
                            // are thrown when we grow the buffer, re-add this catch
                            // and perform handling specific to the exception you are seeing.
                            //
                            // I don't see anywhere an exception would be thrown that 
                            // we would want to simply discard in this try block, so
                            // we'll remove this catch and let any exceptions through. 
                            // 
                            // catch (Exception)
                            // { 
                                // intentionally not throwing here.
                            // }
                        }
                        break; 

 
                    case NativeMethods.CDN_SHAREVIOLATION: 
                        // CDN_SHAREVIOLATION is sent by Explorer-style boxes when OK is clicked
                        // and a network sharing violation occurs for the selected file. 
                        // Network sharing violation is a bit misleading of a term - it could
                        // also mean the user doesn't have permissions for the file, or it could mean
                        // the file is already opened by another process on the same machine.
                        // 
                        // We process this message because of some odd behavior seen when a file
                        // is locked for writing.  (for details, see VS Whidbey 95342) 
                        // 
                        // We get this notification followed by *two* CDN_FILEOK notifications... but only
                        // if the path is entered in the textbox and not selected from the folder view. 
                        //
                        // If we get a CDN_SHAREVIOLATION, we'll set a flag and a counter so we can track
                        // which CDN_FILEOK notification we're on to avoid showing two message boxes.
                        this._ignoreSecondFileOkNotification = true;      // We want to ignore the second CDN_FILEOK 
                        this._fileOkNotificationCount = 0;                // to avoid a second prompt by PromptFileOverwrite.
                        break; 
 

                    case NativeMethods.CDN_FILEOK: 
                        // CDN_FILEOK is sent when the user specifies a filename and clicks OK.
                        // We need to process the files selected and make sure everything's acceptable.
                        // If it's all OK, we don't need to do anything.
                        // 
                        // To tell the dialogs to stay open after we receive a CDN_FILEOK, we must both
                        // return a non-zero value from this hook procedure and call SetWindowLong to 
                        // set a nonzero value for DWL_MSGRESULT. 

 
                        // --- Begin VS Whidbey 95342 Workaround ---
                        // See the CDN_SHAREVIOLATION case above for background info about this issue.
                        if (this._ignoreSecondFileOkNotification)
                        { 
                            // We got a CDN_SHAREVIOLATION notification and want to ignore the second CDN_FILEOK notification.
                            // We'll allow the first one through and block the second. 
                            // Recall that we initialize _fileOkNotificationCount to 0 when we get the CDN_SHAREVIOLATION. 
                            if (this._fileOkNotificationCount == 0)
                            { 
                                // This is the first CDN_FILEOK, record that we received
                                // it and then allow DoFileOk to be called.
                                this._fileOkNotificationCount = 1;
                            } 
                            else
                            { 
                                // This is the second CDN_FILEOK, so we want to ignore it. 
                                this._ignoreSecondFileOkNotification = false;
 
                                // Call SetWindowLong to set the DWL_MSGRESULT value of the file dialog window
                                // to a non-zero number to tell the dialog to stay open.
                                // NativeMethods.InvalidIntPtr is defined as -1.
                                UnsafeNativeMethods.CriticalSetWindowLong(new HandleRef(this, hwnd),             // hWnd (which window are we affecting) 
                                                                  NativeMethods.DWL_MSGRESULT,           // nIndex (which value are we setting)
                                                                  NativeMethods.InvalidIntPtr);          // dwNewLong (what is the new value) 
 
                                // We also need to return a non-zero value to tell the dialog to stay open.
                                returnValue = NativeMethods.InvalidIntPtr; 
                                break;
                            }
                        }
                        // --- End VS Whidbey 95342 Workaround --- 

                        // Call DoFileOk to check if the files that have been selected 
                        // are acceptable.  (See DoFileOk for details.) 
                        //
                        // If it returns false, we must notify the dialog box that it 
                        // needs to stay open for further input.
                        if (!DoFileOk(notify.lpOFN))
                        {
                            // Call SetWindowLong to set the DWL_MSGRESULT value of the file dialog window 
                            // to a non-zero number to tell the dialog to stay open.
                            // NativeMethods.InvalidIntPtr is defined as -1. 
                            UnsafeNativeMethods.CriticalSetWindowLong(new HandleRef(this, hwnd),                  // hWnd (which window are we affecting) 
                                                              NativeMethods.DWL_MSGRESULT,                        // nIndex (which value are we setting)
                                                              NativeMethods.InvalidIntPtr);               // dwNewLong (what is the new value) 

                            // We also need to return a non-zero value to tell the dialog to stay open.
                            returnValue = NativeMethods.InvalidIntPtr;
                            break; 
                        }
                        break; 
                } 
            }
 
            // Return IntPtr.Zero to indicate success, unless we have
            // adjusted the return value elsewhere in the function.
            return returnValue;
        } 

        ///  
        /// Raises the System.Windows.FileDialog.FileOk event. 
        /// 
        protected void OnFileOk(CancelEventArgs e) 
        {
            if (FileOk != null)
            {
                FileOk(this, e); 
            }
        } 
 
        //  Because this class, FileDialog, is the parent class for both OpenFileDialog
        //  and SaveFileDialog, this function will perform the common setup tasks 
        //  shared between Open and Save, and will then call RunFileDialog, which is
        //  overridden in both of the derived classes to show the correct dialog.
        //
        ///  
        /// Performs initialization work in preparation for calling RunFileDialog
        /// to show a file open or save dialog box. 
        ///  
        /// 
        ///     Critical: Calls UnsafeNativeMethods.SetWindowPos() accesses SecurityCritical data 
        ///             _charBuffer.
        /// 
        [SecurityCritical]
        protected override bool RunDialog(IntPtr hwndOwner) 
        {
            // Once we run the dialog, all of our communication with it is handled 
            // by processing WM_NOTIFY messages in our hook procedure, this.HookProc. 
            // NativeMethods.WndProc is a delegate with the appropriate signature
            // needed for a Win32 window hook procedure. 
            NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc);

            // Create a new OPENFILENAME structure.  OPENFILENAME is a structure defined
            // in Win32's commdlg.h that contains most of the information needed to 
            // successfully display a file dialog box.
            // NOTE:  Despite the name, OPENFILENAME is the proper structure for both 
            //        file open and file save dialogs. 
            NativeMethods.OPENFILENAME_I ofn = new NativeMethods.OPENFILENAME_I();
 
            // do everything in a try block, so we always free memory in the finalizer
            try
            {
                // Create an appropriately sized buffer to hold the filenames. 
                // The buffer's initial size is controlled by the FILEBUFSIZE constant,
                // an arbitrary value chosen so that we will rarely have to grow the buffer. 
                _charBuffer = CharBuffer.CreateBuffer(FILEBUFSIZE); 

                // If we have a filename stored in our internal array _fileNames, 
                // place it in the buffer as a default filename.
                if (_fileNames != null)
                {
                    _charBuffer.PutString(_fileNames[0]); 
                }
 
                // --- Set up the OPENFILENAME structure --- 

                // lStructSize 
                // Specifies the length, in bytes, of the structure.
                ofn.lStructSize = Marshal.SizeOf(typeof(NativeMethods.OPENFILENAME_I));

                // hwndOwner 
                // Handle to the window that owns the dialog box. This member can be any
                // valid window handle, or it can be NULL if the dialog box has no owner. 
                ofn.hwndOwner = hwndOwner; 

                // hInstance 
                // This property is ignored unless OFN_ENABLETEMPLATEHANDLE or
                // OFN_ENABLETEMPLATE are set.  Since we do not set either,
                // hInstance is ignored, so we can set it to zero.
                ofn.hInstance = IntPtr.Zero; 

                // lpstrFilter 
                // Pointer to a buffer containing pairs of null-terminated filter strings. 
                // The last string in the buffer must be terminated by two NULL characters.
                // Since our filter strings are stored terminated by vertical bar '|' chars, 
                // we call MakeFilterString to reformat and validate the filter string.
                ofn.lpstrFilter = MakeFilterString(_filter, this.DereferenceLinks);

                // nFilterIndex 
                // Specifies the index of the currently selected filter in the File Types
                // control.  Note that since 0 is reserved for a custom filter (which we 
                // do not support), our valid filter indexes begin at 1. 
                ofn.nFilterIndex = _filterIndex;
 
                // lpstrFile
                // Pointer to a buffer used to store filenames.  When initializing the
                // dialog, this name is used as an initial value in the File Name edit
                // control.  When files are selected and the function returns, the buffer 
                // contains the full path to every file selected.
                ofn.lpstrFile = _charBuffer.AllocCoTaskMem(); 
 
                // nMaxFile
                // Size of the lpstrFile buffer in number of Unicode characters. 
                ofn.nMaxFile = _charBuffer.Length;

                // lpstrInitialDir
                // Pointer to a null terminated string that can specify the initial directory. 
                // A relatively complex algorithm is used to determine which directory is
                // actually used as the initial directory - for details, see MSDN for the 
                // OPENFILENAME structure. 
                ofn.lpstrInitialDir = _initialDirectory.Value;
 
                // lpstrTitle
                // Pointer to a string to be placed in the title bar of the dialog box.
                // NULL causes the title bar to display the operating system default string.
                ofn.lpstrTitle = _title.Value; 

                // Flags 
                // A set of bit flags you can use to initialize the dialog box. 
                // Most of these will be set through public properties that then call
                // GetOption or SetOption.  We retrieve the flags using the Options property 
                // and then add three additional flags here:
                //
                //     OFN_EXPLORER
                //         display an Explorer-style box (newer style) 
                //     OFN_ENABLEHOOK
                //         enable the hook procedure (important for much of our functionality) 
                //     OFN_ENABLESIZING 
                //         allow the user to resize the dialog box
                // 
                ofn.Flags = Options | (NativeMethods.OFN_EXPLORER |
                                       NativeMethods.OFN_ENABLEHOOK |
                                       NativeMethods.OFN_ENABLESIZING);
 
                // lpfnHook
                // Pointer to the hook procedure. 
                // Ignored unless OFN_ENABLEHOOK is set in Flags. 
                ofn.lpfnHook = hookProcPtr;
 
                // FlagsEx
                // Can be either zero or OFN_EX_NOPLACESBAR, depending on whether
                // the Places Bar (My Computer/Favorites/etc) should be shown on the
                // left side of the file dialog. 
                ofn.FlagsEx = NativeMethods.OFN_USESHELLITEM;
 
                // lpstrDefExt 
                // Pointer to a buffer that contains the default extension;  it will
                // be appended to filenames if the user does not type an extension. 
                // Only the first three characters are appended by Windows.  If this
                // is NULL, no extension is appended.
                if (_defaultExtension != null && AddExtension)
                { 
                    ofn.lpstrDefExt = _defaultExtension;
                } 
 
                // Call into either OpenFileDialog or SaveFileDialog to show the
                // actual dialog box.  This call blocks until the dialog is closed; 
                // while dialog is open, all interaction is through HookProc.
                return RunFileDialog(ofn);
            }
            finally 
            {
                // Explicitly set the character buffer to null. 
                _charBuffer = null; 

                // If there is still a pointer to a memory location in 
                // ofn.lpstrFile, we explicitly free that memory here.
                if (ofn.lpstrFile != IntPtr.Zero)
                {
                    Marshal.FreeCoTaskMem(ofn.lpstrFile); 
                }
            } 
        } 

        #endregion Protected Methods 

        //---------------------------------------------------
        //
        // Internal Methods 
        //
        //---------------------------------------------------- 
        #region Internal Methods 

        ///  
        ///  Returns the state of the given options flag.
        /// 
        internal bool GetOption(int option)
        { 
            return (_dialogOptions.Value & option) != 0;
        } 
 
        /// 
        ///     Sets the given option to the given boolean value. 
        /// 
        /// 
        ///    Critical: Setting a SecurityCriticalDataForSet member (_dialogOptions).
        ///  
        [SecurityCritical]
        internal void SetOption(int option, bool value) 
        { 
            if (value)
            { 
                // if value is true, bitwise OR the option with _dialogOptions
                _dialogOptions.Value |= option;
            }
            else 
            {
                // if value is false, AND the bitwise complement of the 
                // option with _dialogOptions 
                _dialogOptions.Value &= ~option;
            } 
        }

        /// 
        ///  Prompts the user with a System.Windows.MessageBox 
        ///  with the given parameters. It also ensures that
        ///  the focus is set back on the window that had 
        ///  the focus to begin with (before we displayed 
        ///  the MessageBox).
        /// 
        ///  Returns the choice the user made in the message box
        ///  (true if MessageBoxResult.Yes,
        ///   false if OK or MessageBoxResult.No)
        /// 
        ///  We have to do this instead of just calling MessageBox because
        ///  of an issue where keyboard navigation would fail after showing 
        ///  a message box.  See [....]/default.asp?URL=/Bugs/URT/84016.asp 
        ///  (WinForms ASURT 80262)
        ///  
        /// 
        ///    Critical: We call GetFocus() and SetFocus() in
        ///    UnsafeNativeMethods, which are marked SupressUnmanagedCodeSecurity.
        ///  
        [SecurityCritical]
        internal bool MessageBoxWithFocusRestore(string message, 
                         MessageBoxButton buttons, 
                         MessageBoxImage image)
        { 
            bool ret = false;

            // Get the window that currently has focus and temporarily cache a handle to it
            IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); 

            try 
            { 
                // Show the message box and compare the return value to MessageBoxResult.Yes to get the
                // actual return value. 
                ret = (MessageBox.Show(message, DialogCaption, buttons, image, MessageBoxResult.OK /*default button is OK*/, 0)
                       ==
                       MessageBoxResult.Yes);
            } 
            finally
            { 
                // Return focus to the window that had focus before we showed the messagebox. 
                // SetFocus can handle improper hwnd values, including null.
                UnsafeNativeMethods.SetFocus(new HandleRef(this, focusHandle)); 
            }
            return ret;
        }
 
        /// 
        /// PromptUserIfAppropriate is a virtual function that shows any prompt 
        /// message boxes (like "Do you want to overwrite this file") necessary after 
        ///  the Open button is pressed in a file dialog.
        /// 
        /// Return value is false if we showed a dialog box and true if we did not.
        /// (in other words, true if it's OK to continue with the open process and
        /// false if we need to return the user to the dialog to make another selection.)
        ///  
        /// 
        ///  SaveFileDialog overrides this method to add additional message boxes for 
        ///  its unique properties. 
        ///
        ///  For FileDialog: 
        ///   If OFN_FILEMUSTEXIST is set, we check to be sure the path passed in on the
        ///   fileName parameter exists as an actual file on the hard disk.  If so, we
        ///   call PromptFileNotFound to inform the user that they must select an actual
        ///   file that already exists. 
        /// 
        ///  
        ///     Critical: due to call to PromptFileNotFound, which displays a message box with focus restore. 
        ///             Asserts FileIOPermission in order to determine whether the file exists.
        ///  
        [SecurityCritical]
        internal virtual bool PromptUserIfAppropriate(string fileName)
        {
            bool fileExists = true; 

            // The only option we deal with in this implementation of 
            // PromptUserIfAppropriate is OFN_FILEMUSTEXIST. 
            if (GetOption(NativeMethods.OFN_FILEMUSTEXIST))
            { 
                // File.Exists requires a full path, so we call GetFullPath on
                // the filename before checking if it exists.
                (new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, fileName)).Assert();
                try 
                {
                    string tempPath = Path.GetFullPath(fileName); 
                    fileExists = File.Exists(tempPath); 
                }
                finally 
                {
                    CodeAccessPermission.RevertAssert();
                }
 
                if (!fileExists)
                { 
                    // file does not exist, we can't continue 
                    // and must display an error
                    // Display the message box 
                    PromptFileNotFound(fileName);
                }
            }
            return fileExists; 
        }
 
        ///  
        ///     Implements the actual call to GetOpenFileName or GetSaveFileName.
        ///  
        internal abstract bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn);

        #endregion Internal Methods
 
        //---------------------------------------------------
        // 
        // Internal Properties 
        //
        //---------------------------------------------------- 
        #region Internal Properties
        /// 
        ///  In cases where we need to return an array of strings, we return
        ///  a clone of the array.  We also need to make sure we return a 
        ///  string[0] instead of a null if we don't have any filenames.
        ///  
        ///  
        ///     Critical:  Accesses _fileNames, which is SecurityCritical.
        ///  
        internal string[] FileNamesInternal
        {
            [SecurityCritical]
            get 
            {
                if (_fileNames == null) 
                { 
                    return new string[0];
                } 
                else
                {
                    return (string[])_fileNames.Clone();
                } 
            }
        } 
 

        #endregion Internal Properties 

        //----------------------------------------------------
        //
        // Internal Events 
        //
        //--------------------------------------------------- 
        //#region Internal Events 
        //#endregion Internal Events
 
        //----------------------------------------------------
        //
        // Private Methods
        // 
        //---------------------------------------------------
        #region Private Methods 
 
        /// 
        ///     Processes the CDN_FILEOK notification, which is sent by an 
        ///     Explorer-style Open or Save As dialog box when the user specifies
        ///     a file name and clicks the OK button.
        /// 
        ///  
        /// true if the dialog can close, or false if we need to return to
        /// the dialog for additional input. 
        ///  
        /// 
        ///     Critical due to call access to _charBuffer, _fileNames and _dialogOptions. 
        /// 
        [SecurityCritical]
        private bool DoFileOk(IntPtr lpOFN)
        { 
            NativeMethods.OPENFILENAME_I ofn = (NativeMethods.OPENFILENAME_I)UnsafeNativeMethods.PtrToStructure(lpOFN, typeof(NativeMethods.OPENFILENAME_I));
 
            // While processing the results we get from the OPENFILENAME struct, 
            // we will adjust several properties of our own class to reflect the
            // new data.  In case we discover we need to send the user back to 
            // the dialog for further input, we need to be able to revert these
            // changes - so we backup _dialogOptions, _filterIndex and _fileNames.
            //
            // We only assign brand new string arrays to _FileNames, so it's OK 
            // to back up by reference here.
            int saveOptions = _dialogOptions.Value; 
            int saveFilterIndex = _filterIndex; 
            string[] saveFileNames = _fileNames;
 
            // ok is a flag to determine whether we need to show the dialog
            // again (false) or if we're satisfied with the results we received (true).
            bool ok = false;
 
            try
            { 
                // Replace the ReadOnly flag in DialogOptions with the ReadOnly flag 
                // from the OPENFILEDIALOG structure - that is, store the user's
                // choice from the Read Only checkbox so our property is up to date. 
                _dialogOptions.Value = _dialogOptions.Value & ~NativeMethods.OFN_READONLY |
                                 ofn.Flags & NativeMethods.OFN_READONLY;

                // Similarly, update the filterIndex to reflect the selected filter. 
                _filterIndex = ofn.nFilterIndex;
 
                // Ask the character buffer to copy the memory from the location 
                // referenced by lpstrFile into our internal character buffer.
                _charBuffer.PutCoTaskMem(ofn.lpstrFile); 

                if (!GetOption(NativeMethods.OFN_ALLOWMULTISELECT))
                {
                    // Since we're selecting a single file, make a string 
                    // array with a single element containing the entire contents
                    // of the character buffer. 
                    _fileNames = new string[] { _charBuffer.GetString() }; 
                }
                else 
                {
                    // Multiselect is a bit more complex - call GetMultiselectFiles
                    // to handle that case.
                    _fileNames = GetMultiselectFiles(_charBuffer); 
                }
 
                // Call ProcessFileNames() to do validation and post-processing 
                // tasks (see that function for details;  it checks if files exist,
                // prompts users with message boxes if invalid selections are made, etc.) 
                if (ProcessFileNames())
                {
                    // ProcessFileNames returned true, so it's OK to fire the
                    // OnFileOk event. 
                    CancelEventArgs ceevent = new CancelEventArgs();
                        OnFileOk(ceevent); 
 
                        // We allow our calling code to do even more post-processing
                        // through the OnFileOk event - and therefore offer them the 
                        // opportunity to redisplay the dialog for additional input
                        // using the event arguments if their validation failed.
                        //
                        // If OnFileOk is not handled, ceevent.Cancel will be false. 
                        ok = !ceevent.Cancel;
                } 
            } 
            finally
            { 
                // No matter what happened, we need to restore dialog state
                // if the result was not ok=true.
                if (!ok)
                { 
                    _dialogOptions.Value = saveOptions;
                    _filterIndex = saveFilterIndex; 
                    _fileNames = saveFileNames; 
                }
            } 
            return ok;
        }

        ///  
        ///     Extracts the filename(s) returned by the file dialog.
        ///  
        ///  Marked static for perf reasons because this function doesn't 
        ///  actually access any instance data as per FxCop CA1822.
        private static string[] GetMultiselectFiles(CharBuffer charBuffer) 
        {
            // Iff OFN_ALLOWMULTISELECT is set for an Explorer-style dialog box
            // and the user selects multiple files, lpstrFile points to a string
            // containing the current directory, followed by a NULL, followed by 
            // two or more filenames that are NULL separated, with an extra NULL
            // character after the last filename. 
            // 
            // We'll use the GetString() function of the character buffer to get
            // two of these null-terminated chunks at a time, one into directory 
            // and one into filename.
            string directory = charBuffer.GetString();
            string fileName = charBuffer.GetString();
 
            // If OFN_ALLOWMULTISELECT is enabled but the user selects only
            // one file, we get the filename and path concatenated together without 
            // a null separator.  This will cause our directory variable to 
            // contain the full path and fileName to be empty, so make a new
            // string array with the contents of directory as its single element. 
            //
            if (fileName.Length == 0)
            {
                return new string[] { directory }; 
            }
 
            // If the directory was provided without a directory separator 
            // character (typically '\' on Windows) at the end, we add it.
            if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) 
            {
                directory = directory + Path.DirectorySeparatorChar;
            }
 
            // Create a generic list of strings to hold the names.
            List names = new List(); 
 
            do
            { 
                // With DereferenceLinks enabled, we can sometimes end
                // up with full paths provided as filenames.  We need
                // to check for two cases here - the case where the
                // filename begins with '\', indicating a UNC share path, 
                // or the case where we have a full hard disk path
                // (e.g. C:\file.txt), where [1] will be the : volume 
                // separator and [2] will be the \ directory separator. 

                bool isUncPath = (fileName[0] == Path.DirectorySeparatorChar && fileName[1] == Path.DirectorySeparatorChar); 

                bool isFullPath = (fileName.Length > 3 &&
                                   fileName[1] == Path.VolumeSeparatorChar &&
                                   fileName[2] == Path.DirectorySeparatorChar ); 

                if (!(isUncPath || isFullPath)) 
                { 
                    // filename is not a full path, so we need to
                    // add on the directory 
                    fileName = directory + fileName;
                }

                names.Add(fileName); 

                // Get the next filename 
                fileName = charBuffer.GetString(); 

            } while (!String.IsNullOrEmpty(fileName)); 

            return names.ToArray();
        }
 
        //  Provides the actual implementation of initialization tasks.
        //  Initialize() is called from both the constructor and the 
        //  public Reset() function to set default values for member 
        //  variables and for the options bitmask.
        ///  
        ///     Critical: Sets Dialog options, which are critical for set.
        /// 
        [SecurityCritical]
        private void Initialize() 
        {
            // 
            // Initialize Options Flags 
            //
            _dialogOptions.Value = 0;   // _dialogOptions is an int containing a set of 
                                        // bit flags used to initialize the dialog box.
                                        // It is placed directly into the OPENFILEDIALOG
                                        // struct used to instantiate the file dialog box.
                                        // Within our code, we only use GetOption and SetOption 
                                        // (change from Windows Forms, which sometimes directly
                                        // modified _dialogOptions).  As such, we initialize to 0 
                                        // here and then call SetOption to get _dialogOptions 
                                        // into the default state.
 
            //
            // Set some default options
            //
            // - Hide the Read Only check box. 
            SetOption(NativeMethods.OFN_HIDEREADONLY, true);
 
            // - Specifies that the user can type only valid paths and file names. If this flag is 
            //   used and the user types an invalid path and file name in the File Name entry field,
            //   we will display a warning in a message box. 
            SetOption(NativeMethods.OFN_PATHMUSTEXIST, true);

            // - This is our own flag, not a standard one defined in OPENFILEDIALOG.  We use this to
            //   indicate to ourselves that we should add the default extension automatically if the 
            //   user does not enter it in themselves in ProcessFileNames.  (See that function for
            //   details.) 
            SetOption(OPTION_ADDEXTENSION, true); 

 
            //
            // Initialize additional properties
            //
            _title.Value = null; 
            _initialDirectory.Value = null;
            _defaultExtension = null; 
            _fileNames = null; 
            _filter = null;
            _filterIndex = 1;        // The index of the first filter entry is 1, not 0. 
                                     // 0 is reserved for the custom filter functionality
                                     // provided by Windows, which we do not expose to the user.

            // Variables used for bug workaround: 
            // When the selected file is locked for writing, we get a sharing violation notification
            // followed by *two* CDN_FILEOK notifications.  These flags are used to track the multiple 
            // notifications so we only show one error message box to the user. 
            // For a more complete explanation and PS bug information, see HookProc.
            _ignoreSecondFileOkNotification = false; 
            _fileOkNotificationCount = 0;
        }

        ///  
        ///     Converts the given filter string to the format required in an OPENFILENAME_I
        ///     structure. 
        ///  
        private static string MakeFilterString(string s, bool dereferenceLinks)
        { 
            if (String.IsNullOrEmpty(s))
            {
                // Workaround for VSWhidbey bug #95338 (carried over from Winforms implementation)
                // Apparently, when filter is null, the common dialogs in Windows XP will not dereference 
                // links properly.  The work around is to provide a default filter;  " |*.*" is used to
                // avoid localization issues from description text. 
                // 
                // This behavior is now documented in MSDN on the OPENFILENAME structure, so I don't
                // expect it to change anytime soon. 
                if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5)
                {
                    s = " |*.*";
                } 
                else
                { 
                    // Even if we don't need the bug workaround, change empty 
                    // strings into null strings.
                    return null; 
                }
            }

            StringBuilder nullSeparatedFilter = new StringBuilder(s); 

            // Replace the vertical bar with a null to conform to the Windows 
            // filter string format requirements 
            nullSeparatedFilter.Replace('|', '\0');
 
            // Append two nulls at the end
            nullSeparatedFilter.Append('\0');
            nullSeparatedFilter.Append('\0');
 
            // Return the results as a string.
            return nullSeparatedFilter.ToString(); 
        } 

        ///  
        /// Handle the AddExtension property on newly acquired filenames, then
        /// call PromptUserIfAppropriate to display any necessary message boxes.
        ///
        /// Returns false if we need to redisplay the dialog and true otherwise. 
        /// 
        ///  
        /// Critical:   due to call to PromptUserIfAppropriate, which displays 
        ///             message boxes with focus restore.
        /// TreatAsSafe: This method does not take external input for the call for 
        ///             PromptUserIfAppropriate.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private bool ProcessFileNames() 
        {
            // Only process the filenames if OFN_NOVALIDATE is not set. 
            if (!GetOption(NativeMethods.OFN_NOVALIDATE)) 
            {
                // Call the FilterExtensions private property to get 
                // a list of valid extensions from the filter(s).
                // The first extension from FilterExtensions is the
                // default extension.
                string[] extensions = GetFilterExtensions(); 

                // For each filename: 
                //      -  Process AddExtension 
                //      -  Call PromptUserIfAppropriate to display necessary dialog boxes.
                for (int i = 0; i < _fileNames.Length; i++) 
                {
                    string fileName = _fileNames[i];

                    // If AddExtension is enabled and we do not already have an extension: 
                    if (AddExtension && !Path.HasExtension(fileName))
                    { 
                        // Loop through all extensions, starting with the default extension 
                        for (int j = 0; j < extensions.Length; j++)
                        { 
                            // Assert for a valid extension
                            Invariant.Assert(!extensions[j].StartsWith(".", StringComparison.Ordinal),
                                        "FileDialog.GetFilterExtensions should not return things starting with '.'");
 
                            string currentExtension = Path.GetExtension(fileName);
 
                            // Assert to make sure Path.GetExtension behaves as we think it should, returning 
                            // "" if the string is empty and something beginnign with . otherwise.
                            // Use StringComparison.Ordinal as per FxCop CA1307 and CA130. 
                            Invariant.Assert(currentExtension.Length == 0 || currentExtension.StartsWith(".", StringComparison.Ordinal),
                                         "Path.GetExtension should return something that starts with '.'");

                            // Because we check Path.HasExtension above, files should 
                            // theoretically not have extensions at this stage - but
                            // we'll go ahead and remove an existing extension if it 
                            // somehow slipped through. 
                            //
                            // Strip out any extension that may be remaining and place the rest 
                            // of the filename in s.
                            //
                            // Changed to use StringBuilder for perf reasons as per FxCop CA1818
                            StringBuilder s = new StringBuilder(fileName.Substring(0, fileName.Length - currentExtension.Length)); 
                            // we don't want to append the extension if it contains wild cards
                            if (extensions[j].IndexOfAny(new char[] { '*', '?' }) == -1) 
                            { 
                                // No wildcards, so go ahead and append
                                s.Append("."); 
                                s.Append(extensions[j]);
                            }

                            // If OFN_FILEMUSTEXIST is not set, or if it is set but the filename we generated 
                            // does in fact exist, we update fileName and stop trying new extensions.
                            if (!GetOption(NativeMethods.OFN_FILEMUSTEXIST) || File.Exists(s.ToString())) 
                            { 
                                fileName = s.ToString();
                                break; 
                            }
                        }
                        // Store this filename back in the _fileNames array.
                        _fileNames[i] = fileName; 
                    }
 
                    // Call PromptUserIfAppropriate to show necessary dialog boxes. 
                    if (!PromptUserIfAppropriate(fileName))
                    { 
                        // We don't want to display a bunch of message boxes
                        // if one has already determined we need to return to
                        // the file dialog, so we will return false to short
                        // circuit additional processing. 
                        return false;
                    } 
                } 
            }
            return true; 
        }

        /// 
        /// Prompts the user with a System.Windows.MessageBox 
        /// when a file does not exist.
        ///  
        ///  
        /// Security Critical due to a call to MessageBoxWithFocusRestore.
        ///  
        [SecurityCritical]
        private void PromptFileNotFound(string fileName)
        {
            MessageBoxWithFocusRestore(SR.Get(SRID.FileDialogFileNotFound, fileName), 
                    System.Windows.MessageBoxButton.OK, MessageBoxImage.Warning);
        } 
 
        #endregion Private Methods
 
        //---------------------------------------------------
        //
        // Private Properties
        // 
        //---------------------------------------------------
        #region Private Properties 
 
        //   If multiple files are selected, we only return the first filename.
        ///  
        ///  Gets a string containing the full path of the file selected in
        ///  the file dialog box.
        /// 
        ///  
        ///     Critical: Do not want to allow access to raw paths to Parially Trusted Applications.
        ///  
        private string CriticalFileName 
        {
            [SecurityCritical] 
            get
            {

                if (_fileNames == null)        // No filename stored internally... 
                {
                    return String.Empty;    // So we return String.Empty 
                } 
                else
                { 
                    // Return the first filename in the array if it is non-empty.
                    if (_fileNames[0].Length > 0)
                    {
                        return _fileNames[0]; 
                    }
                    else 
                    { 
                        return String.Empty;
                    } 
                }
            }
        }
        ///  
        ///  Gets a string containing the title of the file dialog.
        ///  
        ///  
        ///  Critical: due to calls to GetWindowTextLength and GetWindowText.
        ///  
        //   When showing message boxes onscreen, we want them to have the
        //   same title bar as the file open or save dialog itself.  We can't
        //   just use the Title property, because if it's null the operating
        //   system substitutes a standard localized title. 
        //
        //   The solution is this private property, which returns the title of the 
        //   file dialog (using the stored handle of the dialog _hwndFileDialog to 
        //   call GetWindowText).
        // 
        //   It is designed to only be called by MessageBoxWithFocusRestore.
        private string DialogCaption
        {
            [SecurityCritical] 
            get
            { 
                if (!UnsafeNativeMethods.IsWindow(new HandleRef(this, _hwndFileDialog))) 
                {
                    return String.Empty; 
                }

                // Determine the length of the text we want to retrieve...
                int textLen = UnsafeNativeMethods.GetWindowTextLength(new HandleRef(this, _hwndFileDialog)); 
                // then make a StringBuilder...
                StringBuilder sb = new StringBuilder(textLen + 1); 
                // and call GetWindowText to fill it up... 
                UnsafeNativeMethods.GetWindowText(new HandleRef(this, _hwndFileDialog),
                           sb /*target string*/, 
                           sb.Capacity /* max # of chars to copy before truncation occurs */
                           );
                // then return the results.
                return sb.ToString(); 
            }
        } 
 
        /// 
        /// Extracts the file extensions specified by the current file filter into 
        /// an array of strings.  None of the extensions contain .'s, and the
        /// default extension is first.
        /// 
        ///  
        /// Thrown if the filter string stored in the dialog is invalid.
        ///  
        private string[] GetFilterExtensions() 
        {
            string filter = this._filter; 
            List extensions = new List();

            // Always make the default extension the first in the list,
            // because other functions process files in order accepting the first 
            // valid extension they find.  It's a little strange if DefaultExt
            // is not in the filters list, but I guess it's legal. 
            if (_defaultExtension != null) 
            {
                extensions.Add(_defaultExtension); 
            }

            // If we have filters, extract the extensions from the currently selected
            // filter and add them to the extensions list. 
            if (filter != null)
            { 
                // Filter strings are '|' delimited, so we split on them 
                string[] tokens = filter.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
 
                // Calculate the index of the token containing extension(s) selected
                // by the FilterIndex property.  Remember FilterIndex is one based.
                // Multiply by 2 because each filter consists of 2 strings.
                // Now subtract one to get to the filter component. 
                //
                // example:  Text|*.txt|Pictures|*.jpg|Web Pages|*.htm 
                // tokens[]:   0    1       2      3      4        5 
                // FilterIndex = 2 selects Pictures;  (2*2)-1 = 3 points to *.jpg in tokens
                // 
                int indexOfExtension = (_filterIndex * 2) - 1;

                // Check to be sure our filter index is not out of bounds (that is,
                // greater than the number of filters we actually have). 
                // We multiply by 2 here because each filter consists of two strings,
                // description and extensions, both separated by | characters.. so 
                // tokens.length is actually twice the number of filters we have. 
                if (indexOfExtension >= tokens.Length)
                { 
                    throw new InvalidOperationException(SR.Get(SRID.FileDialogInvalidFilterIndex));
                }

                // If our filter index is valid (0 is reserved by Windows for custom 
                // filter functionality we don't expose, so filters must be 1 or greater)
                if (_filterIndex > 0) 
                { 
                    // Find our filter in the tokens list, then split it on the
                    // ';' character (which is the filter extension delimiter) 
                    string[] exts = tokens[indexOfExtension].Split(';');

                    foreach (string ext in exts)
                    { 
                        // Filter extensions should be in the form *.txt or .txt,
                        // so we strip out everything before and including the '.' 
                        // before adding the extension to our list. 
                        // If the extension has no '.', we just ignore it as invalid.
                        int i = ext.LastIndexOf('.'); 

                        if (i >= 0)
                        {
                            // start the substring one beyond the location of the '.' 
                            // (i) and continue to the end of the string
                            extensions.Add(ext.Substring(i + 1, ext.Length - (i + 1))); 
                        } 
                    }
                } 
            }

            return extensions.ToArray();
        } 

        ///  
        ///  Gets an integer representing the Win32 common Open File Dialog OFN_* option flags 
        ///  used to display a dialog with the current set of property values.
        ///  
        //
        //   We bitwise AND _dialogOptions with all of the options we consider valid
        //   before returning the resulting bitmask to avoid accidentally setting a
        //   flag we don't intend to.  Note that this list doesn't include a few of the 
        //   flags we set right before showing the dialog in RunDialog (like
        //   NativeMethods.OFN_EXPLORER), since those are only added when creating 
        //   the OPENFILENAME structure. 
        //
        //   Also note that our private flags are not included in this list (like 
        //   OPTION_ADDEXTENSION)
        protected int Options
        {
            get 
            {
                return _dialogOptions.Value & (NativeMethods.OFN_READONLY | NativeMethods.OFN_HIDEREADONLY | 
                                  NativeMethods.OFN_NOCHANGEDIR | NativeMethods.OFN_NOVALIDATE | 
                                  NativeMethods.OFN_ALLOWMULTISELECT | NativeMethods.OFN_PATHMUSTEXIST |
                                  NativeMethods.OFN_NODEREFERENCELINKS); 
            }
        }

        #endregion Private Properties 

        //---------------------------------------------------- 
        // 
        // Private Fields
        // 
        //---------------------------------------------------
        #region Private Fields

        // _dialogOptions is a set of bit flags used to control the behavior 
        // of the Win32 dialog box.
        private SecurityCriticalDataForSet _dialogOptions; 
 
        // These two flags are related to a fix for an issue where Windows
        // sends two FileOK notifications back to back after a sharing 
        // violation occurs.  See CDN_SHAREVIOLATION in HookProc for details.
        private bool _ignoreSecondFileOkNotification;
        private int _fileOkNotificationCount;
 
        // These private variables store data for the various public properties
        // that control the appearance of the file dialog box. 
        private SecurityCriticalDataForSet _title;                  // Title bar of the message box 
        private SecurityCriticalDataForSet _initialDirectory;       // Starting directory
        private string _defaultExtension;       // Extension appended first if AddExtension 
                                                // is enabled
        private string _filter;                 // The file extension filters that display
                                                // in the "Files of Type" box in the dialog
        private int _filterIndex;               // The index of the currently selected 
                                                // filter (a default filter index before
                                                // the dialog is called, and the filter 
                                                // the user selected afterwards.)  This 
                                                // index is 1-based, not 0-based.
 
        // Since we have to interop with native code to show the file dialogs,
        // we use the CharBuffer class to help with the marshalling of
        // unmanaged memory that stores the user-selected file names.
        ///  
        ///     Critical: This is a buffer that is operated on by unmanaged
        ///  
        [SecurityCritical] 
        private CharBuffer _charBuffer;
 
        // We store the handle of the file dialog inside our class
        // for a variety of purposes (like getting the title of the dialog
        // box when we need to show a message box with the same title bar caption)
        ///  
        ///     Critical: The hWnd of the dialog is critical data.
        ///  
        [SecurityCritical] 
        private IntPtr _hwndFileDialog;
 
        // This is the array that stores the filename(s) the user selected in the
        // dialog box.  If Multiselect is not enabled, only the first element
        // of this array will be used.
        ///  
        ///     Critical: The full file paths are critical data.
        ///  
        [SecurityCritical] 
        private string[] _fileNames;
 
        // Constant to control the initial size of the character buffer;
        // 8192 is an arbitrary but reasonable size that should minimize the
        // number of times we need to grow the buffer.
        private const int FILEBUFSIZE = 8192; 

        // OPTION_ADDEXTENSION is our own bit flag that we use to control our 
        // own automatic extension appending feature. 
        private const int OPTION_ADDEXTENSION = unchecked(unchecked((int)0x80000000));
 
        #endregion Private Fields

    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK