我可以在C:\ Users \ Public \ Documents中启动DotNet的OpenFileDialog吗?

时间:2016-11-13 06:20:33

标签: c# .net openfiledialog

有没有办法在C:\Users\Public\Documents文件夹中启动OpenFileDialog

我正在使用DotNet框架编写C#应用程序。我正在尝试发布一个InitialDirectory"C:\\Users\\Public\\Documents\\" FileName"world.txt" OpenFileDialog。很遗憾,Documents将我置于C:\Users\Public\Documents快捷方式而不是> This PC > Windows7_OS (C:) > Users > Public > Documents

预期结果
我希望看到OpenFileDialog打开,顶部文本框显示world.txt,底部文本框显示C:\Users\Public\Documents。我希望如果我点击顶部文本框,它会显示> This PC > Documents

实际结果
OpenFileDialog打开。顶部文本框显示world.txt,底部文本框显示Documents。如果我点击顶部文本框,则会显示C:\Users\Public\Documents。显示的文件夹内容OpenFileDialog dlg = new OpenFileDialog();的内容相同。

我尝试过的事情
我在以下代码行之后停止了Visual Studio调试器中的代码:
dlg.FileName = "world.txt" ? dlg.FileName dlg.InitialDirectory = "C:\\NonExistentDirectory\\"; dlg.ShowDialog(); dlg.InitialDirectory = "C:\\"; dlg.ShowDialog(); dlg.InitialDirectory = "C:\\Users\\"; dlg.ShowDialog(); dlg.InitialDirectory = "C:\\Users\\Public\\"; dlg.ShowDialog(); dlg.InitialDirectory = "C:\\Users\\Public\\Documents\\"; dlg.ShowDialog();

在立即窗口中,我执行了以下代码:

C:\WINDOWS\System32\cmd.exe

我取消了每个对话框。

我在cdC:\以及C:\Users\C:\Users\Public之间使用C:\Users\Public\Documents\dlg.InitialDirectory = "C:\\NonExistentDirectory\\"

我尝试过的结果

  • This PC > Documents > Visual Studio 2015 > Projects > SimpleGame > Controller > bin > Debug"时,对话框的文件夹最初显示为C:\Users\Owner\Documents\Visual Studio 2015\Projects\SimpleGame\Controller\bin\Debug。单击文本框会使其显示OpenFileDialog。因此,我假设InitialDirectory通过不更改目录来静默处理无效的Debug。在这种情况下,它默认为我的项目的程序集的bin dlg.InitialDirectory文件夹。

  • "C:\\""C:\\Users\\""C:\\Users\\Public\\"C:\时,对话框的行为符合预期。点击顶部文本框会分别生成C:\UsersC:\Users\Publicdlg.InitialDirectory = "C:\\Users\\Public\\Documents\\"

  • > This PC > Documents对话框行为不正确时。顶部文本框显示world.txt,底部文本框显示Documents。如果我点击顶部文本框,则会显示C:\Users\Public\Documents。显示的文件夹内容cmd.exe的内容相同。

  • 使用cd可以按预期在文件夹之间C:\Users\Public\Documents,包括<br>

我的环境
我正在使用Microsoft Visual C#2015运行Microsoft Visual Studio社区2015版本14.0.23107.0 D14REL。我的操作系统是Windows 10 Pro。

4 个答案:

答案 0 :(得分:2)

尽管如Silver所述,这是一个错误,但可以在不同的线程上使用带有WM_SETTEXT的SendMessage API大致绕过,尽管至少说,它很可能会起作用。

我使用NSGaga's post汇总了一些脏代码片段以显示概念证明,这个原始示例不应该按原样使用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication13
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();

            //start a thread to change the dialog path just before displaying it to the user
            Thread posThread = new Thread(setDialogPath);
            posThread.Start();

            //display dialog to the user
            DialogResult dr = dlg.ShowDialog();
        }

        [DllImport("user32.dll")]
        static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName);
        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, string lParam);
        [DllImport("user32.dll")]
        static extern IntPtr PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        [DllImport("user32.Dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);

        private void setDialogPath()
        {
            const string FULL_PATH = "C:\\Users\\Public\\Documents";

            //messages
            const int WM_SETTEXT = 0xC;
            const int WM_KEYDOWN = 0x100;
            const int WM_KEYUP = 0x101;
            //enter key code
            const int VK_RETURN = 0x0D;

            //dialog box window handle
            IntPtr _window_hwnd;

            //how many attempts to detect the window
            int _attempts_count = 0;

            //get the dialog window handle
            while ((_window_hwnd = FindWindow(IntPtr.Zero, "Open")) == IntPtr.Zero)
                if (++_attempts_count > 100)
                    return;
                else
                    Thread.Sleep(500); //try again

            //in it - find the path textbox's handle.
            var hwndChild = EnumAllWindows(_window_hwnd, "Edit").FirstOrDefault();

            //set the path
            SendMessage(hwndChild, WM_SETTEXT, 0, FULL_PATH);

            //apply the path (send 'enter' to the textbox)
            PostMessage(hwndChild, WM_KEYDOWN, VK_RETURN, 0);
            PostMessage(hwndChild, WM_KEYUP, VK_RETURN, 0);
        }


        public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            list.Add(handle);
            return true;
        }

        public static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                Win32Callback childProc = new Win32Callback(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }

        public static string GetWinClass(IntPtr hwnd)
        {
            if (hwnd == IntPtr.Zero)
                return null;
            StringBuilder classname = new StringBuilder(100);
            IntPtr result = GetClassName(hwnd, classname, classname.Capacity);
            if (result != IntPtr.Zero)
                return classname.ToString();
            return null;
        }

        public static IEnumerable<IntPtr> EnumAllWindows(IntPtr hwnd, string childClassName)
        {
            List<IntPtr> children = GetChildWindows(hwnd);
            if (children == null)
                yield break;
            foreach (IntPtr child in children)
            {
                if (GetWinClass(child) == childClassName)
                    yield return child;
                foreach (var childchild in EnumAllWindows(child, childClassName))
                    yield return childchild;
            }
        }

    }
}

Hackish,但可行

答案 1 :(得分:1)

似乎不可能!

您可以在以下链接中看到它是.net框架中的错误:( msdn link 你可以在最后的评论中找到:

  

很难相信,但是:

     

这是一个错误,没有别的。

答案 2 :(得分:1)

如果您使用的是System.Windows.Forms.OpenFileDialog,则可以设置:

dialog.AutoUpgradeEnabled = false;

对话框看起来有点陈旧/平坦/&#34;旧学校&#34;但它确实至少显示正确的内容!

答案 3 :(得分:1)

您会认为显而易见的答案是使用Environment.SpecialFolder.CommonDo‌​‌​cuments,但这似乎与Windows 10上的Environment.SpecialFolder.MyDo‌​‌​cuments完全相同。这必定是.NET中的错误!

我今天遇到了这个问题,找到了一个对我有用的解决方案,可能对其他人有用。只需将子文件夹添加到公共文档,然后将其用作初始目录。最好的做法是将您的东西存储在特定于应用程序的子文件夹中,而不是仅仅存储在根目录中。像这样:

Path.Combine(Environment.SpecialFolder.CommonDocuments, "SomeSubfolder").