{"id":2499,"date":"2013-12-01T00:01:00","date_gmt":"2013-12-01T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/12\/01\/weekend-scripter-determine-process-that-locks-a-file\/"},"modified":"2013-12-01T00:01:00","modified_gmt":"2013-12-01T00:01:00","slug":"weekend-scripter-determine-process-that-locks-a-file","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-determine-process-that-locks-a-file\/","title":{"rendered":"Weekend Scripter: Determine Process that Locks a File"},"content":{"rendered":"<p><strong>Summary<\/strong>: Guest blogger, Adam Driscoll, talks about using Windows PowerShell to determine which process is locking a file.\nMicrosoft Scripting Guy, Ed Wilson, is here. Today we have a guest blogger, Adam Driscoll&#8230;\nIt&rsquo;s quite common to run into the issue where a process is locking a particular file. Attempting to delete or open the file can result in an error message when another process has exclusive access to the file. Unfortunately, Windows PowerShell nor the .NET Framework expose a simple way of determining which process is locking the file. Determine which process is locking the file requires quite a few steps to interrogate the system. This can be accomplished by using a set of Windows API calls and Platform Invoke (PInvoke) functionality. In this post, we&rsquo;ll take a tour of what it takes to identify the process that is locking a file.\nThe first step is to understand how Windows manages files and other objects. When opening a file in Windows, the kernel provides a handle to the calling process to identify the opened file. A handle is a kernel-level identifier to an open file or other Windows object. There are handles for all types of Windows objects, including files, events, and mutexes.\nTo view the open handles on the system, we need to utilize the <strong>NtQuerySystemInformation<\/strong> function. This function is largely undocumented, and it requires some intimate knowledge to use correctly. The first step is to call several Win32 functions and structures that we will need for this operation. This can be accomplished by using the <strong>Add-Type <\/strong>cmdlet. The functions involved include:<\/p>\n<p style=\"padding-left: 30px\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms724509(v=vs.85).aspx\" target=\"_blank\">NtQuerySystemInformation<\/a>: Amongst other things, can query the handles open on the system.<\/p>\n<p style=\"padding-left: 30px\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb432383(v=vs.85).aspx\" target=\"_blank\">NtQueryObject<\/a>: Queries additional information about a handle.<\/p>\n<p style=\"padding-left: 30px\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms684320(v=vs.85).aspx\" target=\"_blank\">OpenProcess<\/a>: Allows us to get more information about the process that owns the handle.<\/p>\n<p style=\"padding-left: 30px\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms724251(v=vs.85).aspx\" target=\"_blank\">DuplicateHandle<\/a>: Creates a copy of a handle in the current process (for example, ISE) so we can perform additional operations on it.<\/p>\n<p style=\"padding-left: 30px\"><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa365461(v=vs.85).aspx\" target=\"_blank\">QueryDosDevice<\/a>: Converts a logical drive (such as drive C) into the DOS device identifier.<\/p>\n<p style=\"padding-left: 30px\"><strong>SystemHandleEntry<\/strong>: Describes the memory structure of handle information that is returned by <strong>NtQuerySystemInformation<\/strong>.\nFor more information about the signatures that are required to use these functions, refer to <a href=\"http:\/\/www.pinvoke.net\/\" target=\"_blank\">PInvoke.net<\/a>.\nNow we need to define the <strong>PInvoke<\/strong> functions. This has been eliminated in this document for brevity, but it is included in the script posted in the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Find-LockingProcess-ea47c3c0\" target=\"_blank\">Find-LockingProcess<\/a>.\nThe next step is to query the handles that are currently open on the system. The following snippet, which is part of a larger function, queries the open handles on the system. We pass 16 into the <strong>NtQuerySystemInformation<\/strong> function to signify that we want it to return open handles. We then pass in <strong>IntPtr<\/strong>, which will hold the information return by the system.\nThe <strong>IntPtr<\/strong> object is used to hold the address of a chunk of memory that has been provided or allocated to us. The first time we fall through the <strong>while<\/strong> loop, it returns the size of the data that we need and then we allocate it by using the <strong>AllocHGlobal<\/strong> function. This returns a segment of memory large enough to hold the data.\nThe <strong>IntPtr<\/strong> object points to the beginning of that segment of memory. The next time we cycle through the loop, we have enough memory available to store all the handle data and <strong>NtQuerySystemInformation<\/strong> fills our memory segment with that data.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ($true)<\/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; $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($length)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $wantedLength = 0<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; $SystemHandleInformation = 16<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $result = [NtDll]::NtQuerySystemInformation($SystemHandleInformation, $ptr, $length, [ref] $wantedLength)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($result -eq [NT_STATUS]::STATUS_INFO_LENGTH_MISMATCH)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp; $length = [Math]::Max($length, $wantedLength)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ptr)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ptr = [IntPtr]::Zero<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&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; elseif ($result -eq [NT_STATUS]::STATUS_SUCCESS)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp; break<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; else<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp; throw (New-Object System.ComponentModel.Win32Exception)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }\nFrom here, we can convert the handle information into a .NET structure that can be used easily in Windows PowerShell&mdash;much like any other object. We need to loop numerous times because we have multiple <strong>SystemHandleEntry<\/strong> structures to create&mdash;one for each open handle. We actually create the structures by using the <strong>Marshal.PtrToStructure<\/strong> method in the .NET Framework.\nYou may have noticed that we are incrementing an offset by using the size of the <strong>SystemHandleEntry<\/strong>. This is because we have a big blob of data, and we need to move sequentially down it to get each <strong>SystemHandleEntry<\/strong>. You can think of this as a big row of Lego blocks. We know the size of one block and instruct the <strong>PtrToStructure<\/strong> method to where the beginning of the each block is. It breaks that block off and returns it as a <strong>SystemHandleEntry<\/strong>.\nNote that I am evaluating whether the <strong>ObjectTypeNumber<\/strong> is 31. This is the file object type number for Windows&nbsp;8. Other operating systems have different identifying numbers. To reduce the size of this post, I&rsquo;ve eliminated the logic to determine that number, but it is possible to do so dynamically.<\/p>\n<p style=\"padding-left: 30px\">$offset = [IntPtr]::Size<\/p>\n<p style=\"padding-left: 30px\">$size = [System.Runtime.InteropServices.Marshal]::SizeOf([SystemHandleEntry])<\/p>\n<p style=\"padding-left: 30px\">for ($i = 0; $i -lt $handleCount; $i++)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">[SystemHandleEntry]$FileHandle = [System.Runtime.InteropServices.Marshal]::PtrToStructure([IntPtr]([long]$ptr + $offset), [SystemHandleEntry])<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">if ($FileHandle.ObjectTypeNumber -eq 31)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">$FileHandle | ConvertTo-HandleHashTable<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">$offset += $size<\/p>\n<p style=\"padding-left: 30px\">}\nWe have also defined a <strong>ConvertTo-HandleHashTable<\/strong> function. The purpose of this function is to convert the handle information into a useful hash table that will contain the path to the file and a System.Diagnostics.Process object. This is much more useful than the memory addresses and process IDs that currently populate the <strong>SystemHandleEntry<\/strong> structures that we just created.\nInside this function, we need to duplicate the handle returned by <strong>NtQuerySystemInformation<\/strong>. If we do not create a copy of the handle, we&rsquo;ll be unable to query additional information about it. We use <strong>OpenProcess<\/strong> to get a handle to the process that owns the original handle. This is necessary for the <strong>DuplicateHandle<\/strong> function. The <strong>DuplicateHandle<\/strong> function is what provides us with a malleable handle inside our process.<\/p>\n<p style=\"padding-left: 30px\">$sourceProcessHandle = [IntPtr]::Zero<\/p>\n<p style=\"padding-left: 30px\">$handleDuplicate = [IntPtr]::Zero<\/p>\n<p style=\"padding-left: 30px\">$currentProcessHandle = (Get-Process -Id $Pid).Handle<\/p>\n<p style=\"padding-left: 30px\">$sourceProcessHandle = [Kernel32]::OpenProcess(0x40, $true, $HandleEntry.OwnerProcessId)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">if (-not [Kernel32]::DuplicateHandle($sourceProcessHandle,<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [IntPtr]$HandleEntry.Handle,<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $currentProcessHandle,<\/p>\n<p style=\"padding-left: 30px\">&nbsp; [ref]$handleDuplicate,<\/p>\n<p style=\"padding-left: 30px\">&nbsp; 0,<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $false,<\/p>\n<p style=\"padding-left: 30px\">&nbsp; 2))<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; return<\/p>\n<p style=\"padding-left: 30px\">}\nNext, we use the <strong>NtQueryObject<\/strong> function to get additional information about the handle. We use the duplicate handle because we will not have the proper rights on the original handle. In this case, we are calling <strong>NtQueryObject<\/strong> with the <strong>ObjectNameInformation<\/strong> flag. This tells <strong>NtQueryObject<\/strong> that we want the name of the handle. In regards to a file handle, this is the path to the file.<\/p>\n<p style=\"padding-left: 30px\">$length = 0<\/p>\n<p style=\"padding-left: 30px\">[NtDll]::NtQueryObject($handleDuplicate,&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [OBJECT_INFORMATION_CLASS]::ObjectNameInformation,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [IntPtr]::Zero,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref]$length) | Out-Null<\/p>\n<p style=\"padding-left: 30px\">$ptr = [IntPtr]::Zero<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($length)<\/p>\n<p style=\"padding-left: 30px\">if ([NtDll]::NtQueryObject($handleDuplicate,<\/p>\n<p style=\"padding-left: 30px\">&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;[OBJECT_INFORMATION_CLASS]::ObjectNameInformation,<\/p>\n<p style=\"padding-left: 30px\">&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; $ptr,<\/p>\n<p style=\"padding-left: 30px\">&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; $length,<\/p>\n<p style=\"padding-left: 30px\">&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; [ref]$length) -ne [NT_STATUS]::STATUS_SUCCESS)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp; return<\/p>\n<p style=\"padding-left: 30px\">}\nWhen we have the name as an <strong>IntPtr<\/strong> object, we need to convert it into a .NET string. To do this, we use the <strong>Marshal.PtrToStringUni<\/strong> method. It converts a memory address into a useable .NET string.<\/p>\n<p style=\"padding-left: 30px\">$Path = [System.Runtime.InteropServices.Marshal]::PtrToStringUni([IntPtr]([long]$ptr+ 2 * [IntPtr]::Size))\nFinally, we create a new <strong>PSCustomObject<\/strong> that contains the path to the file and the <strong>Process<\/strong> object that owns the handle in question. You can see that we are using a <strong>ConvertTo-RegularFileName<\/strong> function. This is due to the fact that the name returned by <strong>NtQueryObject<\/strong> will be in the DOS-style format. This format looks similar to: DeviceHarddiskVolume1myfile.txt.\nPerforming a comparison on a path like this would likely fail later. The result of <strong>ConvertTo-RegularFileName<\/strong> will instead be in this format: C:MyFile.txt.\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [PSCustomObject]@{\n&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; Path=(ConvertTo-RegularFileName $Path);\n&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; Process=(Get-Process -Id $HandleEntry.OwnerProcessId);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }\nThe <strong>ConvertTo-RegularFileName<\/strong> function uses the <strong>QueryDosDevice<\/strong> function to convert a logical drive (such as drive C) into a DOS style drive (for example, DeviceHarddiskVolume1). We use <strong>Environment::GetLogicalDrives<\/strong> to return all the drives on the current machine. Then we convert them to DOS style and fix up the raw path that is returned by <strong>NtQueryObject<\/strong>.<\/p>\n<p style=\"padding-left: 30px\">foreach ($logicalDrive in [Environment]::GetLogicalDrives())<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $targetPath = New-Object System.Text.StringBuilder 256<\/p>\n<p style=\"padding-left: 30px\">if ([Kernel32]::QueryDosDevice($logicalDrive.Substring(0, 2), $targetPath, 256) -eq 0)<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $targetPath<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $targetPathString = $targetPath.ToString()<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($RawFileName.StartsWith($targetPathString))<\/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; $RawFileName = $RawFileName.Replace($targetPathString,&nbsp;&nbsp;&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;&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;&nbsp; $logicalDrive.Substring(0, 2))<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">$RawFileName\nThis all comes together in the most useful of the functions, <strong>Find-LockingProcess<\/strong>. This advanced function accepts a file info object (as returned by <strong>Get-ChildItem<\/strong>), or a raw path, and it finds handles that are open for that path. When it finds the handle, it returns the <strong>Process<\/strong> object for the locking file.<\/p>\n<p style=\"padding-left: 30px\">function Find-LockingProcess<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [CmdletBinding()]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(ValueFromPipeline=$true,ParameterSetName=&#8221;Pipeline&#8221;,Mandatory)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [System.IO.FileInfo]$InputObject,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(ValueFromPipeline=$true,ParameterSetName=&#8221;Path&#8221;,Mandatory)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [String]$Path<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp; Begin {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Handles = Get-FileHandle<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp; Process<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($InputObject)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Handles | Where-Object { $_.Path -eq $InputObject.FullName } | Select-Object -ExpandProperty Process<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($Path)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Handles| Where-Object { $_.Path -contains $Path } | Select-Object -ExpandProperty Process<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; }<\/p>\n<p style=\"padding-left: 30px\">}\nTo use this function, we could then specify a full file path. We will get output that references all the processes that have a handle open to the file. It&rsquo;s worth noting that not all the process may be locking the file, but only a process that currently has a handle can be locking it. In the following example, <strong>Steam<\/strong> is locking the file and <strong>devenv<\/strong> is not.<\/p>\n<p style=\"padding-left: 30px\">Find-LockingProcess -Path &#8220;E:Program Files (x86)Steamsteam.log&#8221;<\/p>\n<p style=\"padding-left: 30px\">Handles&nbsp; NPM(K)&nbsp;&nbsp;&nbsp; PM(K)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WS(K) VM(M)&nbsp;&nbsp; CPU(s)&nbsp;&nbsp;&nbsp;&nbsp; Id ProcessName&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;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&#8212;-&nbsp; &#8212;&#8212;&nbsp;&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8211; &#8212;&#8211;&nbsp;&nbsp; &#8212;&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212; &#8212;&#8212;&#8212;&#8211;&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;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; 685&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 85&nbsp;&nbsp; 218692&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 37356&nbsp;&nbsp; 536&nbsp;&nbsp; 116.84&nbsp;&nbsp; 4420 Steam&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;&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;&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;&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;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; 73285&nbsp;&nbsp;&nbsp;&nbsp; 248&nbsp;&nbsp; 279136&nbsp;&nbsp;&nbsp;&nbsp; 366676&nbsp;&nbsp; 887&nbsp;&nbsp; 550.61&nbsp;&nbsp; 3344 devenv&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;&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;&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;&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;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; 74622&nbsp;&nbsp;&nbsp;&nbsp; 249&nbsp;&nbsp; 267796&nbsp;&nbsp;&nbsp;&nbsp; 359196&nbsp;&nbsp; 904&nbsp;&nbsp; 550.68&nbsp;&nbsp; 3344 devenv &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; &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;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; 75959&nbsp;&nbsp;&nbsp;&nbsp; 246&nbsp;&nbsp; 282880&nbsp;&nbsp;&nbsp;&nbsp; 369672&nbsp;&nbsp; 881&nbsp;&nbsp; 550.76&nbsp;&nbsp; 3344 devenv &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; &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;<\/p>\n<p style=\"padding-left: 30px\">&nbsp; 88123&nbsp;&nbsp;&nbsp;&nbsp; 247&nbsp;&nbsp; 307540&nbsp;&nbsp;&nbsp;&nbsp; 389004&nbsp;&nbsp; 897&nbsp;&nbsp; 551.17&nbsp;&nbsp; 3344 devenv&nbsp;&nbsp;&nbsp;\nThe full script can be found in the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Find-LockingProcess-ea47c3c0\" target=\"_blank\">Find-LockingProcess<\/a>. You will likely need to run your Windows PowerShell host as an administrator to successfully execute this script.\n~Adam\nThank you, Adam, for taking your time to share with our readers.\nI 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=\"http:\/\/blogs.technet.commailto: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.\n<strong>Ed Wilson, Microsoft Scripting Guy<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger, Adam Driscoll, talks about using Windows PowerShell to determine which process is locking a file. Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blogger, Adam Driscoll&#8230; It&rsquo;s quite common to run into the issue where a process is locking a particular file. Attempting to delete or open the [&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":[334,56,31,87,3,61,45],"class_list":["post-2499","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-adam-driscoll","tag-guest-blogger","tag-operating-system","tag-processes","tag-scripting-guy","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger, Adam Driscoll, talks about using Windows PowerShell to determine which process is locking a file. Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blogger, Adam Driscoll&#8230; It&rsquo;s quite common to run into the issue where a process is locking a particular file. Attempting to delete or open the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2499","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=2499"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2499\/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=2499"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2499"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2499"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}