To get it out the way: I understand there are different ways to go about this such as Get-ChildItem.
So, I have a custom class I'm defining to get rid of some of the overhead PowerShell cmdlets have, as well as some .Net classes. The function will run just fine locally, but as soon as I try using it as scriptblock definition with Invoke-Command against a remote computer, it will just hang; even if I invoke it against my own computer. There is a process that's created for a WinRM Plugin that is shown in Task Manager but, that's it. Here are some working examples:
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
As shown above, this will work just fine and return the sum of all files. Then, when I pass a computer name to Invoke-Command for remote execution - it just hangs:
Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList C:\Users\Abraham -ComputerName $env:COMPUTERNAME
It goes without saying that a PSSession doesn't work either; this will be the primary method of running the function - passing it to an open PSSession.
My question is, what the hell is wrong? lol. Is there something going on behind the scenes that won't allow the use of P/Invoke remotely?
Here is the actual function:
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 }
}
}
EDIT: Update - So, it works on subfolders, but not the root folder. Example:
$path = 'C:\Users\Abraham\Desktop' #works
Invoke-Command -ScriptBlock ${Function:Get-FolderSize} -ArgumentList $path -ComputerName $env:COMPUTERNAME
...works, but the root folder C:\Users\Abraham doesn't.
Note: Passing the UNC path to the function/method will work.