Разве классы, определенные с помощью 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...) и могут также иметь флаг «Directory» в дополнение к флагу «ReparsePoint» (который равен 0x400).

В частности, поскольку 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», но это не будет иметь места для программ, реализующих собственное расширение подстановочных знаков.

Связанный контент