1

编辑:Scotty2012 和大卫莫顿的答案对我不起作用,所以我对这个问题给予了赏金。我想我需要在传入之前将字符串的类型更改为其他类型。

我在 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

谢谢!

4

3 回答 3

3

试试这个代码。抱歉冗长,但正确 PInvoke 这个特定功能都需要它。它是一个简单的控制台应用程序,包括两个函数的定义和 SHGetKnownFolderPath 的示例用法。

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

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

如果您想要任何特定的文件夹 ID 或解释,请添加评论,我将更新示例。

编辑更正了 SHSetKnownFolderPath 编组中的一个错误。我没有向字符串值添加 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
于 2009-02-01T21:59:02.977 回答
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\\");
于 2009-01-27T17:29:33.450 回答
0

这将是声明:

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

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

于 2009-01-27T17:30:37.270 回答