File: winforms\Managed\System\WinForms\FileDialog.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="FileDialog.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
    using System.Text;
    using System.Threading;
    using System.Runtime.Remoting;
    using System.Runtime.InteropServices;
 
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
 
    using System;
    using System.Security.Permissions;
    using System.Drawing;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.IO;
    using ArrayList = System.Collections.ArrayList;
 
    using Encoding = System.Text.Encoding;
    using Microsoft.Win32;
    using System.Security;
    using System.Runtime.Versioning;
 
    using CharBuffer = System.Windows.Forms.UnsafeNativeMethods.CharBuffer;
 
    /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Displays a dialog window from which the user can select a file.
    ///    </para>
    /// </devdoc>
    [
    DefaultEvent("FileOk"),
    DefaultProperty("FileName")
    ]
    public abstract partial class FileDialog : CommonDialog {
 
        private const int FILEBUFSIZE = 8192;
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.EventFileOk"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        /// <internalonly/>
        protected static readonly object EventFileOk = new object();
 
        internal const int OPTION_ADDEXTENSION = unchecked(unchecked((int)0x80000000));
 
        internal int options;
 
        private string title;
        private string initialDir;
        private string defaultExt;
        private string[] fileNames;
        private bool securityCheckFileNames;
        private string filter;
        private int filterIndex;
        private bool supportMultiDottedExtensions;
        private bool ignoreSecondFileOkNotification;  // Used for VS Whidbey 95342
        private int okNotificationCount;              // Same
        private CharBuffer charBuffer;
        private IntPtr dialogHWnd;
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.FileDialog"]/*' />
        /// <devdoc>
        ///    <para>
        ///       In an inherited class,
        ///       initializes a new instance of the <see cref='System.Windows.Forms.FileDialog'/>
        ///       class.
        ///    </para>
        /// </devdoc>
        [
            SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")  // If the constructor does not call Reset
                                                                                                    // it would be a breaking change.
        ]
        internal FileDialog() {
            Reset();
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.AddExtension"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the
        ///       dialog box automatically adds an extension to a
        ///       file name if the user omits the extension.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(true),
        SRDescription(SR.FDaddExtensionDescr)
        ]
        public bool AddExtension {
            get {
                return GetOption(OPTION_ADDEXTENSION);
            }
 
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
                SetOption(OPTION_ADDEXTENSION, value);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.CheckFileExists"]/*' />
        /// <devdoc>
        ///    <para>Gets or sets a value indicating whether
        ///       the dialog box displays a warning if the user specifies a file name that does not exist.</para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(false),
        SRDescription(SR.FDcheckFileExistsDescr)
        ]
        public virtual bool CheckFileExists {
            get {
                return GetOption(NativeMethods.OFN_FILEMUSTEXIST);
            }
 
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
                SetOption(NativeMethods.OFN_FILEMUSTEXIST, value);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.CheckPathExists"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the
        ///       dialog box displays a warning if the user specifies a path that does not exist.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(true),
        SRDescription(SR.FDcheckPathExistsDescr)
        ]
        public bool CheckPathExists {
            get {
                return GetOption(NativeMethods.OFN_PATHMUSTEXIST);
            }
 
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
                SetOption(NativeMethods.OFN_PATHMUSTEXIST, value);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.DefaultExt"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the default file extension.
        ///       
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(""),
        SRDescription(SR.FDdefaultExtDescr)
        ]
        public string DefaultExt {
            get {
                return defaultExt == null? "": defaultExt;
            }
 
            set {
                if (value != null) {
                    if (value.StartsWith("."))
                        value = value.Substring(1);
                    else if (value.Length == 0)
                        value = null;
                }
                defaultExt = value;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.DereferenceLinks"]/*' />
        /// <devdoc>
        ///    <para>
        ///       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).
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(true),
        SRDescription(SR.FDdereferenceLinksDescr)
        ]
        public bool DereferenceLinks {
            get { 
                return !GetOption(NativeMethods.OFN_NODEREFERENCELINKS);
            }
            set { 
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
                SetOption(NativeMethods.OFN_NODEREFERENCELINKS, !value);
            }
        }
 
        internal string DialogCaption {
            get {
                int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, dialogHWnd));
                StringBuilder sb = new StringBuilder(textLen+1);
                UnsafeNativeMethods.GetWindowText(new HandleRef(this, dialogHWnd), sb, sb.Capacity);
                return sb.ToString();
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.FileName"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets a string containing
        ///       the file name selected in the file dialog box.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatData), 
        DefaultValue(""),
        SRDescription(SR.FDfileNameDescr)
        ]
        public string FileName {
            get {
                if (fileNames == null) {
                    return "";
                }
                else {
                    if (fileNames[0].Length > 0) {
 
                        // See if we need to perform a security check on file names.  We need
                        // to do this if the set of file names was provided by the file dialog.
                        // A developer can set file names through the FileDialog API as well,
                        // but we don't need to check those since the developer can provide any
                        // name s/he wants.  This is important because it is otherwise possible
                        // to get the FileName property to accept garbage, but throw during get.
                        
                        if (securityCheckFileNames) {
                            Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + fileNames[0] + ") Demanded");
                            IntSecurity.DemandFileIO(FileIOPermissionAccess.AllAccess, fileNames[0]);
                        }
 
                        return fileNames[0];
                    }
                    else {
                        return "";
                    }
                }
            }
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
                if (value == null) {
                    fileNames = null;
                }
                else {
                    fileNames = new string[] {value};
                }
 
                // As the developer has called this API and set the file name with an arbitrary value,
                // we do not need to perform a security check on the name.
                securityCheckFileNames = false;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.FileNames"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the file
        ///       names of all selected files in the dialog box.
        ///    </para>
        /// </devdoc>
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        SRDescription(SR.FDFileNamesDescr)
        ]
        public string[] FileNames {
            get{
                string[] files = FileNamesInternal;
 
                // See if we need to perform a security check on file names.  We need
                // to do this if the set of file names was provided by the file dialog.
                // A developer can set file names through the FileDialog API as well,
                // but we don't need to check those since the developer can provide any
                // name s/he wants.  This is important because it is otherwise possible
                // to get the FileName property to accept garbage, but throw during get.
 
                if (securityCheckFileNames) {
                    foreach (string file in files) {
                        Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + file + ") Demanded");
                        IntSecurity.DemandFileIO(FileIOPermissionAccess.AllAccess, file);
                    }
                }    
                return files;
            }
        }
 
        internal string[] FileNamesInternal {
            get {
 
                if (fileNames == null) {
                    return new string[0];
                }
                else {
                    return(string[])fileNames.Clone();
                }
            }
        }
 
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.Filter"]/*' />
        /// <devdoc>
        ///    <para>
        ///       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 in the dialog box.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(""),
        Localizable(true),
        SRDescription(SR.FDfilterDescr)
        ]
        public string Filter {
            get {
                return filter == null? "": filter;
            }
 
            set {
                if (value != filter) {
                    if (value != null && value.Length > 0) {
                        string[] formats = value.Split('|');
                        if (formats == null || formats.Length % 2 != 0) {
                            throw new ArgumentException(SR.GetString(SR.FileDialogInvalidFilter));
                        }
                    }
                    else {
                        value = null;
                    }
                    filter = value;
                }
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.FilterExtensions"]/*' />
        /// <devdoc>
        ///     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.
        /// </devdoc>
        private string[] FilterExtensions {
            get {
                string filter = this.filter;
                ArrayList extensions = new ArrayList();
                
                // First extension is the default one.  It's a little strange if DefaultExt
                // is not in the filters list, but I guess it's legal.
                if (defaultExt != null) 
                    extensions.Add(defaultExt);
 
                if (filter != null) {
                    string[] tokens = filter.Split('|');
                    
                    if ((filterIndex * 2) - 1 >= tokens.Length) {
                        throw new InvalidOperationException(SR.GetString(SR.FileDialogInvalidFilterIndex));
                    }
                    
                    if (filterIndex > 0) {
                        string[] exts = tokens[(filterIndex * 2) - 1].Split(';');
                        foreach (string ext in exts) {
                            int i = this.supportMultiDottedExtensions ? ext.IndexOf('.') : ext.LastIndexOf('.');
                            if (i >= 0) {
                                extensions.Add(ext.Substring(i + 1, ext.Length - (i + 1)));
                            }
                        }
                    }
                }
                string[] temp = new string[extensions.Count];
                extensions.CopyTo(temp, 0);
                return temp;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.FilterIndex"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the index of the filter currently selected in the file dialog box.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(1),
        SRDescription(SR.FDfilterIndexDescr)
        ]
        public int FilterIndex {
            get {
                return filterIndex;
            }
 
            set {
                filterIndex = value;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.InitialDirectory"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the initial directory displayed by the file dialog
        ///       box.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatData), 
        DefaultValue(""),
        SRDescription(SR.FDinitialDirDescr)
        ]
        public string InitialDirectory {
            get {
                return initialDir == null? "": initialDir;
            }
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
 
                initialDir = value;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.Instance"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets the Win32 instance handle for the application.
        ///    </para>
        /// </devdoc>
        /* SECURITYUNDONE : should require EventQueue permission */
        protected virtual IntPtr Instance {
            [
                SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode),
                SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)
            ]
            [ResourceExposure(ResourceScope.Process)]
            [ResourceConsumption(ResourceScope.Process)]
            get { return UnsafeNativeMethods.GetModuleHandle(null); }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.Options"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Gets the Win32 common Open File Dialog OFN_* option flags.
        ///    </para>
        /// </devdoc>
        protected int Options {
            get {
                return options & (NativeMethods.OFN_READONLY | NativeMethods.OFN_HIDEREADONLY |
                                  NativeMethods.OFN_NOCHANGEDIR | NativeMethods.OFN_SHOWHELP | NativeMethods.OFN_NOVALIDATE |
                                  NativeMethods.OFN_ALLOWMULTISELECT | NativeMethods.OFN_PATHMUSTEXIST |
                                  NativeMethods.OFN_NODEREFERENCELINKS);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.RestoreDirectory"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the dialog box restores the current directory before
        ///       closing.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(false),
        SRDescription(SR.FDrestoreDirectoryDescr)
        ]
        public bool RestoreDirectory {
            get {
                return GetOption(NativeMethods.OFN_NOCHANGEDIR);
            }
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
 
                SetOption(NativeMethods.OFN_NOCHANGEDIR, value);
            }
        }
 
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.ShowHelp"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating
        ///       whether whether the Help button is displayed in the file dialog.
        ///       
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(false),
        SRDescription(SR.FDshowHelpDescr)
        ]
        public bool ShowHelp {
            get {
                return GetOption(NativeMethods.OFN_SHOWHELP);
            }
            set {
                SetOption(NativeMethods.OFN_SHOWHELP, value);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.SupportMultipleExtensions"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets whether def or abc.def is the extension of the file filename.abc.def
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(false),
        SRDescription(SR.FDsupportMultiDottedExtensionsDescr)
        ]
        public bool SupportMultiDottedExtensions
        {
            get
            {
                return this.supportMultiDottedExtensions;
            }
            set
            {
                this.supportMultiDottedExtensions = value;
            }
        } 
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.Title"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the file dialog box title.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatAppearance), 
        DefaultValue(""),
        Localizable(true),
        SRDescription(SR.FDtitleDescr)
        ]
        public string Title {
            get {
                return title == null? "": title;
            }
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
 
                title = value;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.ValidateNames"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the dialog box accepts only valid
        ///       Win32 file names.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior), 
        DefaultValue(true),
        SRDescription(SR.FDvalidateNamesDescr)
        ]
        public bool ValidateNames {
            get {
                return !GetOption(NativeMethods.OFN_NOVALIDATE);
            }
            set {
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogCustomization Demanded");
                IntSecurity.FileDialogCustomization.Demand();
 
                SetOption(NativeMethods.OFN_NOVALIDATE, !value);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.FileOk"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Occurs when the user clicks on the Open or Save button on a file dialog
        ///       box.
        ///    </para>
        /// <remarks>
        ///    <para>
        ///       For information about handling events, see <see topic='cpconEventsOverview'/>.
        ///    </para>
        /// </remarks>
        /// </devdoc>
        [SRDescription(SR.FDfileOkDescr)]
        public event CancelEventHandler FileOk {
            add {
                Events.AddHandler(EventFileOk, value);
            }
            remove {
                Events.RemoveHandler(EventFileOk, value);
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.DoFileOk"]/*' />
        /// <devdoc>
        ///     Processes the CDN_FILEOK notification.
        /// </devdoc>
        private bool DoFileOk(IntPtr lpOFN) {
            NativeMethods.OPENFILENAME_I ofn = (NativeMethods.OPENFILENAME_I)UnsafeNativeMethods.PtrToStructure(lpOFN, typeof(NativeMethods.OPENFILENAME_I));
            int saveOptions = options;
            int saveFilterIndex = filterIndex;
            string[] saveFileNames = fileNames;
            bool saveSecurityCheckFileNames = securityCheckFileNames;
            bool ok = false;
            try {
                options = options & ~NativeMethods.OFN_READONLY |
                          ofn.Flags & NativeMethods.OFN_READONLY;
                filterIndex = ofn.nFilterIndex;
                charBuffer.PutCoTaskMem(ofn.lpstrFile);
 
                // We are filling in the file names list with secure
                // data.  Any access to this list now will require
                // a security demand.  We set this bit before actually
                // setting the names; otherwise a thread ---- could
                // expose them.
                securityCheckFileNames = true;
                Thread.MemoryBarrier();
 
                if ((options & NativeMethods.OFN_ALLOWMULTISELECT) == 0) {
                    fileNames = new string[] {charBuffer.GetString()};
                }
                else {
                    fileNames = GetMultiselectFiles(charBuffer);
                }
 
                if (ProcessFileNames()) {
                    CancelEventArgs ceevent = new CancelEventArgs();
                    if (NativeWindow.WndProcShouldBeDebuggable) {
                        OnFileOk(ceevent);
                        ok = !ceevent.Cancel;
                    }
                    else {
                        try
                        {
                            OnFileOk(ceevent);
                            ok = !ceevent.Cancel;
                        }
                        catch (Exception e)
                        {
                            Application.OnThreadException(e);
                        }
                    }
                }
            }
            finally {
                if (!ok) {
                    securityCheckFileNames = saveSecurityCheckFileNames;
                    Thread.MemoryBarrier();
                    fileNames = saveFileNames;
 
                    options = saveOptions;
                    filterIndex = saveFilterIndex;
                }
            }
            return ok;
        }
 
        /// SECREVIEW: ReviewImperativeSecurity
        ///   vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active.
        ///   reason for exclude: filename is a local variable and not subject to race conditions.
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        internal static bool FileExists(string fileName)
        {
            bool fileExists = false;
            try {
                // SECREVIEW : We must Assert just to check if the file exists. Since
                //           : we are doing this as part of the FileDialog, this is OK.
                new FileIOPermission(FileIOPermissionAccess.Read, IntSecurity.UnsafeGetFullPath(fileName)).Assert();
                try {
                    fileExists = File.Exists(fileName);
                }
                finally {
                    CodeAccessPermission.RevertAssert();
                }
            }
            catch (System.IO.PathTooLongException) {
            }
            return fileExists;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.GetMultiselectFiles"]/*' />
        /// <devdoc>
        ///     Extracts the filename(s) returned by the file dialog.
        /// </devdoc>
        private string[] GetMultiselectFiles(CharBuffer charBuffer) {
            string directory = charBuffer.GetString();
            string fileName = charBuffer.GetString();
            if (fileName.Length == 0) return new string[] {
                    directory
                };
            if (directory[directory.Length - 1] != '\\') {
                directory = directory + "\\";
            }
            ArrayList names = new ArrayList();
            do {
                if (fileName[0] != '\\' && (fileName.Length <= 3 ||
                                            fileName[1] != ':' || fileName[2] != '\\')) {
                    fileName = directory + fileName;
                }
                names.Add(fileName);
                fileName = charBuffer.GetString();
            } while (fileName.Length > 0);
            string[] temp = new string[names.Count];
            names.CopyTo(temp, 0);
            return temp;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.GetOption"]/*' />
        /// <devdoc>
        ///     Returns the state of the given option flag.
        /// </devdoc>
        /// <internalonly/>
 
        internal bool GetOption(int option) {
            return(options & option) != 0;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.HookProc"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Defines the common dialog box hook procedure that is overridden to add
        ///       specific functionality to the file dialog box.
        ///    </para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
        protected override IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {
            if (msg == NativeMethods.WM_NOTIFY) {
                dialogHWnd = UnsafeNativeMethods.GetParent(new HandleRef(null, hWnd));
                try {
                    UnsafeNativeMethods.OFNOTIFY notify = (UnsafeNativeMethods.OFNOTIFY)UnsafeNativeMethods.PtrToStructure(lparam, typeof(UnsafeNativeMethods.OFNOTIFY));
 
                    switch (notify.hdr_code) {
                        case -601: /* CDN_INITDONE */
                            MoveToScreenCenter(dialogHWnd);
                            break;
                        case -602: /* CDN_SELCHANGE */
                            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.
                            int sizeNeeded = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, dialogHWnd), 1124 /*CDM_GETSPEC*/, System.IntPtr.Zero, System.IntPtr.Zero);
                            if (sizeNeeded > ofn.nMaxFile) {
                                // A bigger buffer is required.
                                try {
                                    int newBufferSize = sizeNeeded + (FILEBUFSIZE / 4);
                                    // Allocate new buffer
                                    CharBuffer charBufferTmp = CharBuffer.CreateBuffer(newBufferSize);
                                    IntPtr newBuffer = charBufferTmp.AllocCoTaskMem();
                                    // Free old buffer
                                    Marshal.FreeCoTaskMem(ofn.lpstrFile);
                                    // Substitute buffer
                                    ofn.lpstrFile = newBuffer;
                                    ofn.nMaxFile = newBufferSize;
                                    this.charBuffer = charBufferTmp;
                                    Marshal.StructureToPtr(ofn, notify.lpOFN, true);
                                    Marshal.StructureToPtr(notify, lparam, true);
                                }
                                catch {
                                    // intentionaly not throwing here.
                                }
                            }
                            this.ignoreSecondFileOkNotification = false;
                            break;
                        case -604: /* CDN_SHAREVIOLATION */
                            // See VS Whidbey 95342. When the selected file is locked for writing,
                            // we get this notification followed by *two* CDN_FILEOK notifications.                            
                            this.ignoreSecondFileOkNotification = true;  // We want to ignore the second CDN_FILEOK
                            this.okNotificationCount = 0;                // to avoid a second prompt by PromptFileOverwrite.
                            break;
                        case -606: /* CDN_FILEOK */
                            if (this.ignoreSecondFileOkNotification)
                            {
                                // We got a CDN_SHAREVIOLATION notification and want to ignore the second CDN_FILEOK notification
                                if (this.okNotificationCount == 0)
                                {
                                    this.okNotificationCount = 1;   // This one is the first and is all right.
                                }
                                else
                                {
                                    // This is the second CDN_FILEOK, so we want to ignore it.
                                    this.ignoreSecondFileOkNotification = false;
                                    UnsafeNativeMethods.SetWindowLong(new HandleRef(null, hWnd), 0, new HandleRef(null, NativeMethods.InvalidIntPtr));
                                    return NativeMethods.InvalidIntPtr;
                                }
                            }
                            if (!DoFileOk(notify.lpOFN)) {
                                UnsafeNativeMethods.SetWindowLong(new HandleRef(null, hWnd), 0, new HandleRef(null, NativeMethods.InvalidIntPtr));
                                return NativeMethods.InvalidIntPtr;
                            }
                            break;
                    }
                }
                catch {
                    if (dialogHWnd != IntPtr.Zero) {
                        UnsafeNativeMethods.EndDialog(new HandleRef(this, dialogHWnd), IntPtr.Zero);
                    }
                    throw;
                }
            }
            return IntPtr.Zero;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.MakeFilterString"]/*' />
        /// <devdoc>
        ///     Converts the given filter string to the format required in an OPENFILENAME_I
        ///     structure.
        /// </devdoc>
        private static string MakeFilterString(string s, bool dereferenceLinks) {
            if (s == null || s.Length == 0)
            {
                // Workaround for Whidbey bug #5165
                // Apply the workaround only when DereferenceLinks is true and OS is at least WinXP.
                if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5)
                {
                    s = " |*.*";
                }
                else if (s == null)
                {
                    return null;
                }
            }
            int length = s.Length;
            char[] filter = new char[length + 2];
            s.CopyTo(0, filter, 0, length);
            for (int i = 0; i < length; i++) {
                if (filter[i] == '|') filter[i] = (char)0;
            }
            filter[length + 1] = (char)0;
            return new string(filter);
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.OnFileOk"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Windows.Forms.FileDialog.FileOk'/> event.
        ///    </para>
        /// </devdoc>
        protected void OnFileOk(CancelEventArgs e) {
            CancelEventHandler handler = (CancelEventHandler)Events[EventFileOk];
            if (handler != null) handler(this, e);
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.ProcessFileNames"]/*' />
        /// <devdoc>
        ///     Processes the filenames entered in the dialog according to the settings
        ///     of the "addExtension", "checkFileExists", "createPrompt", and
        ///     "overwritePrompt" properties.
        /// </devdoc>
        private bool ProcessFileNames() {
            if ((options & NativeMethods.OFN_NOVALIDATE) == 0) {
                string[] extensions = FilterExtensions;
                for (int i = 0; i < fileNames.Length; i++) {
                    string fileName = fileNames[i];
                    if ((options & OPTION_ADDEXTENSION) != 0 && !Path.HasExtension(fileName)) {
                        bool fileMustExist = (options & NativeMethods.OFN_FILEMUSTEXIST) != 0;
 
                        for (int j = 0; j < extensions.Length; j++) {
                            string currentExtension = Path.GetExtension(fileName);
                            
                            Debug.Assert(!extensions[j].StartsWith("."), 
                                         "FileDialog.FilterExtensions should not return things starting with '.'");
                            Debug.Assert(currentExtension.Length == 0 || currentExtension.StartsWith("."), 
                                         "File.GetExtension should return something that starts with '.'");
                            
                            string s = 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) {
                                s += "." + extensions[j];
                            }
 
                            if (!fileMustExist || FileExists(s)) {
                                fileName = s;
                                break;
                            }
                        }
                        fileNames[i] = fileName;
                    }
                    if (!PromptUserIfAppropriate(fileName))
                        return false;
                }
            }
            return true;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.MessageBoxWithFocusRestore"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Prompts the user with a <see cref='System.Windows.Forms.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).
        ///    </para>
        /// </devdoc>
        internal bool MessageBoxWithFocusRestore(string message, string caption,
                MessageBoxButtons buttons, MessageBoxIcon icon)
        {
            bool ret;
            IntPtr focusHandle = UnsafeNativeMethods.GetFocus();           
            try {
                ret = RTLAwareMessageBox.Show(null, message, caption, buttons, icon,
                        MessageBoxDefaultButton.Button1, 0) == DialogResult.Yes;
            }
            finally {
                UnsafeNativeMethods.SetFocus(new HandleRef(null, focusHandle));
            }
            return ret;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.PromptFileNotFound"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Prompts the user with a <see cref='System.Windows.Forms.MessageBox'/>
        ///       when a file
        ///       does not exist.
        ///    </para>
        /// </devdoc>
        private void PromptFileNotFound(string fileName) {
            MessageBoxWithFocusRestore(SR.GetString(SR.FileDialogFileNotFound, fileName), DialogCaption,
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
 
        // If it's necessary to throw up a "This file exists, are you sure?" kind of
        // MessageBox, here's where we do it
        // Return value is whether or not the user hit "okay".
        internal virtual bool PromptUserIfAppropriate(string fileName) {
            if ((options & NativeMethods.OFN_FILEMUSTEXIST) != 0) {
                if (!FileExists(fileName)) {
                    PromptFileNotFound(fileName);
                    return false;
                }
            }
            return true;
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.Reset"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Resets all properties to their default values.
        ///    </para>
        /// </devdoc>
        public override void Reset() {
            options = NativeMethods.OFN_HIDEREADONLY | NativeMethods.OFN_PATHMUSTEXIST |
                      OPTION_ADDEXTENSION;
            title = null;
            initialDir = null;
            defaultExt = null;
            fileNames = null;
            filter = null;
            filterIndex = 1;
            supportMultiDottedExtensions = false;
            this._customPlaces.Clear();
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.RunDialog"]/*' />
        /// <devdoc>
        ///    Implements running of a file dialog.
        /// </devdoc>
        /// <internalonly/>
        protected override bool RunDialog(IntPtr hWndOwner) {
            // See VSWhidbey bug 107000. Shell APIs do not support multisthreaded apartment model.
            if (Control.CheckForIllegalCrossThreadCalls && Application.OleRequired() != System.Threading.ApartmentState.STA) {
                throw new System.Threading.ThreadStateException(SR.GetString(SR.DebuggingExceptionOnly, SR.GetString(SR.ThreadMustBeSTA)));
            }
            EnsureFileDialogPermission();
            if (this.UseVistaDialogInternal)
            {
                return RunDialogVista(hWndOwner);
            }
            else
            {
                return RunDialogOld(hWndOwner);
            }
        }
 
        internal abstract void EnsureFileDialogPermission();
 
        private bool RunDialogOld(IntPtr hWndOwner)
        {
            NativeMethods.WndProc hookProcPtr = new NativeMethods.WndProc(this.HookProc);
            NativeMethods.OPENFILENAME_I ofn = new NativeMethods.OPENFILENAME_I();
            try {
                charBuffer = CharBuffer.CreateBuffer(FILEBUFSIZE);
                if (fileNames != null) {
                    charBuffer.PutString(fileNames[0]);
                }
                ofn.lStructSize = Marshal.SizeOf(typeof(NativeMethods.OPENFILENAME_I));
                // Degrade to the older style dialog if we're not on Win2K.
                // We do this by setting the struct size to a different value
                //
                if (Environment.OSVersion.Platform != System.PlatformID.Win32NT ||
                    Environment.OSVersion.Version.Major < 5) {
                    ofn.lStructSize = 0x4C;
                }
                ofn.hwndOwner = hWndOwner;
                ofn.hInstance = Instance;
                ofn.lpstrFilter = MakeFilterString(filter, this.DereferenceLinks);
                ofn.nFilterIndex = filterIndex;
                ofn.lpstrFile = charBuffer.AllocCoTaskMem();
                ofn.nMaxFile = FILEBUFSIZE;
                ofn.lpstrInitialDir = initialDir;
                ofn.lpstrTitle = title;
                ofn.Flags = Options | (NativeMethods.OFN_EXPLORER | NativeMethods.OFN_ENABLEHOOK | NativeMethods.OFN_ENABLESIZING);
                ofn.lpfnHook = hookProcPtr;
                ofn.FlagsEx = NativeMethods.OFN_USESHELLITEM;
                if (defaultExt != null && AddExtension) {
                    ofn.lpstrDefExt = defaultExt;
                }
                //Security checks happen here
                return RunFileDialog(ofn);
            }
            finally {
                charBuffer = null;
                if (ofn.lpstrFile != IntPtr.Zero) {
                    Marshal.FreeCoTaskMem(ofn.lpstrFile);
                }
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.RunFileDialog"]/*' />
        /// <devdoc>
        ///     Implements the actual call to GetOPENFILENAME_I or GetSaveFileName.
        /// </devdoc>
        /// <internalonly/>
        internal abstract bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn);
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.SetOption"]/*' />
        /// <devdoc>
        ///     Sets the given option to the given boolean value.
        /// </devdoc>
        /// <internalonly/>
        internal void SetOption(int option, bool value) {
            if (value) {
                options |= option;
            }
            else {
                options &= ~option;
            }
        }
 
        /// <include file='doc\FileDialog.uex' path='docs/doc[@for="FileDialog.ToString"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>
        ///       Provides a string version of this Object.
        ///    </para>
        /// </devdoc>
        public override string ToString() {
            StringBuilder sb = new StringBuilder(base.ToString() + ": Title: " + Title + ", FileName: ");
            try
            {
                sb.Append(FileName);
            }
            catch (Exception e)
            {
                sb.Append("<");
                sb.Append(e.GetType().FullName);
                sb.Append(">");
            }
            return sb.ToString();
        }
 
        
    }
}