3

In Windows default settings, the pagefile size is set to Automatically manage paging file size for all drives.

This project is quite special. It is not maintained by operators. These computers are only provided for internal personnel to handle business or learn business processes. These computers have no fixed users, they are placed in public areas. When they have problems, the person on duty will reinstall the system using the fixed ghost file and execute the script to optimize the system with one click. These devices are old, and there are some thin terminals with 4G memory and 64G hard disk. Before long, new devices will replace them and have new management solutions. As a temporary transitional scheme before replacing the new scheme, I think providing a simple "optimization" program will be the simplest way to deal with it at present.

I want to change the page file size to 20% - 50% of the physical memory through C# .net-6.0 like this(Test Computer's Physical memory is 32 GB)

I checked some information and it seems that wmic can meet my needs.

wmic COMPUTERSYSTEM set AutomaticManagedPagefile=false

wmic PAGEFILESET where name ="C:\\pagefile.sys" set InitialSize=1638,MaximumSize=4095

I tested these two lines of commands on Windows10 2019 LTSC, and they worked well.But when I use code to execute,I have some problems.Here is my code:

        internal static void ExecuteCmd(string command)
        {
            try
            {
                Process process = new();
                ProcessStartInfo startInfo = new()
                {
                    WindowStyle = ProcessWindowStyle.Hidden,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    FileName = "cmd.exe",
                    Arguments = "/c " + command,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
            };
                process.StartInfo = startInfo;
                process.Start();
            }
            catch(Exception e)
            {
                LogHelper.Error(e.Message);
            }
        }

        internal static void ChangeSystemPageFileSize()
        {
            string? TotalPhysicalMemory = "0";
            string? InitialSize = "0";
            string? MaximumSize = "0";

            try
            {
                ManagementObjectSearcher Search = new ManagementObjectSearcher();
                Search.Query = new ObjectQuery("Select * From Win32_ComputerSystem");
                foreach (ManagementObject obj in Search.Get().Cast<ManagementObject>())
                {
                    TotalPhysicalMemory = $"{Math.Round(Convert.ToDouble(obj["TotalPhysicalMemory"]) / (1024 * 1024))}";

                    if (!string.IsNullOrWhiteSpace(TotalPhysicalMemory))
                    {
                        break;
                    }
                }
                InitialSize = Math.Floor(int.Parse(TotalPhysicalMemory) * 0.2).ToString().Trim();
                MaximumSize = Math.Floor(int.Parse(TotalPhysicalMemory) * 0.5).ToString().Trim();
                CommandHelper.ExecuteCmd("wmic COMPUTERSYSTEM set AutomaticManagedPagefile=false");
                CommandHelper.ExecuteCmd("wmic PAGEFILESET where name =\"C:\\\\pagefile.sys\" set InitialSize=" + InitialSize +",MaximumSize=" + MaximumSize);
            }
            catch (Exception e)
            {
                LogHelper.Error(e.Message);
            }
                

I have obtained the correct physical memory size, but only the first command executed takes effect.When computer restart,I get windows settings like this.

Thanks @ProgrammingLlama @user9938 I updated the page file size setting through WMI. There may be a little problem:

                InitialSize = (UInt32)Math.Floor(TotalPhysicalMemory * 0.2);
                MaximumSize = (UInt32)Math.Floor(TotalPhysicalMemory * 0.5);
                ManagementObject ComputerSystem = new($@"ROOT\CIMV2:Win32_ComputerSystem.Name='{Environment.MachineName}'");
                ComputerSystem["AutomaticManagedPagefile"] = false;
                ComputerSystem.Put();
                ManagementObject PageFileSetting = new($@"ROOT\CIMV2:Win32_PageFileSetting");
                PageFileSetting.SetPropertyValue("Name", "C:\\PAGEFILE.SYS");
                PageFileSetting.SetPropertyValue("InitialSize", InitialSize);
                PageFileSetting.SetPropertyValue("MaximumSize", MaximumSize);
                PageFileSetting["Name"] = "C:\\PAGEFILE.SYS";
                PageFileSetting["InitialSize"] = InitialSize;
                PageFileSetting["MaximumSize"] = MaximumSize;
                PageFileSetting.Put();

Neither the SetPropertyValue method nor the Put method can update the value of InitialSize MaximumSize Name

Maybe someone can give me some advice?

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
Jerry Dora
  • 53
  • 5
  • 5
    It hurts me inside every time I see someone launch cmd.exe /c from C#... why? why not just launch the program you're using this to launch? – ProgrammingLlama Oct 24 '22 at 03:53
  • @ProgrammingLlama I didn't catch your meaning. Don't use /c? If /c is not used, the execution will be stuck – Jerry Dora Oct 24 '22 at 04:02
  • You want to run a program. So you run a program that runs other programs.... Why bother with the middleman? – Jeremy Lakeman Oct 24 '22 at 04:07
  • 2
    cmd.exe is a program, you're using it to launch a program (wmic.exe). So you have a program launching a program, to launch another program. My question is why don't you simply launch wmic.exe from C#? – ProgrammingLlama Oct 24 '22 at 04:11
  • 3
    As for your problem, I think it might be a typo: `"C:\\\\pagefile.sys"` becomes `C:\\pagefile.sys`. Also, have you exhausted the options available to you in the System.Management and Microsoft.Management.Infrastructure namespaces in .NET? I'd be surprised if there isn't a more built-in .NET way of doing this. – ProgrammingLlama Oct 24 '22 at 04:13
  • 2
    According to [wmic](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmic): _The WMI command-line (WMIC) utility is deprecated as of Windows 10, version 21H1, and as of the 21H1 semi-annual channel release of Windows Server._ – Tu deschizi eu inchid Oct 24 '22 at 04:35
  • 3
    What is the use case for this? As a general rule, programs should avoid messing around with internal OS settings, since the probability to cause problems goes way up. To me, that sounds like something that should be an instruction in a setup guide, configured with group policies, or *maybe* a script launched by an installer. – JonasH Oct 24 '22 at 06:49
  • What @JonasH said -- additionally, assuming the pagefile is on the C:\ drive (and only there) is an unwarranted assumption if this code is intended to run anywhere. If it's only supposed to go as far as your own domain, managing it through group policies or user profile scripts seems like a much saner idea, and definitely easier than invoking `wmic` in C# code. If it's supposed to go further than that... well, please tell me what your program's name is so I can be sure to avoid it. :P – Jeroen Mostert Oct 24 '22 at 10:42
  • Thank you @ProgrammingLlama I'm not very familiar with Windows。Now I use WMI Win32_ComputerSystem and Win32_PageFileSetting to finish this.Although there are still some questions, thank you for your help – Jerry Dora Oct 24 '22 at 10:49
  • @JeroenMostert@JonasH This is a secure batch program for the intranet. It will only be used in individual special projects, and it needs to be changed manually. – Jerry Dora Oct 24 '22 at 10:52
  • 2
    So, if it needs to be changed manually, why do you want your program to do it? And if it is only for the intranet, why not manage it thru the IT infrastructure designed for such configuration? And what does "Secure batch program" even mean? – JonasH Oct 24 '22 at 14:05
  • If disk space is a concern, the following may be of interest: [Clean Up the WinSxS Folder](https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/clean-up-the-winsxs-folder?view=windows-10), [Repair a Windows Image](https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/repair-a-windows-image?view=windows-10), and [Restore Points](https://learn.microsoft.com/en-us/windows/win32/sr/restore-points). – Tu deschizi eu inchid Oct 25 '22 at 13:19

1 Answers1

4

The following shows how to use ManagementObject to change the settings for the Windows page file (on the OS partition).

Note: The code below has had limited testing. It's recommended to conduct your own testing to ensure that the code functions as desired. While the code seems to set the PageFile sizes for the OS drive, there may be alternative (more desirable) methods of setting these values. Additionally, prior to running the code below ensure that you Create a System Restore Point. Also, ensure that you've performed a backup of your computer.

According to Win32_ComputerSystem class:

TotalPhysicalMemory

Total size of physical memory. Be aware that, under some circumstances, this property may not return an accurate value for the physical memory. For example, it is not accurate if the BIOS is using some of the physical memory. For an accurate value, use the Capacity property in Win32_PhysicalMemory instead.


Download/install NuGet package: System.Management

Add Application Manifest File:

  • In VS menu, click Project
  • Select Add New Item...
  • Select Application Manifest File (name: app.manifest)
  • Click Add

Open Solution Explorer:

  • In VS menu, click View
  • Select Solution Explorer

Modify requestedExecutionLevel:

  • In Solution Explorer, right-click app.manifest and select Open

Change From:

<requestedExecutionLevel  level="asInvoker" uiAccess="false" />

Change To:

<requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />

It appears that the data for PagingFiles can be modified by the following:

Add the following using directives

  • using System.Management;
  • using Microsoft.Win32;
  • using System.Diagnostics;

Code:

public UInt64 GetTotalMemoryInMB()
{
    UInt64 totalPhysicalMemoryInBytes = 0;

    using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select Capacity from Win32_PhysicalMemory"))
    {
        foreach (ManagementObject obj in searcher.Get())
        {
            if (obj == null)
                continue;

            totalPhysicalMemoryInBytes += obj["Capacity"] is null ? 0 : Convert.ToUInt64(obj["Capacity"]);
        }
    }

    return totalPhysicalMemoryInBytes / 1024 / 1024;
}

public string SetPageFileWmi(uint initialSizePercentage, uint maximumSizePercentage)
{
    StringBuilder sb = new StringBuilder();

    //get Windows folder
    string? winDir = Environment.GetEnvironmentVariable("windir");

    //get drive letter that OS is installed on
    string winDriveLetter = winDir?.Substring(0, 2) is null ? String.Empty : winDir.Substring(0, 2); //ex: C:
    
    //get total physical memory
    UInt64 totalPhysicalMemory = GetTotalMemoryInMB();

    sb.AppendLine($"Total Physical Memory: {totalPhysicalMemory}");

    //calculate initial size
    UInt32 initialSize = Convert.ToUInt32(Math.Floor(totalPhysicalMemory * (initialSizePercentage / 100.0))); //ex: 20% / 100.0 = 0.2
    initialSize = initialSize < 16 ? 16 : initialSize; //initial size needs to be >= 16 MB

    //calculate maximum size
    UInt32 maximumSize = Convert.ToUInt32(Math.Floor(totalPhysicalMemory * (maximumSizePercentage / 100.0))); //ex: 50% / 100.0 = 0.5

    using (ManagementObject obj = new ManagementObject($@"ROOT\CIMV2:Win32_ComputerSystem.Name='{Environment.MachineName}'"))
    {
        //set value
        obj.SetPropertyValue("AutomaticManagedPagefile", false);

        //commit
        obj.Put(new PutOptions() { Type = PutType.UpdateOnly });
    }

    using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select Name from Win32_PageFile"))
    {
        foreach (ManagementObject obj in searcher.Get())
        {
            string? pfName = obj["Name"]?.ToString();

            Debug.WriteLine($"pfName: '{pfName}'");

            //only get the page file for the OS drive
            if (!String.IsNullOrEmpty(pfName) && pfName.ToLower().StartsWith(winDriveLetter.ToLower()))
            {
                sb.AppendLine($"Name: {pfName}");
                sb.AppendLine($"InitialSize: {initialSize}");
                sb.AppendLine($"MaximumSize: {maximumSize}");
                sb.AppendLine("");

                using (ManagementObject obj2 = new ManagementObject($@"ROOT\CIMV2:Win32_PageFileSetting.Name='{pfName}'"))
                {
                    //set value
                    //obj2.SetPropertyValue("Name", pfName);
                    obj2.SetPropertyValue("InitialSize", initialSize);
                    obj2.SetPropertyValue("MaximumSize", maximumSize);

                    //commit
                    obj2.Put(new PutOptions() { Type = PutType.UpdateOrCreate });
                }
            }
        }
    }

    return sb.ToString();
}

Usage

string result = SetPageFileWmi(20, 50);

According to Changing the Location of the Pagefile, the registry location is HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\.


Update:

Here's a method to reset to the default setting:

public static string SetPageFileDefaultWmi()
{
    using (ManagementObject obj = new ManagementObject($@"ROOT\CIMV2:Win32_ComputerSystem.Name='{Environment.MachineName}'"))
    {
        obj.SetPropertyValue("AutomaticManagedPagefile", true);
        obj.Put(new PutOptions() { Type = PutType.UpdateOnly });
    }

    return "Paging file set to default.";
}

Resources:

Additional Resources:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • 2
    has a very good (and detailed) suggestion - I would (for the sanity of our OPS people) would like to opt NOT TO DO THIS - operational people do not like this stuff – riffnl Oct 24 '22 at 23:28
  • @riffnl: Is your comment meant to discourage setting a custom page file size, or to discourage setting a custom page file size in this manner? My post doesn't address whether or not setting a custom page file size is a good idea, but rather shows that it's possible to do so using `ManagementObject`. – Tu deschizi eu inchid Oct 25 '22 at 02:46
  • @user9938 Thanks, it works. It is very detailed and runs perfectly on Windows 10 2004 and Windows 2019 LTSC. – Jerry Dora Oct 25 '22 at 02:54
  • @riffnl I know your concern. Operators usually hate the program to modify user-defined settings, which can be very annoying when problems or accidents occur. But our scenario is special. I have put forward specific scenarios in the question. Thank you for your reminding. – Jerry Dora Oct 25 '22 at 03:37