P/Invoke を通じて定義されたクラスはリモートで実行できないのですか?

P/Invoke を通じて定義されたクラスはリモートで実行できないのですか?

それを邪魔にならないようにするために:これには次のようなさまざまな方法があることは理解していますGet-ChildItem

そこで、PowerShell コマンドレットのオーバーヘッドの一部と .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

言うまでもなく、aもPSSession機能しません。これが関数を実行する主な方法になります。オープンに渡す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 を持つことができるため、通常、このジャンクションには、実行を阻止する Deny ACE がありますFindFirstFile(@"Application Data\*.*")(つまり、既知のパスへの直接アクセスのみを許可します)。ただし、ジャンクションの ACL がリセットされた場合、AppData の内容を列挙しようとするプログラムは、無限ループに陥り、パスの長さの制限に達するまで、同じジャンクションに永遠に陥ります。

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

ファイル名には が含まれている必要がないことに注意してください.。FindFirstFile() は、.*まだ「8.3 filename.ext」時代にとどまっているプログラムを支援するために、意図的にこれを機能させます (空の文字列に一致できるようにすることで)。ただし、独自のワイルドカード拡張を実装しているプログラムの場合はそうではありません。

関連情報