{"id":2711,"date":"2013-10-19T00:01:00","date_gmt":"2013-10-19T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/10\/19\/weekend-scripter-use-powershell-and-pinvoke-to-remove-stubborn-files\/"},"modified":"2013-10-19T00:01:00","modified_gmt":"2013-10-19T00:01:00","slug":"weekend-scripter-use-powershell-and-pinvoke-to-remove-stubborn-files","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-use-powershell-and-pinvoke-to-remove-stubborn-files\/","title":{"rendered":"Weekend Scripter: Use PowerShell and Pinvoke to Remove Stubborn Files"},"content":{"rendered":"<p><strong>Summary<\/strong>: Windows PowerShell MVP, Boe Prox, talks about using Windows PowerShell and Pinvoke to delete locked files.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post by newly crowned Windows PowerShell MVP, Boe Prox. Long-time readers of the Hey, Scripting Guy! Blog are familiar with Boe&rsquo;s work,&nbsp;but this is his first guest blog post as an MVP. WooHoo!<\/p>\n<p style=\"padding-left: 30px\">Boe Prox is a Microsoft MVP in Windows PowerShell and a senior Windows system administrator. He has worked in the IT field since 2003, and he supports a variety of different platforms. He is also an Honorary Scripting Guy and has submitted a number of posts as a guest blogger, which discuss a variety of topics. He is a contributing author in <a href=\"http:\/\/manning.com\/hicks\">PowerShell Deep Dives<\/a> with chapters about WSUS and TCP communication. He is a moderator on the <a href=\"http:\/\/social.technet.microsoft.com\/Forums\/scriptcenter\/en-US\/home?forum=ITCG\">Hey, Scripting Guy! forum<\/a>, and he has been a judge for the Scripting Games since 2010. He recently presented talks on the topic of WSUS and Windows PowerShell at the <a href=\"http:\/\/mspsug.com\/2013\/09\/17\/video-and-presentation-materials-from-the-september-mspsug-meeting\/\">Mississippi PowerShell User Group<\/a>.<\/p>\n<p style=\"padding-left: 30px\">To read more of his posts, see these&nbsp;<a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/boe+prox\/\" target=\"_blank\">Hey, Scripting Guy! Blog posts by Boe Prox<\/a>.<strong><br \/><\/strong>Boe&rsquo;s blog: <a href=\"http:\/\/learn-powershell.net\/\">Learn Powershell | Achieve More<br \/><\/a>Codeplex projects: <a href=\"http:\/\/poshwsus.codeplex.com\/\">PoshWSUS<\/a>, <a href=\"http:\/\/poshpaig.codeplex.com\/\">PoshPAIG<\/a>, <a href=\"http:\/\/poshchat.codeplex.com\/\">PoshChat<\/a>, and <a href=\"http:\/\/posheventui.codeplex.com\/\">PoshEventUI<\/a><\/p>\n<p>Take it away, Boe&hellip;<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong>&nbsp; The&nbsp;<a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Register-FileToDelete-0cbb00bb\" target=\"_blank\">complete script for this blog post<\/a>&nbsp;is available via the Scripting Guys Script Repository.<\/p>\n<p>We have all been there. A file that we need to delete is locked by some process that we are unable to kill for one reason or another, and we have to reboot the system in hopes that by the time we get signed in to the system, it will not have a lock on the file. Wouldn&rsquo;t it be great if we can just mark the file for deletion after a reboot and not worry about it being around by the time we sign in? If you answered, &ldquo;Yes!&rdquo; to this question, stick around because I will show you how to accomplish this task by using Windows PowerShell and hooking into the Win32 API using Pinvoke.<\/p>\n<p>One of the many beauties of Windows PowerShell is its ability to use inline code (C# for example) that can be compiled by using <strong>Add-Type<\/strong> and then called to perform an action. By doing so, you can leverage some pretty powerful low-level functions to do things that you just cannot do natively via Windows PowerShell.<\/p>\n<p>The first thing that needs to be done is determine what we can use to make this work. Looking at <a href=\"http:\/\/pinvoke.net\/\">Pinvoke.net<\/a> (a great website for finding out what is available to use with Pinvoke), I find the <strong>MoveFileEx<\/strong> function and the C# signature that we can put to work in Windows PowerShell.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7181.sg1.png\"><img decoding=\"async\" title=\"Image of function\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7181.sg1.png\" alt=\"Image of function\" \/><\/a><\/p>\n<p>Looking at the <strong>MoveFileEx<\/strong> function, I see three parameters (<strong>lpExistingFileName, lpNewFileName<\/strong>, and<strong> dwFlags<\/strong>) that are required for the function to operate correctly. The first two parameters are pretty simple to understand: we need the source file that will be deleted and then we need a destination file. If this sounds like it an issue, trust me, it isn&rsquo;t. All I need to do is specify <strong>$Null<\/strong> in its place, and the file will be marked for deletion instead. Of course, all of this rests in the hands of the <strong>dwFlags<\/strong> parameter that is still left to deal with.<\/p>\n<p>Looking at the same page, I can see a link to <a href=\"http:\/\/www.pinvoke.net\/default.aspx\/Enums\/MoveFileFlags.html\" target=\"_blank\">MoveFileFlags (Enum)<\/a> where I can get info about the available flags. Judging from the information, I only need to supply 0x4 as the flag to mark the file for deletion after a reboot. Or I can more easily create the <strong>Enum<\/strong> and reference the proper value (MOVEFILE_DELAY_UNTIL_REBOOT).<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1616.sg2.png\"><img decoding=\"async\" title=\"Image of script\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1616.sg2.png\" alt=\"Image of script\" \/><\/a><\/p>\n<p>Although this is fine, I would prefer to know what each one means. Fortunately, I can also check out the Windows Dev Center page about this: <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa365240(v=vs.85).aspx\" target=\"_blank\">MoveFileEx function<\/a>.<\/p>\n<p>From here, I find the flag that I am most curious about, <strong>MOVEFILE_DELAY_UNTIL_REBOOT<\/strong>.<\/p>\n<table style=\"width: 700px\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><strong>MOVEFILE_DELAY_UNTIL_REBOOT<\/strong><\/p>\n<p>4 (0x4)<\/p>\n<\/td>\n<td valign=\"top\">\n<p>The system does not move the file until the operating system is restarted. The system moves the file immediately after AUTOCHK is executed, but before creating any paging files. Consequently, this parameter enables the function to delete paging files from previous startups.<\/p>\n<p>This value can be used only if the process is in the context of a user who belongs to the administrators group or the LocalSystem account.<\/p>\n<p>This value cannot be used with&nbsp;<strong>MOVEFILE_COPY_ALLOWED<\/strong>.<\/p>\n<p><strong>Windows Server&nbsp;2003 and Windows&nbsp;XP:&nbsp;&nbsp;<\/strong>For information about special situations where this functionality can fail, and a suggested workaround solution, see&nbsp;<a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?linkid=117125\" target=\"_blank\">Files are not exchanged when Windows Server 2003 restarts if you use the MoveFileEx function to schedule a replacement for some files<\/a>&nbsp;in the Help and Support Knowledge Base.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;The key takeaway here is that only an Administrator can really utilize this flag to mark a file for deletion. In case you were wondering how I know that this flag is used in conjunction with the second parameter of the <strong>MoveFileEx<\/strong>, check out the description for the <strong>lpNewFileName<\/strong> parameter:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8270.sg3.png\"><img decoding=\"async\" title=\"Image of description\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8270.sg3.png\" alt=\"Image of description\" \/><\/a><\/p>\n<p>By using this information, I can create a <strong>Here-String<\/strong> and supply it to <strong>Add-Type<\/strong> to create a compiled type that can be used in our example to mark a file for deletion.<\/p>\n<p style=\"padding-left: 30px\">Add-Type @&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; using System;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; using System.Text;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; using System.Runtime.InteropServices;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; public class Posh<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public enum MoveFileFlags<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVEFILE_REPLACE_EXISTING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x00000001,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVEFILE_COPY_ALLOWED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x00000002,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVEFILE_DELAY_UNTIL_REBOOT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x00000004,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVEFILE_WRITE_THROUGH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x00000008,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVEFILE_CREATE_HARDLINK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x00000010,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVEFILE_FAIL_IF_NOT_TRACKABLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x00000020<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [DllImport(&#8220;kernel32.dll&#8221;, SetLastError = true, CharSet = CharSet.Unicode)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static bool MarkFileDelete (string sourcefile)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool brc = false;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brc = MoveFileEx(sourcefile, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return brc;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&#8220;@<\/p>\n<p>I&rsquo;ll caveat this by saying that I am not a C# developer, but I can work my way around the code to make this work the way I want it to. My original intention was to simply reference the <strong>MoveFileEx<\/strong> method with my custom type, but supplying <strong>$Null<\/strong> into the <strong>NewFileName<\/strong> parameter would not work properly. In fact, it assumes that <strong>$Null<\/strong> is the actual file name, and it would throw an error when it couldn&rsquo;t locate it. So with that, I had to dip my toes into the C# waters to make some adjustments to get this to work properly.<\/p>\n<p>We can verify that this actually worked by calling the new type with the static method:<\/p>\n<p style=\"padding-left: 30px\">[Posh]::MoveFileEx<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4130.sg4.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4130.sg4.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Perfect! Now I just need to pick out a file that I want to test this against.<\/p>\n<p style=\"padding-left: 30px\"><strong>Warning<\/strong>&nbsp;&nbsp;This has the potential to greatly cause havoc to your system if you pick the wrong file or a system file to mark for deletion! Use at your own risk!<\/p>\n<p>Well look at this, a test.txt file just waiting to be deleted:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1512.sg5.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1512.sg5.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>When you are working with this method and supplying the file name, it is important that you provide the full name of the file. Adding only the file name will have unintended results.<\/p>\n<p style=\"padding-left: 30px\">[Posh]::MarkFileDelete(&#8220;C:\\users\\Administrator\\Desktop\\TEST.txt&#8221;)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2046.sg6.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2046.sg6.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>This will return a Boolean value (True = file marked for deletion; False = something happened and needs to be looked at). To really see what the issue is other than a False value, we will have to force the error out by calling <strong>System.WindowsComponent.Win32Exception<\/strong>:<\/p>\n<p style=\"padding-left: 30px\">Throw (New-Object System.ComponentModel.Win32Exception)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7080.sg7.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7080.sg7.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>In this case, the error is nothing more than a confirmation that the command worked. But if a False value was returned, this error would let you know why it was returned.<\/p>\n<p>How can we verify that this will actually happen? The registry will provide the answer to this question. By navigating to <strong>HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SessionManager<\/strong> and looking for the <strong>PendingFileRenameOperations<\/strong> property in the registry, you should see the file name listed and nothing on the next line. (You may have multiple operations for deletions or moves in this property, which will have the original file and the new location listed directly after the original file.)<\/p>\n<p style=\"padding-left: 30px\">(Get-ItemProperty -Path &#8216;HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager&#8217; -Name &#8216;PendingFileRenameOperations&#8217;).PendingFileRenameOperations<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7658.sg8.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/7658.sg8.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p><strong>\\??\\C:\\users\\Administrator\\desktop\\test.txt<\/strong> is what we are looking for. That space after the file is there for a reason.&nbsp; What this means is that as soon as we reboot the system, this file will be gone the next time we sign in. Now queue the reboot:<\/p>\n<p style=\"padding-left: 30px\">Restart-Computer -Force<\/p>\n<p>Now we are back and signed in to the system. Let&rsquo;s check to see if the file is still around.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8880.sg9.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8880.sg9.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Nope, it is long gone. And we can also check the registry to see if we have any pending file operations:<\/p>\n<p style=\"padding-left: 30px\">(Get-ItemProperty -Path &#8216;HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager&#8217; -Name &#8216;PendingFileRenameOperations&#8217;).PendingFileRenameOperations<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3857.sg10.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3857.sg10.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Well, it is a good sign that there is nothing in the registry. So with that, you have seen how to use the <strong>MoveFileEx<\/strong> method with Pinvoke to mark a file for deletion after a reboot. You can also use this on directories, but the directory must have no child items (files or folders) in it for this to work.<\/p>\n<p>Of course, I couldn&rsquo;t stop at this. I also made a function (<strong>Register-FileToDelete<\/strong>) that makes this a much easier process and allows for verbose logging, <strong>-WhatIf<\/strong> parameter (because if you are doing something that has potential to make a change, you better have this), and handles input from the pipeline. You can download this function from the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Register-FileToDelete-0cbb00bb\" target=\"_blank\">Register-FileToDelete<\/a>.<\/p>\n<p>Here is an example of what would happen if I simply wanted to hit all .txt files, and I use the <strong>&ndash;WhatIf<\/strong> switch to avoid accidently deleting the files:<\/p>\n<p style=\"padding-left: 30px\">Get-ChildItem -File -Filter *.txt | Register-FileToDelete -WhatIf<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5873.sg11.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5873.sg11.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>In this instance, only two files would have been deleted after the reboot. So now let&rsquo;s mark the test.txt file for deletion:<\/p>\n<p style=\"padding-left: 30px\">Get-ChildItem -File -Filter test.txt | Register-FileToDelete -Verbose<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6165.sg12.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/6165.sg12.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>And there you have it! The next time this system reboots, it will delete the file.<\/p>\n<p>That is all for working with Pinvoke and utilizing the <strong>MoveFileEx<\/strong> function to make it easier to remove a file that may be locked by a process after a reboot. Hope you enjoyed it!<\/p>\n<p>~Boe<\/p>\n<p>Thank you, Boe, for an awesome post, and one that will be immediately useful to many readers.<\/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><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Windows PowerShell MVP, Boe Prox, talks about using Windows PowerShell and Pinvoke to delete locked files. Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post by newly crowned Windows PowerShell MVP, Boe Prox. Long-time readers of the Hey, Scripting Guy! Blog are familiar with Boe&rsquo;s work,&nbsp;but this is his [&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":[162,40,56,3,4,12,61,45],"class_list":["post-2711","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-boe-prox","tag-filesystemobject","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-storage","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Windows PowerShell MVP, Boe Prox, talks about using Windows PowerShell and Pinvoke to delete locked files. Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post by newly crowned Windows PowerShell MVP, Boe Prox. Long-time readers of the Hey, Scripting Guy! Blog are familiar with Boe&rsquo;s work,&nbsp;but this is his [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2711","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=2711"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2711\/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=2711"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2711"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2711"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}