Results 1 to 2 of 2

Thread: Importing Win32 Native functions into Powershell

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Administrator James's Avatar
    Join Date
    May 2010
    Location
    on the intraweb
    Posts
    3,180

    Default Importing Win32 Native functions into Powershell

    Hoping I can get some assistance. I'm working on a little project of importing some win32 functions into powershell so I can natively use them.
    As awesome as Powershell is, one frustrating/annoying limitation I am running into is the filename\path limitation of 260 characters...
    For anyone unfamiliar with what I am talking about; if you have a file or a path to a directory that exceeds 260 characters; windows will have a difficult time deleting that file.

    As a way around this limitation, I am trying to import some useful win32 functions to perform the task I need...
    For starters, I am natively using DeleteFileW(). https://docs.microsoft.com/en-us/win...pi-deletefilew

    Here is my code:

    CLS

    $script:nativeMethods = @();
    function Register-NativeMethod([Parameter(Mandatory=$true)][string]$dll, [Parameter(Mandatory=$true)][string]$methodSignature, [string]$OptionalParameters)
    {
    if($OptionalParameters -ne "")
    {
    $OptionalParameters = (", " + $OptionalParameters)
    $dll = $dll + $OptionalParameters
    }

    #$script:nativeMethods += [PSCustomObject]@{ Dll = $dll; Signature = $methodSignature; Optional = $OptionalParameters; }
    $script:nativeMethods += [PSCustomObject]@{ Dll = $dll; Signature = $methodSignature; }
    }

    <#
    function Add-NativeMethods()
    {
    $nativeMethodsCode = $script:nativeMethods | % { "
    [DllImport(`"$($_.Dll)$($_.Optional)`")]
    public static extern $($_.Signature);
    " }

    Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    public class NativeMethods {
    $nativeMethodsCode
    }
    "@
    }
    #>
    function Add-NativeMethods()
    {
    $nativeMethodsCode = $script:nativeMethods | % { "
    [DllImport(`"$($_.Dll)`")]
    public static extern $($_.Signature);
    " }

    Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    public class NativeMethods {
    $nativeMethodsCode
    }
    "@
    }
    ###########################################################################################

    # Add your Win32 API here:
    ###########################################################################################
    Register-NativeMethod "kernel32.dll" "bool DeleteFileW(string lpFileName)" "CharSet = CharSet.Unicode, SetLastError = true"
    ###########################################################################################

    #Build class and registers them:

    Try
    {
    Add-NativeMethods
    }
    catch [System.Exception]
    {
    #$Error
    #$error[0].Exception | gm
    }

    [NativeMethods]::DeleteFileW("C:\TEST\test.txt")
    [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()


    The issue I am running in to is the output shows it was successfully completed, but the file doesn't get deleted. And to answer any question, no the file isn't opened up, and yes I have full access to it.

    Here is the error
    Code:
    Exception calling "DeleteFileW" with "1" argument(s): "Unable to load DLL 'kernel32.dll, CharSet = CharSet.Unicode, SetLastError = true': The specified module could 
    not be found. (Exception from HRESULT: 0x8007007E)"
    At C:\TEST\testB.ps1:68 char:1
    + [NativeMethods]::DeleteFileW("C:\TEST\test.txt
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : DllNotFoundException
     
    The operation completed successfully
    
    PS C:\TEST>
    I'm taking a guess here, but I think part of the problem is the "optional parameters" I am passing into the dllimport, which is this piece

    "CharSet = CharSet.Unicode, SetLastError = true"


    Because the raw code should look something like this:

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool DeleteFileW(string lpFileName);


    Do any of you have any thoughts?

    BTW, if I write everything out in raw form, it works fine, but ideally, I'd like to create a "quick" importer so I don't have to do it long hand for every function I want to import, I just write it in my "Register-NativeMethod" section, and it imports automatically.

    Here is the raw form that works perfectly

    CLS

    $signature = @'
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool DeleteFileW(string lpFileName);
    '@

    $type = Add-Type -MemberDefinition $signature -Name Win32Utils -Namespace Win32 -Using System.Text -PassThru

    $type::DeleteFileW("C:\TEST\test.txt");
    [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()


  2. #2
    Administrator James's Avatar
    Join Date
    May 2010
    Location
    on the intraweb
    Posts
    3,180

    Default

    I got it working... I rewrote it I think it's much easier to understand now.


    CLS

    $global:signature = @();
    $global:AppendSig = @();

    Function RegisterFunction
    {
    Param(
    [Parameter(Mandatory=$True)]
    [String] $Dll,

    [Parameter(Mandatory=$True)]
    [String] $FuncSig,

    [String] $OptionalParameters
    )

    $dll = '"' + $dll + '"'

    if($OptionalParameters -ne "")
    {
    $OptionalParameters = (", " + $OptionalParameters)
    $dll = $dll + $OptionalParameters
    }

    $global:AppendSig += [PSCustomObject]@{ Dll = $Dll; Signature = $FuncSig; }

    $global:signature = $global:AppendSig | % { "
    [DllImport($($_.Dll))]
    public static extern $($_.Signature);
    " }
    }

    #Register our Function
    RegisterFunction "Kernel32.dll" "bool DeleteFileW(string lpFileName)" "CharSet = CharSet.Unicode, SetLastError = true"

    $Win32 = Add-Type -MemberDefinition $global:signature -Name Win32 -Namespace Win32 -Using System.Text -PassThru

    $Win32::DeleteFileW("C:\TEST\test.txt");
    [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •