0

i want create thread that run script code in powershell using CreateThread WinApi. i use this code but error occured:

function local:Get-ProcAddress {
Param (
    [OutputType([IntPtr])]

    [Parameter( Position = 0, Mandatory = $True )]
    [String]
    $Module,

    [Parameter( Position = 1, Mandatory = $True )]
    [String]
    $Procedure
)

# Get a reference to System.dll in the GAC
$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
    Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
$UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
# Get a reference to the GetModuleHandle and GetProcAddress methods
$GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
# Get a handle to the module specified
$Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
$tmpPtr = New-Object IntPtr
$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)

# Return the address of the function
$GetProcAddress.Invoke($null, @([Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
}
function local:Get-DelegateType {
Param (
    [OutputType([Type])]

    [Parameter( Position = 0)]
    [Type[]]
    $Parameters = (New-Object Type[](0)),

    [Parameter( Position = 1 )]
    [Type]
    $ReturnType = [Void]
)

$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object Reflection.AssemblyName('ReflectedDelegate')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
$TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
$ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
$MethodBuilder.SetImplementationFlags('Runtime, Managed')

$TypeBuilder.CreateType()
}

and define createthread winapi:

# CreateThread
$CreateThreadAddr = Get-ProcAddress kernel32.dll CreateThread
$CreateThreadDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr])
$CreateThread = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateThreadAddr, $CreateThreadDelegate)

i call createthread winapi in this way:

$ThreadProcScript = {
Param (
    [Parameter()]
    [IntPtr]$lpParameter

)
calc.exe
return [Int32]0

}
$Delegate = Get-DelegateType @([IntPtr]) ([int32])
$Callback = $ThreadProcScript -as $Delegate
$CreateThread.Invoke(0,0,$Callback ,0,0,0)

but error occured: Cannot convert the "MyDelegateType" value of type "MyDelegateType" to type "System.IntPtr" how can i Fix this error? and is there another way to using create thread winapi in powershell?

1 Answers1

4

Converting an managed object to IntPtr is possible by "pinning" the object, but this is very tricky business because it is only allowed in "unsafe" code. You can do it in C# using the 'fixed' statement. The GCHandle.AddrOfPinnedObject() method is an alternative that might be called directly from PowerShell, but I can't recommend it to you.

I'll warn you however that full generality "winapi" programming (or even using the unmanaged CreateThread) turns out to have of awful complexity if you try to do it in PowerShell. You would need to understand advanced .NET interop and PowerShell's restrictions on threads, both of which are only partially documented. Using .NET's System.Thread class instead of the windows API help with a part of this without the requirement for "unsafe" code, but even that approach is something I'd recommend you not do in PowerShell without a higher-level explanation than just "wanting" winapi programming in PowerShell.

For a up-to-date Powershell module that helps to abstract what's needed for lightweight (thread) based jobs for general purposes see PoshRSJob on GitHub. It will probably save you a lot of time debugging over trying to call the WINAPI functions yourself. Introductory article at https://learn-powershell.net/2015/03/31/introducing-poshrsjob-as-an-alternative-to-powershell-jobs/

Burt_Harris
  • 6,415
  • 2
  • 29
  • 64
  • i want using createThread because start-job create new powershell process. i want only one process do my work. – Alireza Jafari Sep 12 '16 at 06:49
  • @AlirezaJafari: Your mental model of how things work is broken, which led you to ask the wrong question, using the wrong tool. You cannot somehow host a process by moving it onto another thread. You have **lots** of reading ahead of you. – IInspectable Sep 12 '16 at 10:25
  • @AlirezaJafari, a number of people have succeeded in spawning threads using PowerShell **in limited scopes**, a common example might be where interfacing to WPF needs to run on a separate thread. Understanding runspaces is key.. See for example https://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/ – Burt_Harris Sep 12 '16 at 21:01