SHGetKnownFolderPath / Environment.GetFolderPath () возвращает неправильное значение для открытых документов - PullRequest
9 голосов
/ 26 августа 2011

При попытке разрешить каталог CommonDocuments я получил несколько странную ошибку.Он продолжает преобразовываться в неправильный каталог после того, как каталог CommonDocuments был перенаправлен / перемещен в новое местоположение с помощью проводника Windows (Свойства-> Путь из контекстного меню).

минимальная рабочаякусок кода будет:

namespace CommonDocumentsTest
{
    class Program
    {
        private static readonly Guid CommonDocumentsGuid = new Guid("ED4824AF-DCE4-45A8-81E2-FC7965083634");

        [Flags]
        public enum KnownFolderFlag : uint
        {
            None = 0x0,
            CREATE = 0x8000,
            DONT_VERFIY = 0x4000,
            DONT_UNEXPAND= 0x2000,
            NO_ALIAS = 0x1000,
            INIT = 0x800,
            DEFAULT_PATH = 0x400,
            NOT_PARENT_RELATIVE = 0x200,
            SIMPLE_IDLIST = 0x100,
            ALIAS_ONLY = 0x80000000
        }

        [DllImport("shell32.dll")]
        static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath);

        static void Main(string[] args)
        {
            KnownFolderFlag[] flags = new KnownFolderFlag[] {
                KnownFolderFlag.None,
                KnownFolderFlag.ALIAS_ONLY | KnownFolderFlag.DONT_VERFIY,
                KnownFolderFlag.DEFAULT_PATH | KnownFolderFlag.NOT_PARENT_RELATIVE,
            };


            foreach (var flag in flags)
            {
                Console.WriteLine(string.Format("{0}; P/Invoke==>{1}", flag, pinvokePath(flag)));
            }
            Console.ReadLine();
        }

        private static string pinvokePath(KnownFolderFlag flags)
        {
            IntPtr pPath;
            SHGetKnownFolderPath(CommonDocumentsGuid, (uint)flags, IntPtr.Zero, out pPath); // public documents

            string path = System.Runtime.InteropServices.Marshal.PtrToStringUni(pPath);
            System.Runtime.InteropServices.Marshal.FreeCoTaskMem(pPath);
            return path;
        }
    }
}

Ожидаемое поведение:
Вывод D:\TestDocuments

Фактическое поведение:
Выход составляет C:\Users\Public\Documents

Нет;P / Invoke ==> C: \ Users \ Public \ Documents
DONT_VERFIY, ALIAS_ONLY;P / Invoke ==>
NOT_PARENT_RELATIVE, DEFAULT_PATH;P / Invoke ==> C: \ Users \ Public \ Documents

Правильное значение хранится в реестре Windows (HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Explorer \ Папки оболочки \ Общие документы), но не возвращается SHGetKnownFolderPath (или Environment.GetFolderPath)

ОС: Windows 7 Professional x64
.NET Framework v4.0.30319 Приложение скомпилировано для x86 CPU

То, что я пробовал до сих пор:

  • перезапуск моего приложения
  • перезапуск компьютера
  • вызов Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments);
  • directвызовы Win32-API SHGetKnownFolderPath

EDIT 2 Шаги для воспроизведения:

  1. отключить UAC на вашем компьютере [и перезапустить!]
  2. перейдите в C: \ Users \ Public \
  3. , щелкните правой кнопкой мыши папку «Public Documents» и выберите Properties
  4. , выберите вкладку «Путь»
  5. нажмите «Переместить» и выберите (новую) папку на диске D: с именем TestDocuments
  6. нажмите «Применить»
  7. принять, чтобы переместить все файлыПосле того, как вы перейдете на новое место, запустите минимальное приложение выше

1 Ответ

5 голосов
/ 13 сентября 2011

tl; dr: Поведение является заданным и отображается только при запуске сборки, скомпилированной для x86 CPU в операционной системе x64


Более длинная версия:
Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments) получает доступ к 32-разрядному кусту реестра Windows.
Фактический путь к папке хранится в 64-битном кусте. Эта проблема была передана команде Windows и может быть исправлена ​​в будущей версии Windows.

Для получения дополнительной информации см. отчет Microsoft Connect

.

Обход создайте консольное приложение со следующим кодом и скомпилируйте его для ЛЮБОГО ЦП

static void Main(string[] args)
{
        Console.WriteLine("{0}", Environment.GetFolderPath(System.Environment.SpecialFolder.CommonDocuments));
}

затем позвоните из основного приложения:

Process proc = new Process();
ProcessStartInfo info = new ProcessStartInfo("myConsoleApp.exe");

// allow output to be read
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
proc.StartInfo = info;

proc.Start(); 
proc.WaitForExit();
string path = proc.StandardOutput.ReadToEnd();

запустит исполняемый файл ANY CPU , который выводит только желаемый путь к стандартному выводу. Затем вывод читается в основном приложении, и вы получаете реальный путь.

...