P / Invoke SHSetKnownFolderPath - PullRequest
       30

P / Invoke SHSetKnownFolderPath

0 голосов
/ 27 января 2009

РЕДАКТИРОВАТЬ: Скотти2012 и ответы Дэвида Мортона не работают для меня, поэтому я положил награду за этот вопрос. Я думаю, что мне нужно изменить тип строки на что-то еще, прежде чем передать его.

Я не так много копов в P / Invoke, и я борюсь с объявлением и вызовом SHSetKnownFolderPath Я использую VB9, но если кто-нибудь выложит ответы на C #, я смогу перевести.

У меня работает SHGetKnowFolderPath. Вот мой код.

В VB

Imports System.Runtime.InteropServices

Public Class Form1
    <DllImport("shell32.dll")> _
    Private Shared Function SHGetKnownFolderPath(<MarshalAs(UnmanagedType.LPStruct)> ByVal rfid As Guid, ByVal dwFlags As UInteger, ByVal hToken As IntPtr, ByRef pszPath As IntPtr) As Integer
    End Function

    <DllImport("shell32.dll")> _
    Private Shared Function SHSetKnownFolderPath(<MarshalAs(UnmanagedType.LPStruct)> ByVal rfid As Guid, ByVal dwFlags As UInteger, ByVal hToken As IntPtr, ByRef pszPath As IntPtr) As Integer
    End Function

    Public Shared ReadOnly Documents As New Guid("FDD39AD0-238F-46AF-ADB4-6C85480369C7")


    Private Sub ButtonSetDocumentsPath_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSetDocumentsPath.Click
        Dim pPath As IntPtr = Marshal.StringToCoTaskMemUni(TextBoxPath.Text)
        If SHSetKnownFolderPath(Documents, 0, IntPtr.Zero, pPath) = 0 Then
            MsgBox("Set Sucessfully")
        End If

    End Sub

    Private Sub ButtonGetDocumentsPath_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonGetDocumentsPath.Click
        Dim pPath As IntPtr
        If SHGetKnownFolderPath(Documents, 0, IntPtr.Zero, pPath) = 0 Then
            Dim s As String = Marshal.PtrToStringUni(pPath)
            Marshal.FreeCoTaskMem(pPath)
            TextBoxPath.Text = s
        End If

    End Sub
End Class

Спасибо!

Ответы [ 3 ]

2 голосов
/ 02 февраля 2009

Попробуйте этот код. Извините за длину, но все это необходимо для правильной PInvoke этой конкретной функции. Это простое консольное приложение, которое включает определение для обеих функций и пример использования SHGetKnownFolderPath.

Я пошел дальше и включил определения для KNOWN_FOLDER_FLAG, а также несколько определений для идентификаторов папок. Все идентификаторы папок на самом деле просто GUID. Все возможные идентификаторы могут быть найдены в% ProgramFiles% \ Windows SDK \ v6.0A \ Include \ KnownFolders.h и добавлены так же, как я добавил в примере.

Я включил несколько функций-оболочек, которые скрывают все детали злого маршала для вызова определенных функций.

Если есть какой-либо конкретный идентификатор папки, который вы хотели бы или объяснение, пожалуйста, добавьте комментарий, и я обновлю образец.

EDIT Исправлена ​​ошибка в Marshalling SHSetKnownFolderPath. Я не добавил тэг MarshalAs к значению String, и по умолчанию он представлял собой строку ANSI. Для API требуется Unicode. Функция SHSetFolder теперь работает (подтверждено с помощью RecentFolder)

Imports System.Runtime.InteropServices



