2016-09-11 5 views
0

Ich möchte Thread erstellen, die Skript-Code in Powershell mit CreateThread WinApi ausführen. Ich benutze diesen Code aber Fehler aufgetreten:Verwenden von CreateThread winapi in Powershell-Skript

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() 
} 

und definieren Create 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 Create winapi auf diese Weise nennen:

$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) 

aber Fehler aufgetreten: Kann nicht konvertieren die "MyDelegateType" -Wert des Typs "MyDelegateType" zum Eingeben von "System.IntPtr" Wie kann ich diesen Fehler beheben? Und gibt es eine andere Möglichkeit, den Thread WinAPI in Powershell zu erstellen?

+0

Was der Zweck von all dem? – PetSerAl

+0

Ich möchte WinAPI-Programmierung in Powershell. –

+0

warum nicht Runsspaces verwenden? – Avshalom

Antwort

4

Konvertieren eines verwalteten Objekts in IntPtr ist möglich, indem Sie das Objekt "pinning", aber das ist sehr heikel, da es nur in "unsicheren" Code erlaubt ist. Sie können es in C# mit der 'fixed' statement tun. Die GCHandle.AddrOfPinnedObject()-Methode ist eine Alternative, die direkt von PowerShell aufgerufen werden kann, aber ich kann es Ihnen nicht empfehlen.

Ich warne Sie jedoch, dass vollständige "winapi" -Programmierung (oder sogar die Verwendung des nicht verwalteten CreateThread) von großer Komplexität ist, wenn Sie versuchen, es in PowerShell zu tun. Sie müssen die erweiterten .NET-Interop- und PowerShell-Einschränkungen für Threads verstehen, die beide nur teilweise dokumentiert sind. Die Verwendung der .NET System.Thread-Klasse anstelle der Windows-API hilft bei einem Teil davon ohne die Forderung nach "unsicherem" Code, aber selbst diese Vorgehensweise würde ich Ihnen empfehlen nicht in PowerShell ohne eine höhere Erklärung zu tun als nur die winapi-Programmierung in der PowerShell "zu wollen".

Für ein aktuelles Powershell-Modul, das hilft zu abstrahieren, was für leichte (Thread) basierte Jobs für allgemeine Zwecke benötigt wird, siehe PoshRSJob auf GitHub. Es wird Ihnen wahrscheinlich viel Zeit beim Debuggen sparen, wenn Sie versuchen, die WINAPI-Funktionen selbst aufzurufen. Einleitender Artikel unter https://learn-powershell.net/2015/03/31/introducing-poshrsjob-as-an-alternative-to-powershell-jobs/

+0

Ich möchte mit createThread, weil Start-Job neue Powershell-Prozess erstellen. Ich möchte nur einen Prozess meine Arbeit machen. –

+0

@AlirezaJafari: Dein mentales Modell, wie die Dinge funktionieren, ist kaputt, was dich dazu gebracht hat, die falsche Frage zu stellen und das falsche Werkzeug zu benutzen. Sie können einen Prozess nicht irgendwie hosten, indem Sie ihn auf einen anderen Thread verschieben. Sie haben viel Vorlesen vor sich. – IInspectable

+0

@AlirezaJafari, eine Reihe von Personen haben mithilfe begrenzter Bereiche in PowerShell ** Threads erzeugt. Ein häufiges Beispiel könnte sein, dass die Schnittstellen zu WPF in einem separaten Thread ausgeführt werden müssen. Das Verständnis von Runspaces ist der Schlüssel. Siehe zum Beispiel https://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-u-from-a-different-runspace/ –