en-US/PSNetDetour.dll-Help.xml
|
<?xml version="1.0" encoding="utf-8"?> <helpItems schema="maml" xmlns="http://msh"> <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp"> <command:details> <command:name>New-NetDetourHook</command:name> <command:verb>New</command:verb> <command:noun>NetDetourHook</command:noun> <maml:description> <maml:para>Creates a hook on a .NET method to intercept calls and modify behavior.</maml:para> </maml:description> </command:details> <maml:description> <maml:para>The `New-NetDetourHook` cmdlet creates a runtime hook (detour) on a .NET method. When the hooked method is called, your hook scriptblock executes instead, allowing you to inspect arguments, modify behavior, and control the return value. You can optionally call the original method inside the hook using `$Detour.Invoke(...)`.</maml:para> <maml:para>The method to hook can be specified in two ways: - Using the `-Source` parameter with a scriptblock containing a method call expression</maml:para> <maml:para>- Using the `-Method` parameter with a MethodBase object</maml:para> <maml:para></maml:para> <maml:para>The hook remains active until the returned NetDetourHook object is disposed. For automatic disposal, use the `Use-NetDetourContext` cmdlet instead.</maml:para> <maml:para>Inside the hook scriptblock, you have access to: - Method parameters as scriptblock parameters</maml:para> <maml:para>- `$Detour.Instance` - The instance object (null for static methods)</maml:para> <maml:para>- `$Detour.Invoke(...)` - Invokes the original method - arguments are based on the original method</maml:para> <maml:para>- `$Detour.State` - The state object passed via `-State` parameter (null if not set)</maml:para> <maml:para></maml:para> <maml:para>Each hook is added as chain on the original method, if you create subsequent hooks for the same method it'll apply on top and `$Detour.Invoke(...)` will invoke the next hook in the chain.</maml:para> <maml:para>Due to limitations in the underlying library managing the hooks, it is not possible to hook a method on a generic type like `[System.Collections.Generic.List[int]].Add([int])` or a generic method like `[Array]::Empty([int])`.</maml:para> <maml:para>Hooking some methods might lead to unexpected crashes or hangs in the process. Common types used in background threads like `System.Text.StringBuilder` can be problematic if you hook methods on them.</maml:para> </maml:description> <command:syntax> <command:syntaxItem> <maml:name>New-NetDetourHook</maml:name> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none"> <maml:name>Source</maml:name> <maml:description> <maml:para>A scriptblock containing a method call expression that identifies the method to hook. The scriptblock must contain a single method call statement in one of these forms:</maml:para> <maml:para>- Static method: `{ [InstanceType]::MethodName() }` or `{ [InstanceType]::MethodName([Type1], [Type2], ...) }`</maml:para> <maml:para>- Instance method: `{ [InstanceType].MethodName() }` or `{ [InstanceType].MethodName([Type1], [Type2], ...) }`</maml:para> <maml:para>- Constructor: `{ [InstanceType]::new() }` or `{ [InstanceType]::new([Type1], [Type2], ...) }`</maml:para> <maml:para>- Property getter: `{ [InstanceType].get_PropertyName() }`</maml:para> <maml:para>- Property setter: `{ [TyInstanceTypepe].set_PropertyName([PropertyType]) }`</maml:para> <maml:para></maml:para> <maml:para>The type arguments in parentheses are type constraints, not values and represent the types for each parameter. For ref/out parameters, use `[ref][TypeName]`. For pointer parameters like `int ` change ` ` to `+`, `[int+]`.</maml:para> <maml:para>Methods are first searched in a case sensitive way before falling back to searching for a case insensitive match.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none"> <maml:name>Hook</maml:name> <maml:description> <maml:para>The scriptblock that executes when the hooked method is called. This scriptblock should have parameters matching the method's signature (excluding the instance for instance methods) or no `param()` block at all to access the args through `$args`. Inside the hook, you can access:</maml:para> <maml:para>- `$Detour.Instance` - The instance object (for instance methods)</maml:para> <maml:para>- `$Detour.Invoke(...)` - Call the original method with modified or original arguments</maml:para> <maml:para>- `$Detour.State` - The state object if provided via `-State`</maml:para> <maml:para></maml:para> <maml:para>The first output value in the hook will become the method's return value. If the hook produces no output for a method with a return type, the default value is returned (0 for int, null for reference types, etc.). Any remaining output will be discarded.</maml:para> <maml:para>If the hook is run in the current runspace it will have access to the session state where the method was invoked. If using the hook inside `Use-NetDetourContext` the hook will also be able to write to the various streams. If the hook is run in a new runspace or runspace pool it will not have access to any variables or functions defined outside of the hook scriptblock. The `-State` parameter or `$using:var` syntax can be used to inject variables from when the hook was defined for these scenarios.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>FindNonPublic</maml:name> <maml:description> <maml:para>Allows the cmdlet to find and hook private, internal, or protected methods. By default, only public methods are searched. This is useful when you need to hook internal framework methods that aren't part of the public API. Using this switch will not be able to find non-public types, just methods on the types.</maml:para> <maml:para>If the method to be hooked is not on a public type, the `-Method` parameter must be used instead of `-Source`.</maml:para> </maml:description> <dev:type> <maml:name>SwitchParameter</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>False</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>IgnoreConstructorNew</maml:name> <maml:description> <maml:para>When the `-Source` value is `{ [TypeName]::new() }` by default it will find the constructor for `TypeName`. This switch will change that logic to instead search for the static method on `TypeName` called `new`.</maml:para> </maml:description> <dev:type> <maml:name>SwitchParameter</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>False</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga"> <maml:name>ProgressAction</maml:name> <maml:description> <maml:para>New common parameter introduced in PowerShell 7.4.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue> <dev:type> <maml:name>ActionPreference</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>State</maml:name> <maml:description> <maml:para>An object that will be available in the hook scriptblock via `$Detour.State`. This is useful for passing context or maintaining state between hook invocations. The state object is shared across all invocations of the hook, making it ideal for counters, caches, or configuration.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">Object</command:parameterValue> <dev:type> <maml:name>Object</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>UseRunspace</maml:name> <maml:description> <maml:para>Specifies which runspace to use when executing the hook scriptblock. This is critical for hooks that may be invoked from threads without an active runspace. Valid values are:</maml:para> <maml:para>- `'Current'` (default): Use the runspace of the thread invoking the method. Fails if no runspace is active.</maml:para> <maml:para>- `'New'`: Create a new dedicated Runspace for the hook (managed automatically).</maml:para> <maml:para>- `'Pool'`: Create a new RunspacePool for the hook (managed automatically).</maml:para> <maml:para>- A Runspace object: Use a specific runspace you manage.</maml:para> <maml:para>- A RunspacePool object: Use a specific runspace pool you manage.</maml:para> <maml:para></maml:para> <maml:para>When using 'New' or 'Pool', the runspace is automatically disposed when the hook is disposed.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">UseRunspaceValue</command:parameterValue> <dev:type> <maml:name>UseRunspaceValue</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> </command:syntaxItem> <command:syntaxItem> <maml:name>New-NetDetourHook</maml:name> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none"> <maml:name>Method</maml:name> <maml:description> <maml:para>A MethodBase object (MethodInfo or ConstructorInfo) representing the method to hook. This provides an alternative to the -Source parameter when you already have a reflection object. You can obtain MethodBase objects using reflection methods like `GetMethod()` or `GetConstructor()`.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">MethodBase</command:parameterValue> <dev:type> <maml:name>MethodBase</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none"> <maml:name>Hook</maml:name> <maml:description> <maml:para>The scriptblock that executes when the hooked method is called. This scriptblock should have parameters matching the method's signature (excluding the instance for instance methods) or no `param()` block at all to access the args through `$args`. Inside the hook, you can access:</maml:para> <maml:para>- `$Detour.Instance` - The instance object (for instance methods)</maml:para> <maml:para>- `$Detour.Invoke(...)` - Call the original method with modified or original arguments</maml:para> <maml:para>- `$Detour.State` - The state object if provided via `-State`</maml:para> <maml:para></maml:para> <maml:para>The first output value in the hook will become the method's return value. If the hook produces no output for a method with a return type, the default value is returned (0 for int, null for reference types, etc.). Any remaining output will be discarded.</maml:para> <maml:para>If the hook is run in the current runspace it will have access to the session state where the method was invoked. If using the hook inside `Use-NetDetourContext` the hook will also be able to write to the various streams. If the hook is run in a new runspace or runspace pool it will not have access to any variables or functions defined outside of the hook scriptblock. The `-State` parameter or `$using:var` syntax can be used to inject variables from when the hook was defined for these scenarios.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga"> <maml:name>ProgressAction</maml:name> <maml:description> <maml:para>New common parameter introduced in PowerShell 7.4.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue> <dev:type> <maml:name>ActionPreference</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>State</maml:name> <maml:description> <maml:para>An object that will be available in the hook scriptblock via `$Detour.State`. This is useful for passing context or maintaining state between hook invocations. The state object is shared across all invocations of the hook, making it ideal for counters, caches, or configuration.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">Object</command:parameterValue> <dev:type> <maml:name>Object</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>UseRunspace</maml:name> <maml:description> <maml:para>Specifies which runspace to use when executing the hook scriptblock. This is critical for hooks that may be invoked from threads without an active runspace. Valid values are:</maml:para> <maml:para>- `'Current'` (default): Use the runspace of the thread invoking the method. Fails if no runspace is active.</maml:para> <maml:para>- `'New'`: Create a new dedicated Runspace for the hook (managed automatically).</maml:para> <maml:para>- `'Pool'`: Create a new RunspacePool for the hook (managed automatically).</maml:para> <maml:para>- A Runspace object: Use a specific runspace you manage.</maml:para> <maml:para>- A RunspacePool object: Use a specific runspace pool you manage.</maml:para> <maml:para></maml:para> <maml:para>When using 'New' or 'Pool', the runspace is automatically disposed when the hook is disposed.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">UseRunspaceValue</command:parameterValue> <dev:type> <maml:name>UseRunspaceValue</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> </command:syntaxItem> </command:syntax> <command:parameters> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>FindNonPublic</maml:name> <maml:description> <maml:para>Allows the cmdlet to find and hook private, internal, or protected methods. By default, only public methods are searched. This is useful when you need to hook internal framework methods that aren't part of the public API. Using this switch will not be able to find non-public types, just methods on the types.</maml:para> <maml:para>If the method to be hooked is not on a public type, the `-Method` parameter must be used instead of `-Source`.</maml:para> </maml:description> <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue> <dev:type> <maml:name>SwitchParameter</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>False</dev:defaultValue> </command:parameter> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none"> <maml:name>Hook</maml:name> <maml:description> <maml:para>The scriptblock that executes when the hooked method is called. This scriptblock should have parameters matching the method's signature (excluding the instance for instance methods) or no `param()` block at all to access the args through `$args`. Inside the hook, you can access:</maml:para> <maml:para>- `$Detour.Instance` - The instance object (for instance methods)</maml:para> <maml:para>- `$Detour.Invoke(...)` - Call the original method with modified or original arguments</maml:para> <maml:para>- `$Detour.State` - The state object if provided via `-State`</maml:para> <maml:para></maml:para> <maml:para>The first output value in the hook will become the method's return value. If the hook produces no output for a method with a return type, the default value is returned (0 for int, null for reference types, etc.). Any remaining output will be discarded.</maml:para> <maml:para>If the hook is run in the current runspace it will have access to the session state where the method was invoked. If using the hook inside `Use-NetDetourContext` the hook will also be able to write to the various streams. If the hook is run in a new runspace or runspace pool it will not have access to any variables or functions defined outside of the hook scriptblock. The `-State` parameter or `$using:var` syntax can be used to inject variables from when the hook was defined for these scenarios.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>IgnoreConstructorNew</maml:name> <maml:description> <maml:para>When the `-Source` value is `{ [TypeName]::new() }` by default it will find the constructor for `TypeName`. This switch will change that logic to instead search for the static method on `TypeName` called `new`.</maml:para> </maml:description> <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue> <dev:type> <maml:name>SwitchParameter</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>False</dev:defaultValue> </command:parameter> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none"> <maml:name>Method</maml:name> <maml:description> <maml:para>A MethodBase object (MethodInfo or ConstructorInfo) representing the method to hook. This provides an alternative to the -Source parameter when you already have a reflection object. You can obtain MethodBase objects using reflection methods like `GetMethod()` or `GetConstructor()`.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">MethodBase</command:parameterValue> <dev:type> <maml:name>MethodBase</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga"> <maml:name>ProgressAction</maml:name> <maml:description> <maml:para>New common parameter introduced in PowerShell 7.4.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue> <dev:type> <maml:name>ActionPreference</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none"> <maml:name>Source</maml:name> <maml:description> <maml:para>A scriptblock containing a method call expression that identifies the method to hook. The scriptblock must contain a single method call statement in one of these forms:</maml:para> <maml:para>- Static method: `{ [InstanceType]::MethodName() }` or `{ [InstanceType]::MethodName([Type1], [Type2], ...) }`</maml:para> <maml:para>- Instance method: `{ [InstanceType].MethodName() }` or `{ [InstanceType].MethodName([Type1], [Type2], ...) }`</maml:para> <maml:para>- Constructor: `{ [InstanceType]::new() }` or `{ [InstanceType]::new([Type1], [Type2], ...) }`</maml:para> <maml:para>- Property getter: `{ [InstanceType].get_PropertyName() }`</maml:para> <maml:para>- Property setter: `{ [TyInstanceTypepe].set_PropertyName([PropertyType]) }`</maml:para> <maml:para></maml:para> <maml:para>The type arguments in parentheses are type constraints, not values and represent the types for each parameter. For ref/out parameters, use `[ref][TypeName]`. For pointer parameters like `int ` change ` ` to `+`, `[int+]`.</maml:para> <maml:para>Methods are first searched in a case sensitive way before falling back to searching for a case insensitive match.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>State</maml:name> <maml:description> <maml:para>An object that will be available in the hook scriptblock via `$Detour.State`. This is useful for passing context or maintaining state between hook invocations. The state object is shared across all invocations of the hook, making it ideal for counters, caches, or configuration.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">Object</command:parameterValue> <dev:type> <maml:name>Object</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>UseRunspace</maml:name> <maml:description> <maml:para>Specifies which runspace to use when executing the hook scriptblock. This is critical for hooks that may be invoked from threads without an active runspace. Valid values are:</maml:para> <maml:para>- `'Current'` (default): Use the runspace of the thread invoking the method. Fails if no runspace is active.</maml:para> <maml:para>- `'New'`: Create a new dedicated Runspace for the hook (managed automatically).</maml:para> <maml:para>- `'Pool'`: Create a new RunspacePool for the hook (managed automatically).</maml:para> <maml:para>- A Runspace object: Use a specific runspace you manage.</maml:para> <maml:para>- A RunspacePool object: Use a specific runspace pool you manage.</maml:para> <maml:para></maml:para> <maml:para>When using 'New' or 'Pool', the runspace is automatically disposed when the hook is disposed.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">UseRunspaceValue</command:parameterValue> <dev:type> <maml:name>UseRunspaceValue</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> </command:parameters> <command:inputTypes> <command:inputType> <dev:type> <maml:name>None</maml:name> </dev:type> <maml:description> <maml:para></maml:para> </maml:description> </command:inputType> </command:inputTypes> <command:returnValues> <command:returnValue> <dev:type> <maml:name>PSNetDetour.NetDetourHook</maml:name> </dev:type> <maml:description> <maml:para>If not run inside `Use-NetDetourContext`, this cmdlet will output the `NetDetourHook` object representing the hook. The hook will stay alive until it is disposed.</maml:para> </maml:description> </command:returnValue> </command:returnValues> <maml:alertSet> <maml:alert> <maml:para></maml:para> </maml:alert> </maml:alertSet> <command:examples> <command:example> <maml:title>--------------- Example 1: Hook a static method ---------------</maml:title> <dev:code>$hook = New-NetDetourHook -Source { [System.IO.Path]::GetTempPath() } -Hook { "C:\CustomTemp\" } try { [System.IO.Path]::GetTempPath() } finally { $hook.Dispose() } # C:\CustomTemp\</dev:code> <dev:remarks> <maml:para>This example hooks `GetTempPath()` to return a custom path. As the hook is created outside of `Use-NetDetourContext` it needs to be manually disposed to ensure the hook is no longer in active.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>---------------- Example 2: Hook with arguments ----------------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.IO.Path]::Combine([string], [string]) } -Hook { param($path1, $path2) Write-Host "Combining: $path1 and $path2" $Detour.Invoke($path1, $path2) } [System.IO.Path]::Combine("C:\", "temp") } # Combining: C:\ and temp # C:\temp</dev:code> <dev:remarks> <maml:para>This example intercepts `Path.Combine()` to log the arguments before calling the original method. The hook is run inside `Use-NetDetourContext` which will automatically dispose of the hook once the ScriptBlock is finished.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>-------------- Example 3: Hook an instance method --------------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.DateTime].AddDays([double]) } -Hook { param($days) Write-Host "Instance: $($Detour.Instance.ToString('o'))" Write-Host "Adding $days days" $Detour.Invoke($days * 2) } $date = [DateTime]::new(2024, 1, 1) $date.AddDays(5).ToString("o") } # Instance: 2024-01-01T00:00:00.0000000 # Adding 5 days # 2024-01-11T00:00:00.0000000</dev:code> <dev:remarks> <maml:para>This example hooks the `AddDays()` method to double the number of days being added.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>------------- Example 4: Use state to track calls -------------</maml:title> <dev:code>$state = @{ Calls = 0 } Use-NetDetourContext { New-NetDetourHook -Source { [System.Random].Next() } -Hook { $Detour.State.Calls++ $Detour.Invoke() } -State $state $rnd = [System.Random]::new() $rnd.Next() $rnd.Next() } $state.Calls # 2</dev:code> <dev:remarks> <maml:para>This example uses the state parameter to count method invocations. The state value provided is accessible through the `$Detour.State` property.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>------------ Example 5: Hook with $using: variables ------------</maml:title> <dev:code>$prefix = "LOG: " Use-NetDetourContext { New-NetDetourHook -Source { [Console]::WriteLine([string]) } -Hook { param($message) $Detour.Invoke($using:prefix + $message) } [Console]::WriteLine("Hello World") } # LOG: Hello World</dev:code> <dev:remarks> <maml:para>This example uses `$using:` to capture variables from the calling scope. The variables are captured when the hook is created and is used as another way of providing data to the hook like the `-State` parameter.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>---------------- Example 6: Hook async methods ----------------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.Net.Http.HttpClient].GetAsync([string]) } -Hook { param($uri) Write-Host "HTTP GET: $uri" $Detour.Invoke($uri) } -UseRunspace New $client = [System.Net.Http.HttpClient]::new() $task = $client.GetAsync("https://httpbin.org/get") while (-not $task.AsyncWaitHandle.WaitOne(300)) {} $response = $task.GetAwaiter().GetResult() } $response.StatusCode # HTTP GET: https://httpbin.org/get # OK</dev:code> <dev:remarks> <maml:para>This example hooks the async `GetAsync` method on HttpClient using `-UseRunspace New` to handle the async callback context that will be run in another thread. In this case the original Task from `GetAsync()` will be returned back to PowerShell to then await.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>------ Example 7: Hook async methods and await the result ------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.Net.Http.HttpClient].GetAsync([string]) } -Hook { param($uri) Write-Host "HTTP GET: $uri" $task = $Detour.Invoke($uri) while (-not $task.AsyncWaitHandle.WaitOne(300)) {} $response = $task.GetAwaiter().GetResult() Write-Host "Response: $($response.StatusCode)" $task } -UseRunspace New $client = [System.Net.Http.HttpClient]::new() $task = $client.GetAsync("https://httpbin.org/get") while (-not $task.AsyncWaitHandle.WaitOne(300)) {} $response = $task.GetAwaiter().GetResult() } $response.StatusCode # HTTP GET: https://httpbin.org/get # Response: OK # OK</dev:code> <dev:remarks> <maml:para>This example hooks `GetAsync` and awaits the task result inside the hook using `GetAwaiter().GetResult()`, prints the status code, then returns the original task response back to the caller. It is possible to return a custom task result with a new Http response.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>----------- Example 8: Hook using -Method parameter -----------</maml:title> <dev:code>Use-NetDetourContext { $method = [System.IO.Path].GetMethod('GetTempPath', [Type[]]@()) New-NetDetourHook -Method $method -Hook { "C:\CustomTemp\" } [System.IO.Path]::GetTempPath() } # C:\CustomTemp\</dev:code> <dev:remarks> <maml:para>This example demonstrates using the `-Method` parameter with a MethodInfo object obtained through reflection, providing an alternative to the `-Source` parameter.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>------- Example 9: Hook methods with ref/out parameters -------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [int]::TryParse([string], [ref][int]) } -Hook { param($s, $result) Write-Host "Parsing: $s" $originalResult = $Detour.Invoke($s, $result) if ($originalResult) { Write-Host "Parsed value: $($result.Value)" $result.Value += 10 } $originalResult } $num = 0 [int]::TryParse("123", [ref]$num) "Result: $num" } # Parsing: 123 # Parsed value: 123 # Result: 133</dev:code> <dev:remarks> <maml:para>This example shows how to hook methods with `ref` or `out` parameters. In the `-Source` parameter, use `[ref][TypeName]` for ref/out parameters. In the hook, the parameter will be a reference that can be accessed via `.Value`. In this example the `ref` parameter is being updated after the original method is called.</maml:para> </dev:remarks> </command:example> </command:examples> <command:relatedLinks> <maml:navigationLink> <maml:linkText>Online Version:</maml:linkText> <maml:uri>https://www.github.com/jborean93/PSNetDetour/blob/main/docs/en-US/New-NetDetourHook.md</maml:uri> </maml:navigationLink> </command:relatedLinks> </command:command> <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp"> <command:details> <command:name>Use-NetDetourContext</command:name> <command:verb>Use</command:verb> <command:noun>NetDetourContext</command:noun> <maml:description> <maml:para>Executes a scriptblock with automatic management of NetDetour hooks.</maml:para> </maml:description> </command:details> <maml:description> <maml:para>The `Use-NetDetourContext` cmdlet executes a scriptblock and automatically manages the lifecycle of any NetDetourHook objects created within it. When the scriptblock completes (either successfully or with an error), all hooks created by `New-NetDetourHook` inside the context are automatically disposed.</maml:para> <maml:para>This cmdlet provides a convenient way to use hooks without manually managing their disposal with try/finally blocks. It's similar to C#'s `using` statement.</maml:para> <maml:para>By default, the scriptblock executes in a new child scope, use the `-NoNewScope` switch to run in the current scope if you need to modify parent scope variables.</maml:para> <maml:para>The cmdlet properly forwards all PowerShell streams (Error, Warning, Verbose, Debug, Information, and Progress) from hooks back to the caller, even when hooks are executed in separate runspaces. If the hook is executed in the current runspace it will be emitted as soon as the hook created the record but if the hook is run in another runspace the records will only be emitted when `Use-NetDetourContext` ends.</maml:para> </maml:description> <command:syntax> <command:syntaxItem> <maml:name>Use-NetDetourContext</maml:name> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none"> <maml:name>ScriptBlock</maml:name> <maml:description> <maml:para>The scriptblock to execute. Any NetDetourHook objects created inside this scriptblock (via `New-NetDetourHook`) that are output to the pipeline (not captured in variables) will be automatically disposed when the scriptblock completes.</maml:para> <maml:para>The scriptblock can produce output which will be returned from the cmdlet. Hook objects themselves are captured internally and not returned in the output.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>NoNewScope</maml:name> <maml:description> <maml:para>Runs the scriptblock in the current scope instead of a new child scope. This allows the scriptblock to modify variables in the parent scope. By default, scriptblocks run in a new scope which isolates variables.</maml:para> </maml:description> <dev:type> <maml:name>SwitchParameter</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>False</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga"> <maml:name>ProgressAction</maml:name> <maml:description> <maml:para>New common parameter introduced in PowerShell 7.4.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue> <dev:type> <maml:name>ActionPreference</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> </command:syntaxItem> </command:syntax> <command:parameters> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none"> <maml:name>NoNewScope</maml:name> <maml:description> <maml:para>Runs the scriptblock in the current scope instead of a new child scope. This allows the scriptblock to modify variables in the parent scope. By default, scriptblocks run in a new scope which isolates variables.</maml:para> </maml:description> <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue> <dev:type> <maml:name>SwitchParameter</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>False</dev:defaultValue> </command:parameter> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga"> <maml:name>ProgressAction</maml:name> <maml:description> <maml:para>New common parameter introduced in PowerShell 7.4.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue> <dev:type> <maml:name>ActionPreference</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none"> <maml:name>ScriptBlock</maml:name> <maml:description> <maml:para>The scriptblock to execute. Any NetDetourHook objects created inside this scriptblock (via `New-NetDetourHook`) that are output to the pipeline (not captured in variables) will be automatically disposed when the scriptblock completes.</maml:para> <maml:para>The scriptblock can produce output which will be returned from the cmdlet. Hook objects themselves are captured internally and not returned in the output.</maml:para> </maml:description> <command:parameterValue required="true" variableLength="false">ScriptBlock</command:parameterValue> <dev:type> <maml:name>ScriptBlock</maml:name> <maml:uri /> </dev:type> <dev:defaultValue>None</dev:defaultValue> </command:parameter> </command:parameters> <command:inputTypes> <command:inputType> <dev:type> <maml:name>None</maml:name> </dev:type> <maml:description> <maml:para></maml:para> </maml:description> </command:inputType> </command:inputTypes> <command:returnValues> <command:returnValue> <dev:type> <maml:name>System.Object</maml:name> </dev:type> <maml:description> <maml:para>Any object emitted by the scriptblock.</maml:para> </maml:description> </command:returnValue> </command:returnValues> <maml:alertSet> <maml:alert> <maml:para></maml:para> </maml:alert> </maml:alertSet> <command:examples> <command:example> <maml:title>----- Example 1: Basic usage with automatic hook disposal -----</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.IO.Path]::GetTempPath() } -Hook { "C:\CustomTemp\" } [System.IO.Path]::GetTempPath() } # C:\CustomTemp\ [System.IO.Path]::GetTempPath() # C:\Users\username\AppData\Local\Temp\</dev:code> <dev:remarks> <maml:para>The hook is active inside the context and automatically disposed when the scriptblock completes.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>----------- Example 2: Multiple hooks in one context -----------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.IO.Path]::GetTempPath() } -Hook { "C:\CustomTemp\" } New-NetDetourHook -Source { [System.IO.Path]::GetTempFileName() } -Hook { "C:\CustomTemp\test.tmp" } [System.IO.Path]::GetTempPath() [System.IO.Path]::GetTempFileName() } # C:\CustomTemp\ # C:\CustomTemp\test.tmp</dev:code> <dev:remarks> <maml:para>All hooks created in the context are automatically disposed when the scriptblock completes.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>--- Example 3: Using -NoNewScope to modify parent variables ---</maml:title> <dev:code>$result = $null Use-NetDetourContext -NoNewScope { New-NetDetourHook -Source { [System.Random].Next([int]) } -Hook { 42 } $result = [System.Random]::new().Next(100) } $result # 42</dev:code> <dev:remarks> <maml:para>The `-NoNewScope` switch allows the scriptblock to modify variables in the parent scope.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>------------------ Example 4: Nested contexts ------------------</maml:title> <dev:code>Use-NetDetourContext { New-NetDetourHook -Source { [System.Random].Next() } -Hook { 1 } [System.Random]::new().Next() # Returns 1 Use-NetDetourContext { New-NetDetourHook -Source { [System.Random].Next() } -Hook { 2 } [System.Random]::new().Next() # Returns 2 } [System.Random]::new().Next() # Returns 1 again } # 1 # 2 # 1</dev:code> <dev:remarks> <maml:para>Contexts can be nested, with inner hooks taking precedence and being disposed when the inner context exits.</maml:para> </dev:remarks> </command:example> <command:example> <maml:title>------- Example 5: Error handling with automatic cleanup -------</maml:title> <dev:code>try { Use-NetDetourContext { New-NetDetourHook -Source { [System.IO.File]::ReadAllText([string]) } -Hook { param($path) Write-Host "Reading: $path" throw "Simulated error" } [System.IO.File]::ReadAllText("test.txt") } } catch { Write-Host "Caught: $($_.Exception.Message)" } [System.IO.File]::ReadAllText("test.txt") # Reading: test.txt # Exception occurred while invoking hook for ReadAllText: Simulated error # Text here</dev:code> <dev:remarks> <maml:para>Even when errors occur, hooks are properly disposed when exiting the context.</maml:para> </dev:remarks> </command:example> </command:examples> <command:relatedLinks> <maml:navigationLink> <maml:linkText>Online Version:</maml:linkText> <maml:uri>https://www.github.com/jborean93/PSNetDetour/blob/main/docs/en-US/Use-NetDetourContext.md</maml:uri> </maml:navigationLink> </command:relatedLinks> </command:command> </helpItems> |