简体   繁体   English

P/调用 SHSetKnownFolderPath 失败

[英]P/Invoke SHSetKnownFolderPath Failing

I thought this would be a simple enough script.我认为这将是一个足够简单的脚本。 I came across this article about how to use powershell to redirect folders to OneDrive here: https://stealthpuppy.com/onedrive-intune-folder-redirection/我在这里看到这篇关于如何使用 powershell 将文件夹重定向到 OneDrive 的文章: https://stealthpuppy.com/onedrive-intune-folder-redirection/

I realized this script would not work for me, as the way it is written it requires user context and cannot be run silently using Task Scheduler, so I set out to try and "rewrite" the script using a system context.我意识到这个脚本对我不起作用,因为它的编写方式需要用户上下文并且不能使用 Task Scheduler 静默运行,所以我开始尝试使用系统上下文“重写”脚本。 All seems to be working except for one chunk that I can't seem to understand.除了我似乎无法理解的一大块之外,一切似乎都在工作。 I think it's C# and I'm only familiar with powershell. I am only concerned with redirecting the downloads folder, so his is what I have.我认为它是 C#,我只熟悉 powershell。我只关心重定向下载文件夹,所以他就是我的。

#These are the potential GUIDs for the Downloads KFM path of the user
$Guids = $null
$Guids = @()
$Guids += [pscustomobject]@{
        Guid = '{374DE290-123F-4565-9164-39C4925E467B}'
        Paths = @()
        }
$Guids += [pscustomobject]@{
        Guid = '{7d83ee9b-2244-4e70-b1f5-5393042af1e4}'
        Paths = @()
        }

#Log Variables
$timestamp = Get-Date -Format o | ForEach-Object { $_ -replace ":", "." }
$LogPath = "$env:USERPROFILE\AppData\Local\Intune-PowerShell-Logs\OneDriveSync-$timestamp.txt"

Start-Transcript -Path $LogPath

$CurrentUsers = Get-ItemProperty -path  "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" | Where {($_.ProfileImagePath -notmatch '.NET') -and ($_.ProfileImagePath -notmatch 'Default') -and ($_.ProfileImagePath -match "Users")} | select ProfileImagePath,PSChildName

ForEach ($CurrentUser in $CurrentUsers)
{
    $SourcePath = "$($CurrentUser.ProfileImagePath)\Downloads"
    $Targetpath = "$($CurrentUser.ProfileImagePath)\OneDrive\Downloads"
    $Rootpath = "HKU:\$($CurrentUser.PSChildName)"
    $ShellFolderspath = "$RootPath\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
    $UserShellFolderspath = "$Rootpath\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
    
    #region 1 - Identify registry keys to update
    New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue
    #Adding Downloads Guids to array
    ForEach ($object in $Guids)
    {
        #Check Shell Folders Path
        If ($Item = Get-ItemProperty $ShellFolderspath -Name $object.Guid -ErrorAction SilentlyContinue)
        {
        "$($object.guid) found in Shell Folders path"
        $Object.paths += @($Item.PSpath)
        }
        
        #Check User Shell Folders Path
        If ($Item = Get-ItemProperty $UserShellFolderspath -Name $object.Guid -ErrorAction SilentlyContinue)
        {
        "$($object.guid) found in User Shell Folders path"
        $Object.paths += @($Item.PSpath)
        }
    }
    #endregion
    
#region 2
# Define SHSetKnownFolderPath if it hasn't been defined already
$Type = ([System.Management.Automation.PSTypeName]'KnownFolders').Type
If (-not $Type) 
{
$Signature = @'
[DllImport("shell32.dll")]
public extern static int SHSetKnownFolderPath(ref Guid folderId, uint flags, IntPtr token, [MarshalAs(UnmanagedType.LPWStr)] string path);
'@
$Type = Add-Type -MemberDefinition $Signature -Name 'KnownFolders' -Namespace 'SHSetKnownFolderPath' -PassThru
}

#Test if new directory exists
If (!(Test-Path -Path $Targetpath -PathType Container))
{
    New-Item -Path $TargetPath -Type Directory -Force
}
#Validate the path
If (Test-Path $TargetPath -PathType Container) 
{
    #Call SHSetKnownFolderPath
    #return $Type::SHSetKnownFolderPath([ref]$KnownFolders[$KnownFolder], 0, 0, $Path)
    ForEach ($object in $Guids) 
    {
        $result = $Type::SHSetKnownFolderPath([ref]$object.guid, 0, 0, $Targetpath)
        If ($result -ne 0) 
        {
            $errormsg = "Error redirecting $($Object.guid). Return code $($result) = $((New-Object System.ComponentModel.Win32Exception($result)).message)"
            Throw $errormsg
        }
    }
} 
Else 
{
    Throw New-Object System.IO.DirectoryNotFoundException "Could not find part of the path $Path."
}

#endregion

#region 3 - Set new downloads directory
ForEach ($Object in $Guids)
{
    ForEach ($path in $object.paths)
    {
        Set-ItemProperty -Path $path -Name $object.Guid -Value $Targetpath -Verbose
    }
}
#endregion

#region 4
#Move files from old directory to the new one
#Robocopy.exe "$SourcePath" "$Targetpath" /E /MOV /XJ /XF *.ini /R:1 /W:1 /NP
#endregion
#}
}
    
    Stop-Transcript

