{"id":3325,"date":"2013-06-27T00:01:00","date_gmt":"2013-06-27T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/06\/27\/use-powershell-to-interact-with-the-windows-api-part-3\/"},"modified":"2013-06-27T00:01:00","modified_gmt":"2013-06-27T00:01:00","slug":"use-powershell-to-interact-with-the-windows-api-part-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-interact-with-the-windows-api-part-3\/","title":{"rendered":"Use PowerShell to Interact with the Windows API: Part 3"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary<\/strong><span style=\"font-size: 12px\">: Guest blogger, Matt Graeber, completes his three-part series about interacting with the Windows API.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Matt Graeber is back with us today to finish up his three-part series. Read the previous parts to catch up for today&rsquo;s post:<\/p>\n<ul>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2013\/06\/25\/use-powershell-to-interact-with-the-windows-api-part-1.aspx\" target=\"_blank\">Use PowerShell to Interact with the Windows API: Part 1<\/a><\/li>\n<li><a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2013\/06\/26\/use-powershell-to-interact-with-windows-apis-part-2.aspx\" target=\"_blank\">Use PowerShell to Interact with the Windows API: Part 2<\/a><\/li>\n<\/ul>\n<p>In the last two posts, I described two methods of interacting with the Windows API. In this post, I&rsquo;ll introduce the last and final method: reflection.<\/p>\n<p>Reflection gives a programmer the ability to perform type introspection on code. The most common form of type introspection you would perform in Windows PowerShell would be using the <strong>Get-Member<\/strong> cmdlet, which enables you to discover the methods and properties of an object. In the last post, we also used reflection to find a non-public method that implemented the kernel32.dll <strong>CopyFile<\/strong> function. In this post, we will be using reflection to generate code dynamically. This concept is known as metaprogramming.<\/p>\n<h2>Steps to define a dynamic method<\/h2>\n<p>To define a dynamic method that will call the <strong>CopyFile<\/strong> method in kernel32.dll, the following steps are needed:<\/p>\n<ol>\n<li>Define a dynamic assembly. Recall that the assembly is the container for modules, types, and members.<\/li>\n<li>Define the dynamic assembly in your current AppDomain. Think of the AppDomain as your Windows PowerShell session. It&rsquo;s the environment in which our method is going to execute.<\/li>\n<li>Define a dynamic module. A module is a container for types and their members.<\/li>\n<li>Define a dynamic type. A type (that is, class) is a container for members (methods, properties, nested types, fields, events, and constructors).<\/li>\n<li>Define our dynamic method. Here we specify attributes (Public and Static) and the method&rsquo;s parameters and return type.<\/li>\n<li>Manually build a <strong>DllImport<\/strong> attribute. The result will be the equivalent of the following C# attribute: <br \/> [DllImport(&#8220;kernel32.dll&#8221;, SetLastError = true, PreserveSig = true,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallingConvention = CallingConvention.WinApi, CharSet = CharSet.Unicode)]<\/li>\n<li>Apply the custom <strong>DllImport<\/strong> attribute to the dynamic method.<\/li>\n<li>Call the <strong>CreateType<\/strong> method to bake everything together and make our method available to our Windows PowerShell session.<\/li>\n<\/ol>\n<p>As you can see, this is not a trivial process. This process involves performing tasks that <strong>Add-Type<\/strong> and the C# compiler would typically take care of on your behalf.<\/p>\n<p>So you may be asking now, &ldquo;Why the heck would I want to go through all this trouble just to call a function?&rdquo; The reason I typically do this is twofold:<\/p>\n<ol>\n<li>I need to maintain a minimal forensic footprint while executing my script. I don&rsquo;t want to invoke the C# compiler and write temp files to disk.<\/li>\n<li>The Windows API function I want is not present in the portion of the .NET Framework loaded by my current Windows PowerShell session.<\/li>\n<\/ol>\n<p>To see all of this in action, I wrote another implementation of the <strong>Copy-RawItem<\/strong> function that uses reflection. The complete text of this function is available in the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Copy-RawItem-Reflection-38fae6d4\" target=\"_blank\">Copy-RawItem (Reflection Version)<\/a>.<\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\"><strong>Copy-RawItem &ndash; Reflection Version<\/strong>:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">function Copy-RawItem<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">{<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&lt;#<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">.SYNOPSIS<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; Copies a file from one location to another including files contained within DeviceObject paths.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">.PARAMETER Path<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; Specifies the path to the file to copy.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">.PARAMETER Destination<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; Specifies the path to the location where the item is to be copied.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">.PARAMETER FailIfExists<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; Do not copy the file if it already exists in the specified destination.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">.OUTPUTS<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; None or an object representing the copied item.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">.EXAMPLE<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; Copy-RawItem &#8216;\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy2\\Windows\\System32\\config\\SAM&#8217; &#8216;C:\\temp\\SAM&#8217;<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">#&gt;<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; [CmdletBinding()]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; [OutputType([System.IO.FileSystemInfo])]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; Param (<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(Mandatory = $True, Position = 0)]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ValidateNotNullOrEmpty()]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [String]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Path,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(Mandatory = $True, Position = 1)]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ValidateNotNullOrEmpty()]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [String]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Destination,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Switch]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $FailIfExists<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; )<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Create a new dynamic assembly. An assembly (typically a dll file) is the container for modules<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $DynAssembly = New-Object System.Reflection.AssemblyName(&#8216;Win32Lib&#8217;)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Define the assembly and tell is to remain in memory only (via [Reflection.Emit.AssemblyBuilderAccess]::Run)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Define a new dynamic module. A module is the container for types (a.k.a. classes)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule(&#8216;Win32Lib&#8217;, $False)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Define a new type (class). This class will contain our method &#8211; CopyFile<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # I&#8217;m naming it &#8216;Kernel32&#8217; so that you will be able to call CopyFile like this:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # [Kernel32]::CopyFile(src, dst, FailIfExists)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $TypeBuilder = $ModuleBuilder.DefineType(&#8216;Kernel32&#8217;, &#8216;Public, Class&#8217;)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Define the CopyFile method. This method is a special type of method called a P\/Invoke method.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # A P\/Invoke method is an unmanaged exported function from a module &#8211; like kernel32.dll<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $PInvokeMethod = $TypeBuilder.DefineMethod(<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">&#8216;CopyFile&#8217;,<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">[Reflection.MethodAttributes] &#8216;Public, Static&#8217;,<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">[Bool],<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">[Type[]] @([String], [String], [Bool]))<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; #region DllImportAttribute<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Set the equivalent of: [DllImport(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp; &nbsp; # &nbsp;&nbsp;&#8220;kernel32.dll&#8221;, <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp; &nbsp; # &nbsp;&nbsp;SetLastError = true, <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp; &nbsp; # &nbsp;&nbsp;PreserveSig = true,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # &nbsp; CallingConvention = CallingConvention.WinApi,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # &nbsp; CharSet = CharSet.Unicode)]<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Note: DefinePInvokeMethod cannot be used if SetLastError needs to be set<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $FieldArray = [Reflection.FieldInfo[]] @(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.DllImportAttribute].GetField(&#8216;EntryPoint&#8217;),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.DllImportAttribute].GetField(&#8216;PreserveSig&#8217;),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.DllImportAttribute].GetField(&#8216;SetLastError&#8217;),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.DllImportAttribute].GetField(&#8216;CallingConvention&#8217;),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.DllImportAttribute].GetField(&#8216;CharSet&#8217;)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; )<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $FieldValueArray = [Object[]] @(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;CopyFile&#8217;,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $True,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $True,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.CallingConvention]::Winapi,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Runtime.InteropServices.CharSet]::Unicode<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; )<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">$DllImportConstructor,<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">@(&#8216;kernel32.dll&#8217;),<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">$FieldArray,<\/span><\/p>\n<p style=\"padding-left: 60px\"><span style=\"font-size: small\">$FieldValueArray)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; #endregion<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Make our method accesible to PowerShell<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $Kernel32 = $TypeBuilder.CreateType()<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; # Perform the copy<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; $CopyResult = $Kernel32::CopyFile($Path, $Destination, ([Bool] $PSBoundParameters[&#8216;FailIfExists&#8217;]))<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; if ($CopyResult -eq $False)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # An error occured. Display the Win32 error set by CopyFile<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw ( New-Object ComponentModel.Win32Exception )<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; else<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Output (Get-ChildItem $Destination)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-size: small\">}<\/span><\/p>\n<p>Here is <strong>Copy-RawItem<\/strong> in action with descriptive errors that are displayed:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3531.hsg-6-27-13-1.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3531.hsg-6-27-13-1.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>This wraps up my series about using Windows PowerShell to interact with the Windows API. As you can see, this process can be as simple or as complicated as you want it to be. Which method you choose will ultimately be determined by your unique execution requirements. However in most cases, using <strong>Add-Type<\/strong> to compile C# code will suffice.<\/p>\n<p>For more resources about reflection, search the MSDN documentation for the System.Reflection and System.Reflection.Emit namespaces. Also, check out some of the awesome work by Windows PowerShell MVPs Oisin Grehan and Adam Driscoll. These guys are experts on metaprogramming and reflection, among other things.<\/p>\n<p>The full script is available in the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Copy-RawItem-Reflection-38fae6d4\" target=\"_blank\">Copy-RawItem (Reflection Version)<\/a>.<\/p>\n<p>~Matt<\/p>\n<p>Thank you, Matt, for sharing your time and knowledge.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong><span style=\"font-size: 12px\">&nbsp;<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger, Matt Graeber, completes his three-part series about interacting with the Windows API. Microsoft Scripting Guy, Ed Wilson, is here. Matt Graeber is back with us today to finish up his three-part series. Read the previous parts to catch up for today&rsquo;s post: Use PowerShell to Interact with the Windows API: Part 1 [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,431,3,63,45],"class_list":["post-3325","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-matt-graeber","tag-scripting-guy","tag-security","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger, Matt Graeber, completes his three-part series about interacting with the Windows API. Microsoft Scripting Guy, Ed Wilson, is here. Matt Graeber is back with us today to finish up his three-part series. Read the previous parts to catch up for today&rsquo;s post: Use PowerShell to Interact with the Windows API: Part 1 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3325","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=3325"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/3325\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=3325"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=3325"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=3325"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}