Module NativeMethods

    Public Enum KNOWN_FOLDER_FLAG

        '''KF_FLAG_CREATE -> 0x00008000
        KF_FLAG_CREATE = 32768

        '''KF_FLAG_DONT_VERIFY -> 0x00004000
        KF_FLAG_DONT_VERIFY = 16384

        '''KF_FLAG_DONT_UNEXPAND -> 0x00002000
        KF_FLAG_DONT_UNEXPAND = 8192

        '''KF_FLAG_NO_ALIAS -> 0x00001000
        KF_FLAG_NO_ALIAS = 4096

        '''KF_FLAG_INIT -> 0x00000800
        KF_FLAG_INIT = 2048

        '''KF_FLAG_DEFAULT_PATH -> 0x00000400
        KF_FLAG_DEFAULT_PATH = 1024

        '''KF_FLAG_NOT_PARENT_RELATIVE -> 0x00000200
        KF_FLAG_NOT_PARENT_RELATIVE = 512

        '''KF_FLAG_SIMPLE_IDLIST -> 0x00000100
        KF_FLAG_SIMPLE_IDLIST = 256

        '''KF_FLAG_ALIAS_ONLY -> 0x80000000
        KF_FLAG_ALIAS_ONLY = &H80000000
    End Enum


    Public ComputerFolder As Guid = New Guid("0AC0837C-BBF8-452A-850D-79D08E667CA7")
    Public DesktopFolder As Guid = New Guid("B4BFCC3A-DB2C-424C-B029-7FE99A87C641")
    Public DocumentsFolder As Guid = New Guid("FDD39AD0-238F-46AF-ADB4-6C85480369C7")


    <DllImport("shell32.dll")> _
    Public Function SHGetKnownFolderPath( _
        ByRef folderId As Guid, _
        ByVal flags As UInteger, _
        ByVal token As IntPtr, _
        <Out()> ByRef pathPtr As IntPtr) As Integer

    End Function

    <DllImport("shell32.dll")> _
    Public Function SHSetKnownFolderPath( _
        ByRef folderId As Guid, _
        ByVal flags As UInteger, _
        ByVal token As IntPtr, _
        <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal path As String) As Integer

    End Function

    Public Function SHGetKnownFolderPathWrapper(ByVal folderId As Guid) As String
        Return SHGetKnownFolderPathWrapper(folderId, 0)
    End Function

    Public Function SHGetKnownFolderPathWrapper( _
        ByVal folderId As Guid, _
        ByVal flags As KNOWN_FOLDER_FLAG) As String

        Dim ptr = IntPtr.Zero
        Dim path = String.Empty
        Try
            Dim ret = SHGetKnownFolderPath(folderId, CUInt(flags), IntPtr.Zero, ptr)
            If ret <> 0 Then
                Throw Marshal.GetExceptionForHR(ret)
            End If
            path = Marshal.PtrToStringUni(ptr)
        Finally
            Marshal.FreeCoTaskMem(ptr)
        End Try
        Return path
    End Function

    Public Sub SHSetKnownFolderPathWrapper( _
        ByVal folderId As Guid, _
        ByVal path As String)

        SHSetKnownFolderPathWrapper(folderId, 0, path)
    End Sub

    Public Sub SHSetKnownFolderPathWrapper( _
        ByVal folderId As Guid, _
        ByVal flags As KNOWN_FOLDER_FLAG, _
        ByVal path As String)

        Dim ret = SHSetKnownFolderPath(folderId, CUInt(flags), IntPtr.Zero, path)
        If ret <> 0 Then
            Throw Marshal.GetExceptionForHR(ret)
        End If
    End Sub

End Module

Module Module1

    Sub Main()
        Dim path = SHGetKnownFolderPathWrapper(NativeMethods.DesktopFolder)
        Console.WriteLine(path)
    End Sub

End Module
2 голосов
/ 27 января 2009

Я думаю, что это должно работать в C # (у меня нет Vista, поэтому я не могу проверить):

[DllImport("shell32.dll")]
private static int SHSetKnownFolderPath(ref Guid guid, int flags, IntPtr hToken, string newPath);

Вы можете назвать это так

SHSetKnownFolderPath(ref Documents, 0, IntPtr.Zero, "c:\\my new path\\");
0 голосов
/ 27 января 2009

Это будет объявление:

[DllImport("shell32.dll")]
static extern int SHSetFolderPath(int csidl, IntPtr hToken, uint dwFlags, StringBuilder path)

Вам необходимо создать StringBuilder, передавая максимальный путь 260 в конструктор (это будет верно для Vista / XP.) Это строитель строк, который будет иметь новый каталог для папки, которую вы пытаетесь установить, поэтому добавьте свой текст в StringBuilder для вашего нового местоположения. Однако самая большая проблема с вашей реализацией заключается в том, что параметр csidl не совпадает с параметром Guid, указанным в Windows. Эти значения фактически являются значениями, которые объявлены в shlobj.h . Перейдите по ссылке, чтобы увидеть значения, которые будут переданы сверху. HToken всегда должен быть IntPtr.Zero, если у вас нет указателя на конкретного пользователя, для которого вы пытаетесь изменить это. IntPtr.Zero будет использовать текущий пользователь. dwFlags всегда должен быть равен 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...