windowspowershellairplane-mode

How to Toggle Airplane Mode in Windows Using PowerShell?


I want to toggle airplane mode in Windows programmatically using PowerShell. After some research, I found that this can be done through COM interfaces and native Windows APIs. Below is a script I’ve written that successfully toggles airplane mode using P/Invoke.


Solution

  • Below is a PowerShell script that toggles airplane mode in Windows using COM interfaces and native Windows APIs.

    # script for toggling the airplane mode in Windows
    # carsten.giese@googlemail.com
    
    cls
    remove-variable * -ea 0
    $ErrorActionPreference = 'stop'
    
    Add-Type -TypeDefinition @'
    using System;
    using System.Runtime.InteropServices;
    
    public static class NativeMethods {
        [DllImport("ole32.dll")]
        public static extern int CoInitialize(IntPtr pv);
        [DllImport("ole32.dll")]
        public static extern void CoUninitialize();
        [DllImport("ole32.dll")]
        public static extern uint CoCreateInstance(Guid clsid, IntPtr pv, uint ctx, Guid iid, out IntPtr ppv);
    }
    
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int GetSystemRadioStateDelegate(IntPtr cg, out int ie, out int se, out int p3);
    
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int SetSystemRadioStateDelegate(IntPtr ptr, int state);
    
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int ReleaseDelegate(IntPtr ptr);
    '@
    
    $CLSID = '581333F6-28DB-41BE-BC7A-FF201F12F3F6'
    $IID   = 'DB3AFBFB-08E6-46C6-AA70-BF9A34C30AB7'
    $CRID  = '73726163-6574-676e-6965-736531333131'
    $mrs   = [System.Runtime.InteropServices.Marshal]
    
    try {
        $irm  = 0
        $null = [NativeMethods]::CoInitialize(0)
        $null = [NativeMethods]::CoCreateInstance($CLSID, 0, 4, $IID, [ref]$irm)
    
        $comPtr  = $mrs::ReadIntPtr($irm)
        $methodPtr  = [IntPtr[]]::new(8)
        $mrs::Copy($comPtr, $methodPtr, 0, $methodPtr.Length)
    
        $getState = $mrs::GetDelegateForFunctionPointer($methodPtr[5], [GetSystemRadioStateDelegate])
        $setState = $mrs::GetDelegateForFunctionPointer($methodPtr[6], [SetSystemRadioStateDelegate])
        $release  = $mrs::GetDelegateForFunctionPointer($methodPtr[2], [ReleaseDelegate])
    
        # get the current airplane mode:
        $oldState, $p2, $p3 = (0,0,0)
        $null = $getState.Invoke($irm, [ref]$oldState, [ref]$p2, [ref]$p3)
    
        # toggle the state of the airplane mode (# 0=airplane_mode, 1=normal_mode):
        $newState = !$oldState
        $null = $setState.Invoke($irm, $newState)
        $null = $release.Invoke($irm)
    }
    finally {
        $null = [NativeMethods]::CoUninitialize()
    }
    

    This PowerShell script toggles airplane mode in Windows by interacting directly with COM interfaces and native Windows APIs using P/Invoke. Below is a breakdown of how the script works and why specific elements are necessary.


    Explanation of the Code Logic

    1. Initialization and Setup:

      • The script defines required COM methods and delegates using Add-Type to include C# code. It initializes the COM library using CoInitialize and creates an instance of the COM object with CoCreateInstance.
    2. Method Resolution with $methodPtr:

      • $methodPtr is an array of pointers extracted from the COM object’s virtual function table (vtable). This table contains the memory addresses of the object’s methods.
      • Using Marshal.Copy, the function pointers are copied into $methodPtr, making individual methods accessible by their index.
    3. Using $methodPtr Methods:

      • $methodPtr[5]: Calls GetSystemRadioState to get the current airplane mode status.
      • $methodPtr[6]: Calls SetSystemRadioState to set (or toggle) the airplane mode.
      • $methodPtr[2]: Calls Release to release the COM object after use.
    4. List of Methods in the vtable:
      The COM object exposes the following methods via $methodPtr:

      • Method 0: QueryInterface (internal for COM object communication).
      • Method 1: AddRef (increments reference count).
      • Method 2: Release (decrements reference count, frees resources).
      • Method 5: GetSystemRadioState (fetches current airplane mode state).
      • Method 6: SetSystemRadioState (toggles or sets airplane mode).
      • Other indices likely represent additional internal or reserved methods.
    5. Toggling Airplane Mode:

      • The script retrieves the current state of airplane mode (0 for enabled, 1 for disabled) using the GetSystemRadioState delegate.
      • It toggles the state by negating the current value and applies the new state using the SetSystemRadioState delegate.
    6. Cleanup:

      • The COM object is released using the Release method, and CoUninitialize is called to clean up the COM environment.

    Why Is [UnmanagedFunctionPointer] Required?

    The [UnmanagedFunctionPointer] attribute is critical because it allows the script to define managed delegates that represent unmanaged function pointers. These are necessary to call methods from the COM object’s vtable.

    1. Interfacing with Native Code:

      • COM methods in the vtable are implemented in native code and cannot be directly called from managed .NET methods.
      • The delegates with [UnmanagedFunctionPointer] act as a bridge, enabling managed code to interact with unmanaged function pointers.
    2. Specifying Calling Conventions:

      • The CallingConvention.StdCall ensures the method calls follow the correct calling convention used by the COM object.
      • Mismatched calling conventions could result in runtime errors, memory corruption, or crashes.
    3. Mapping Method Signatures:

      • The attribute ensures the managed delegate’s signature (parameters, return type) matches the unmanaged method’s signature exactly, avoiding type mismatches and undefined behavior.

    This script demonstrates how PowerShell can interact with COM interfaces for advanced Windows functionality, such as toggling airplane mode, without relying on external tools or GUIs.