透過P/Invoke定義的類別是否不允許遠端執行?

透過P/Invoke定義的類別是否不允許遠端執行?

為了解決這個問題:我知道有不同的方法可以解決這個問題,例如Get-ChildItem

因此,我定義了一個自訂類,以消除 PowerShell cmdlet 的一些開銷以及一些 .Net 類別。該函數在本地運行得很好,但是一旦我嘗試將它用作Invoke-Command遠端電腦的腳本塊定義,它就會掛起;即使我對我自己的計算機調用它。有一個為WinRM 插件這顯示在任務管理器中,但僅此而已。以下是一些工作範例:

PS C:\Users\Abraham> Get-FolderSize -Path C:\Users\Abraham
143.98GB

PS C:\Users\Abraham> Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList C:\Users\Abraham
143.98GB

如上所示,這將正常工作並傳回所有文件的總和。然後,當我將電腦名稱傳遞Invoke-Command給遠端執行時 - 它只是掛起:

Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList C:\Users\Abraham -ComputerName $env:COMPUTERNAME

不言而喻,aPSSession也不起作用;這將是運行該函數的主要方法 -將其傳遞給一個開放的PSSession

我的問題是,到底出了什麼問題?哈哈。是否有幕後發生的事情不允許使用P/調用遠程?

這是實際的功能:

Function Get-FolderSize ([parameter(Mandatory)][string]$Path) {
    Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace ProfileMethods
{
    public class DirectorySum
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct WIN32_FIND_DATA
        {
            public uint dwFileAttributes;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
            public uint nFileSizeHigh;
            public uint nFileSizeLow;
            public uint dwReserved0;
            public uint dwReserved1;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string cFileName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
            public string cAlternateFileName;
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

        [DllImport("kernel32.dll")]
        private static extern bool FindClose(IntPtr hFindFile);

        public long GetFolderSize(string path)
        {
            long size = 0;
            List<string> dirList = new List<string>();
            WIN32_FIND_DATA fileData;
            IntPtr hFile = FindFirstFile(path + @"\*.*", out fileData);
            if (hFile != IntPtr.Zero)
            {
                do
                {
                    if (fileData.cFileName == "." || fileData.cFileName == "..")
                    {
                        continue;
                    }
                    string fullPath = path + @"\" + fileData.cFileName;
                    if ((fileData.dwFileAttributes & 0x10) == 0x10)
                    {
                        dirList.Add(fullPath);
                    }
                    else
                    {
                        size += ((long)fileData.nFileSizeHigh * (long)uint.MaxValue + (long)fileData.nFileSizeLow);
                    }
                } while (FindNextFile(hFile, out fileData));
                FindClose(hFile);
                foreach (string dir in dirList)
                {
                    size += GetFolderSize(dir);
                }
            }
            return size;
        }
    }
}
"@
    $program = [ProfileMethods.DirectorySum]::new()
    switch ($program.GetFolderSize($Path))
    {
        {$_ -lt 1GB} { '{0}MB' -f [math]::Round($_/1MB,2); Continue }
        {$_ -gt 1GB -and $_ -lt 1TB} { '{0}GB' -f [math]::Round($_/1GB,2); Continue }
        {$_ -gt 1TB} { '{0}TB' -f  [math]::Round($_/1TB,2); Continue }
    }
}

編輯:更新- 因此,它適用於子資料夾,但不適用於根資料夾。例子:

$path = 'C:\Users\Abraham\Desktop' #works
Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList $path -ComputerName $env:COMPUTERNAME

....有效,但根資料夾C:\Users\Abraham無效。


筆記:將 UNC 路徑傳遞給函數/方法將會起作用。

答案1

這是一個猜測,因為您還沒有嘗試調查任何事情,因此沒有足夠的資訊來提供準確的答案。

但根資料夾 C:\Users\Abraham 沒有。

if ((fileData.dwFileAttributes & 0x10) == 0x10)

現代 Windows 系統可能需要額外的檢查 – 您需要注意重解析點,它們有多種類型(符號連結、連接點、掛載點、OneDrive 佔位符...),除了「ReparsePoint」標誌(0x400)之外,還可能具有「Directory」標誌。

特別是,當Vista 將舊的“~\Local Settings”目錄移至~\AppData\Local 時,為了相容性,它在“~\AppData\Local\Application Data”處放置了一個目錄連接,該目錄指向……回到相同的「 ~\AppData\Local」。

連接點比符號連結稍微特殊一些,並且可以有自己的 ACL,因此通常該連接點將有一個拒絕 ACE 來阻止您執行操作FindFirstFile(@"Application Data\*.*")(即,它只允許直接存取已知路徑)。但是,如果其上的 ACL 曾經被重置,則任何嘗試列舉 AppData 內容的程式都會最終陷入無限循環,永遠下降到同一個連接點(直到超出路徑長度限制)。

FindFirstFile(path + @"\*.*", out fileData);

.請記住,檔案名稱中不需要包含。 FindFirstFile() 故意使這項工作(透過允許.*匹配空字串)來幫助仍停留在「8.3 filename.ext」時代的程序,但對於實現自己的通配符擴展的程序來說,情況並非如此。

相關內容