P / Invoke SHSetKnownFolderPath

时间:2009-01-27 17:00:19

标签: .net pinvoke

编辑:Scotty2012和David Morton的答案对我不起作用,所以我对这个问题给予了赏金。我认为我需要在传递之前将字符串的类型更改为其他内容。

我对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 个答案:

答案 0 :(得分:2)

我认为这应该适用于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\\");

答案 1 :(得分:2)

尝试使用此代码。很抱歉这个长度,但是所有这些都需要正确地启动这个特定的功能。它是一个简单的控制台应用程序,包括函数的定义和SHGetKnownFolderPath的示例用法。

我继续前进,包括KNOWN_FOLDER_FLAG的定义以及文件夹ID的一些定义。所有文件夹Id实际上只是GUID。可以在%ProgramFiles%\ Windows SDK \ v6.0A \ Include \ KnownFolders.h中找到所有可能的ID,并以与我在示例中添加的方式相同的方式添加。

我包含了几个包装函数,它们隐藏了调用特定函数的所有邪恶的marashal'ing细节。

如果您有任何特定的文件夹ID或说明,请添加评论,我会更新示例。

编辑纠正了SHSetKnownFolderPath编组中的错误。我没有向String值添加MarshalAs标记,它默认为ANSI字符串。 API需要unicode。 SHSetFolderFunction现在可以使用(使用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 :(得分:0)

这将是宣言:

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

你需要创建一个StringBuilder,将最大路径260传递给构造函数(对于Vista / XP来说也是如此。)这是一个stringbuilder,它将为你正在尝试的文件夹提供新目录。设置,所以将您的文本附加到StringBuilder中以获取新位置。但是,实现的最大问题是csidl参数与Windows中指定的Guid不同。这些值实际上是shlobj.h中声明的值。按照链接查看最常传入的值.hToken应始终为IntPtr.Zero,除非您有指向特定用户的指针,而您正尝试更改此值。 IntPtr.Zero将使用当前用户。 dwFlags应始终设置为0.