|
//------------------------------------------------------------------------------
// <copyright file="FileDialog_Vista.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms
{
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Diagnostics.CodeAnalysis;
public partial class FileDialog
{
private bool _autoUpgradeEnabled = true;
internal virtual bool SettingsSupportVistaDialog
{
get
{
return !this.ShowHelp &&
((Application.VisualStyleState & VisualStyles.VisualStyleState.ClientAreaEnabled) == VisualStyles.VisualStyleState.ClientAreaEnabled);
}
}
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
internal bool UseVistaDialogInternal
{
get
{
if (UnsafeNativeMethods.IsVista && this._autoUpgradeEnabled && SettingsSupportVistaDialog)
{
// SECREVIEW : Querying for environment info is safe.
//
new EnvironmentPermission(PermissionState.Unrestricted).Assert();
try
{
return SystemInformation.BootMode == BootMode.Normal; // see DDB#169589
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
return false;
}
}
internal abstract FileDialogNative.IFileDialog CreateVistaDialog();
[
SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode),
SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")
]
private bool RunDialogVista(IntPtr hWndOwner)
{
FileDialogNative.IFileDialog dialog = CreateVistaDialog();
OnBeforeVistaDialog(dialog);
VistaDialogEvents events = new VistaDialogEvents(this);
uint eventCookie;
dialog.Advise(events, out eventCookie);
try
{
int result = dialog.Show(hWndOwner);
return 0 == result;
}
finally
{
dialog.Unadvise(eventCookie);
//Make sure that the event interface doesn't get collected
GC.KeepAlive(events);
}
}
internal virtual void OnBeforeVistaDialog(FileDialogNative.IFileDialog dialog)
{
dialog.SetDefaultExtension(this.DefaultExt);
dialog.SetFileName(this.FileName);
if (!string.IsNullOrEmpty(this.InitialDirectory))
{
try
{
FileDialogNative.IShellItem initialDirectory = GetShellItemForPath(this.InitialDirectory);
dialog.SetDefaultFolder(initialDirectory);
dialog.SetFolder(initialDirectory);
}
catch (FileNotFoundException)
{
}
}
dialog.SetTitle(this.Title);
dialog.SetOptions(GetOptions());
SetFileTypes(dialog);
this._customPlaces.Apply(dialog);
}
private FileDialogNative.FOS GetOptions()
{
const FileDialogNative.FOS BlittableOptions =
FileDialogNative.FOS.FOS_OVERWRITEPROMPT
| FileDialogNative.FOS.FOS_NOCHANGEDIR
| FileDialogNative.FOS.FOS_NOVALIDATE
| FileDialogNative.FOS.FOS_ALLOWMULTISELECT
| FileDialogNative.FOS.FOS_PATHMUSTEXIST
| FileDialogNative.FOS.FOS_FILEMUSTEXIST
| FileDialogNative.FOS.FOS_CREATEPROMPT
| FileDialogNative.FOS.FOS_NODEREFERENCELINKS
;
const int UnexpectedOptions =
NativeMethods.OFN_USESHELLITEM //This is totally bogus (only used in FileDialog by accident to ensure that places are shown
| NativeMethods.OFN_SHOWHELP //If ShowHelp is true, we don't use the Vista Dialog
| NativeMethods.OFN_ENABLEHOOK //These shouldn't be set in options (only set in the flags for the legacy dialog)
| NativeMethods.OFN_ENABLESIZING //These shouldn't be set in options (only set in the flags for the legacy dialog)
| NativeMethods.OFN_EXPLORER //These shouldn't be set in options (only set in the flags for the legacy dialog)
;
System.Diagnostics.Debug.Assert(0==(UnexpectedOptions & options), "Unexpected FileDialog options");
FileDialogNative.FOS ret = (FileDialogNative.FOS)options & BlittableOptions;
//Force no mini mode for the SaveFileDialog
ret |= FileDialogNative.FOS.FOS_DEFAULTNOMINIMODE;
// DevDiv Bugs 107025: Make sure that the Open dialog allows the user to specify
// non-file system locations. This flag will cause the dialog to copy the resource
// to a local cache (Temporary Internet Files), and return that path instead. This
// also affects the Save dialog by disallowing navigation to these areas.
// An example of a non-file system location is a URL (http://), or a file stored on
// a digital camera that is not mapped to a drive letter.
// This reproduces the behavior of the "classic" Open and Save dialogs.
ret |= FileDialogNative.FOS.FOS_FORCEFILESYSTEM;
return ret;
}
internal abstract string[] ProcessVistaFiles(FileDialogNative.IFileDialog dialog);
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private bool HandleVistaFileOk(FileDialogNative.IFileDialog dialog)
{
int saveOptions = options;
int saveFilterIndex = filterIndex;
string[] saveFileNames = fileNames;
bool saveSecurityCheckFileNames = securityCheckFileNames;
bool ok = false;
try
{
securityCheckFileNames = true;
Thread.MemoryBarrier();
uint filterIndexTemp;
dialog.GetFileTypeIndex(out filterIndexTemp);
filterIndex = unchecked((int)filterIndexTemp);
fileNames = ProcessVistaFiles(dialog);
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)
{
//Order here is important. We don't want a window where securityCheckFileNames is false, but the temporary fileNames is still in place
securityCheckFileNames = saveSecurityCheckFileNames;
Thread.MemoryBarrier();
fileNames = saveFileNames;
options = saveOptions;
filterIndex = saveFilterIndex;
}
else
{
if (0 != (options & NativeMethods.OFN_HIDEREADONLY))
{
//When the dialog is dismissed OK, the Readonly bit can't
// be left on if ShowReadOnly was false
// Downlevel this happens automatically, on Vista mode, we need to watch out for it.
options &= ~ NativeMethods.OFN_READONLY;
}
}
}
return ok;
}
private class VistaDialogEvents : FileDialogNative.IFileDialogEvents
{
private FileDialog _dialog;
public VistaDialogEvents(FileDialog dialog)
{
this._dialog = dialog;
}
public int OnFileOk(FileDialogNative.IFileDialog pfd)
{
return this._dialog.HandleVistaFileOk(pfd) ? NativeMethods.S_OK : NativeMethods.S_FALSE;
}
public int OnFolderChanging(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psiFolder)
{
return NativeMethods.S_OK;
}
public void OnFolderChange(FileDialogNative.IFileDialog pfd)
{
}
public void OnSelectionChange(FileDialogNative.IFileDialog pfd)
{
}
public void OnShareViolation(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psi, out FileDialogNative.FDE_SHAREVIOLATION_RESPONSE pResponse)
{
pResponse = FileDialogNative.FDE_SHAREVIOLATION_RESPONSE.FDESVR_DEFAULT;
}
public void OnTypeChange(FileDialogNative.IFileDialog pfd)
{
}
public void OnOverwrite(FileDialogNative.IFileDialog pfd, FileDialogNative.IShellItem psi, out FileDialogNative.FDE_OVERWRITE_RESPONSE pResponse)
{
pResponse = FileDialogNative.FDE_OVERWRITE_RESPONSE.FDEOR_DEFAULT;
}
}
private void SetFileTypes(FileDialogNative.IFileDialog dialog)
{
FileDialogNative.COMDLG_FILTERSPEC[] filterItems = FilterItems;
dialog.SetFileTypes((uint)filterItems.Length, filterItems);
if (filterItems.Length > 0)
{
dialog.SetFileTypeIndex(unchecked((uint)filterIndex));
}
}
private FileDialogNative.COMDLG_FILTERSPEC[] FilterItems
{
get
{
return GetFilterItems(this.filter);
}
}
private static FileDialogNative.COMDLG_FILTERSPEC[] GetFilterItems(string filter)
{
//Expected input types
//"Text files (*.txt)|*.txt|All files (*.*)|*.*"
//"Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*"
List<FileDialogNative.COMDLG_FILTERSPEC> extensions = new List<FileDialogNative.COMDLG_FILTERSPEC>();
if (!string.IsNullOrEmpty(filter))
{
string[] tokens = filter.Split('|');
if (0 == tokens.Length % 2)
{
//All even numbered tokens should be labels
//Odd numbered tokens are the associated extensions
for (int i = 1; i < tokens.Length; i += 2)
{
FileDialogNative.COMDLG_FILTERSPEC extension;
extension.pszSpec = tokens[i];//This may be a semicolon delimeted list of extensions (that's ok)
extension.pszName = tokens[i - 1];
extensions.Add(extension);
}
}
}
return extensions.ToArray();
}
internal static FileDialogNative.IShellItem GetShellItemForPath(string path)
{
FileDialogNative.IShellItem ret = null;
IntPtr pidl = IntPtr.Zero;
uint zero = 0;
if (0 <= UnsafeNativeMethods.Shell32.SHILCreateFromPath(path, out pidl, ref zero))
{
if (0 <= UnsafeNativeMethods.Shell32.SHCreateShellItem(
IntPtr.Zero, //No parent specified
IntPtr.Zero,
pidl,
out ret))
{
return ret;
}
}
throw new System.IO.FileNotFoundException();
}
internal static string GetFilePathFromShellItem(FileDialogNative.IShellItem item)
{
string filename;
item.GetDisplayName(FileDialogNative.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out filename);
return filename;
}
private FileDialogCustomPlacesCollection _customPlaces = new FileDialogCustomPlacesCollection();
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public FileDialogCustomPlacesCollection CustomPlaces
{
get
{
return this._customPlaces;
}
}
/// <summary>
/// Gets or Sets whether the dialog will be automatically upgraded to enable new features.
/// </summary>
[
DefaultValue(true)
]
public bool AutoUpgradeEnabled
{
get
{
return this._autoUpgradeEnabled;
}
set
{
this._autoUpgradeEnabled = value;
}
}
}
}
|