All seems to work except that I get an error on line 74 that says, "Error redirecting {374DE290-123F-4565-9164-39C4925E467B}. Return code -2147024894 = The system cannot find the file specified"一切似乎都正常,除了我在第 74 行收到一条错误消息,“错误重定向 {374DE290-123F-4565-9164-39C4925E467B}。返回代码 -2147024894 = 系统找不到指定的文件”

I have spent almost an entire work day trying to tackle this problem and am getting nowhere.我几乎花了一整天的时间来解决这个问题,但一无所获。 Any help would be appreciated!任何帮助,将不胜感激!

You have two problems:你有两个问题:

  • The immediate, technical problem: The system cannot find the file specified implies that the specified KNOWNFOLDERID GUID is not recognized.直接的技术问题: The system cannot find the file specified意味着无法识别指定的KNOWNFOLDERID GUID。

    • That said, among your two GUIDs it is {7d83ee9b-2244-4e70-b1f5-5393042af1e4} , not the one mentioned in your error message, {374DE290-123F-4565-9164-39C4925E467B} (the Downloads folder), that is unknown.也就是说,在您的两个 GUID 中,它是{7d83ee9b-2244-4e70-b1f5-5393042af1e4} ,而不是错误消息中提到的{374DE290-123F-4565-9164-39C4925E467B} (下载文件夹),这是未知的.
  • A conceptual problem: You're trying to call SHSetKnownFolderPath for specific users , which requires that you pass a so-called access token representing the user of interest as the hToken argument.一个概念性问题:您正在尝试为特定用户调用SHSetKnownFolderPath ,这要求您传递一个代表感兴趣用户的所谓访问令牌作为hToken参数。

    • Since you're passing 0 for hToken , you're always updating for the current user.由于您为hToken传递了0 ,因此您总是在为当前用户更新。

If you want to avoid the additional complexity of obtaining access tokens representing other users via the LogonUser WinAPI function , you can bypass SHSetKnownFolderPath calls and write directly to the registry, as your code in part already does;如果您想避免通过LogonUser WinAPI function获取代表其他用户的访问令牌的额外复杂性,您可以绕过SHSetKnownFolderPath调用并直接写入注册表,因为您的部分代码已经这样做了; however, note that while that should work as of this writing, doing so isdiscouraged and may at some point in the future stop working.但是,请注意,虽然在撰写本文时这应该有效,但不鼓励这样做,并且可能在将来的某个时候停止工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM