Welcome to MSDN Blogs Sign in | Join | Help

PINVOKE or accessing WIN32 APIs

In the very early days of Monad, we focused on a world where everyone would publish Cmdlets and providers.  Cmdlets and providers were designed around providing the best user experience.  They provide the right, high-level, task-oriented abstractions that users want while also providing the semantic benefits that the Monad engine provides (-WHATIF, -CONFIRM, -VERBOSE, -ERRORACTION, -ERRORVARIABLE, etc etc).  The trick then is - how do we get full coverage with Cmdlets?

To address this issue - we took an economic approach: require a tiny amount of code, provide a huge amount of function and good things will occur.  Anyone that has written a Cmdlet gets this in focus very quickly.  We provide value-add functions in a variety of ways.  First, Cmdlets are subclassed from our base class and then hosted by the Monad engine.  The base class and the engine provide a ton of functionality for free when you use the right APIs.  For instance, all you have to do is call ShouldProcess() and the engine will implement -WHATIF -CONFIRM and -VERBOSE for you.  Good deal. 

We also provide a ton of functions to manipulate  the objects that your cmdlets emit.  We have object utilities (sort, where, group, select, compare, etc), we have formating and outputing commands (format-table/list/wide/custom, out-host/file/printer/string etc, export/import-clixml/csv), we allow 3rd parties and end-users to extend your objects (*format.mshxml, *types.mshxml), we provide an interactive shell and a programming language with an awesome dynamic range which is usable by beginner scripters to very sophisticated programmers.  All and all, I think you'd be hard-pressed to get more functions per line of code than a Cmdlet provides (providers give even more functionality but they are not as tiny as Cmdlets). 

While we think we've produced a compelling economic,  it didn't take very long for us to do the analysis and realize that would quite a while before customers had full coverage for all the functions that they needed.  It would take a while before vendors had an opportunity to update their products, it would take a while before customers adopted the new versions of those products, and in the end - there might be some vendors that won't write Cmdlets no matter what (for technical, strategic, geo-political, or religious reasons or maybe because the vendor went out of business).  At this point, we expanded the scope of the project to embrace existing forms of instrumentation. 

Today we support Text Processing, COM automation, access to .NET classes but currently there is no native support for Win32 APIs.   That said, .NET provides some great support for calling Win32 APIs.  Here is a pointer to a good article on the details of .NET's support for PINVOKE (Platform Invoke) :

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcwlkplatforminvoketutorial.asp

Below is a code sample for how you can write an MSH Script with embedded C# code to invoke a Win32API.

#######################################################################
#  This is a general purpose routine that I put into a file called
#   LibraryCodeGen.msh and then dot-source when I need it.
#######################################################################
function Compile-Csharp ([string] $code, $FrameworkVersion="v2.0.50727",
[Array]$References)
{
    #
    # Get an instance of the CSharp code provider
    #
    $cp = new-object Microsoft.CSharp.CSharpCodeProvider

    #
    # Build up a compiler params object...
    $framework = Combine-Path $env:windir "Microsoft.NET\Framework\$FrameWorkVersion"
    $refs = new Collections.ArrayList
    $refs.AddRange( @("${framework}\System.dll",
        "${mshhome}\System.Management.Automation.dll",
        "${mshhome}\System.Management.Automation.ConsoleHost.dll",
        "${framework}\system.windows.forms.dll",
        "${framework}\System.data.dll",
        "${framework}\System.Drawing.dll",
        "${framework}\System.Xml.dll"))
    if ($references.Count -ge 1)
    {
        $refs.AddRange($References)
    }

    $cpar = New-Object System.CodeDom.Compiler.CompilerParameters
    $cpar.GenerateInMemory = $true
    $cpar.GenerateExecutable = $false
    $cpar.OutputAssembly = "custom"
    $cpar.ReferencedAssemblies.AddRange($refs)
    $cr = $cp.CompileAssemblyFromSource($cpar, $code)

    if ( $cr.Errors.Count)
    {
        $codeLines = $code.Split("`n");
        foreach ($ce in $cr.Errors)
        {
            write-host "Error: $($codeLines[$($ce.Line - 1)])"
            $ce |out-default
        }
        Throw "INVALID DATA: Errors encountered while compiling code"
    }
}

###########################################################################
#  Here I leverage one of my favorite features (here-strings) to define
# the C# code I want to run.  Remember - if you use single quotes - the
# string is taken literally but if you use double-quotes, we'll do variable
# expansion.  This can be VERY useful.
###########################################################################
$code = @'
using System;
using System.Runtime.InteropServices;

namespace test
{
    public class Testclass
    {
        [DllImport("msvcrt.dll")]
        public static extern int puts(string c);
        [DllImport("msvcrt.dll")]
        internal static extern int _flushall();

        public static void Run(string message)
        {
            puts(message);
            _flushall();
        }
    }
}
'@

##################################################################
# So now we compile the code and use .NET object access to run it.
##################################################################
compile-CSharp $code
[Test.TestClass]::Run("Monad ROCKS!")

Enjoy

Jeffrey P. Snover [MSFT]

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

Published Tuesday, April 25, 2006 5:17 PM by PowerShellTeam

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: PINVOKE or accessing WIN32 APIs

Would you mind updating this script for RC2?

Thanks!

-Steve

Friday, October 27, 2006 5:55 PM by Steve Trefethen

# re: PINVOKE or accessing WIN32 APIs

The revisions for PowerShell v1.0 are as follows:

Replace Combine-Path with Join-Path

Replace new with New-Object

Delete lines containing ${mshhome}

Replace ("Monad ROCKS!") with ("PowerShell ROCKS!")

Wednesday, July 11, 2007 11:19 AM by Matthew J. Fisher

# Development in a Blink » Blog Archive » ‘Embedding’ F# in PowerShell

# Backing up Event Logs on remote machines with PowerShell | Aaron Lerch

# Blogs and RSS » Windows PowerShell : PINVOKE or accessing WIN32 APIs

# Autodiscover and Client-only Sites (Revisited)

Previously I had posted a problem regarding Autodiscover site-scoping and client-only sites .  For

Tuesday, May 20, 2008 7:39 AM by BradHugh's WebLog

# Calling functions from a plain old DLL? | keyongtech

Wednesday, January 21, 2009 10:12 PM by Calling functions from a plain old DLL? | keyongtech

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker