【问题标题】:P/Invoke SHSetKnownFolderPath FailingP/Invoke SHSetKnownFolderPath 失败
【发布时间】:2021-02-15 20:41:48
【问题描述】:

我认为这将是一个足够简单的脚本。我在这里看到了这篇关于如何使用 powershell 将文件夹重定向到 OneDrive 的文章:https://stealthpuppy.com/onedrive-intune-folder-redirection/

我意识到这个脚本对我不起作用,因为它的编写方式需要用户上下文并且不能使用任务计划程序静默运行,所以我开始尝试使用系统上下文“重写”脚本。除了我似乎无法理解的一大块之外,一切似乎都在工作。我认为它是 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

除了我在第 74 行收到一条错误消息外,一切似乎都有效, “重定向 {374DE290-123F-4565-9164-39C4925E467B} 时出错。返回码 -2147024894 = 系统找不到指定的文件”

我几乎花了一整天的时间试图解决这个问题,但毫无进展。任何帮助将不胜感激!

【问题讨论】:

  • 当我执行$CurrentUsers = Get-ItemProperty -path "HKLM:\SOFTWARE\Microsoft\... 命令时,我会返回 3 个配置文件,其中 2 个不是我的活动帐户,甚至不再是我计算机上的用户。我确定如果我运行脚本,当处理那些额外的$CurrentUser 对象时会出错。也许这就是你正在经历的?
  • 这很有趣......我只获得了我的活动个人资料,所以这并不能解释我这边的错误。我的目标是缩小任何实际用户配置文件的范围,因为我们的某些机器可能有多个用户帐户,而 NT SYSTEM 帐户无法知道哪个帐户是活动帐户。

标签: windows powershell known-folders


【解决方案1】:

你有两个问题:

  • 直接的技术问题:The system cannot find the file specified 表示指定的KNOWNFOLDERID GUID 无法识别。

    • 也就是说,在您的两个 GUID 中,它是 {7d83ee9b-2244-4e70-b1f5-5393042af1e4},而不是您的错误消息中提到的 {374DE290-123F-4565-9164-39C4925E467B}(下载文件夹),这是未知的。
  • 一个概念性问题:您尝试为特定用户调用SHSetKnownFolderPath,这要求您传递一个代表感兴趣用户的所谓访问令牌作为hToken 参数。

    • 由于您将0 传递给hToken,因此您始终为当前 用户进行更新。

如果您想避免通过LogonUser WinAPI function 获取代表其他用户的访问令牌的额外复杂性,您可以绕过SHSetKnownFolderPath 调用并直接写入注册表,就像您的代码部分已经做的那样;但是,请注意,虽然这在撰写本文时应该有效,但这样做是 discouraged 并且可能在将来的某个时候停止工作。

【讨论】:

  • 抱歉,我是 Stack Overflow 的新手。这很有帮助,但它最终没有实际意义,因为我作为登录用户运行并使用用户上下文的其他脚本似乎正在工作。如果我需要以 SYSTEM 帐户执行此功能,我一定会参考这个。
  • @Wumbowarrior:不用担心,感谢您的接受。很高兴听到您找到了解决方案。实际上,在手头的情况下,您不会对 SYSTEM 帐户使用LogonUser 函数,而是模拟一个特定用户(这样做需要管理员权限)。
猜你喜欢
  • 2014-02-24
  • 2016-07-15
  • 1970-01-01
  • 2011-10-15
  • 1970-01-01
  • 2015-02-12
  • 2023-03-03
  • 2021-07-22
  • 2020-07-11
相关资源
最近更新 更多