Can’t Delete a VHD if you Ctrl-C the New-VHD Command
TL;WR: If you started creating a classic fixed VHD with New-VHD, then tried to cancel it with Ctrl+C you’ll probably realize that it’s still stuck initializing in the background. Use WMI to kill it.
Click here to skip to the answer
I was experimenting with some data recovery stuff and needed some classic fixed VHD’s. I started creating a 1 TB VHD with New-VHD
but needed to change it so I hit Ctrl+C and assumed that would cancel the cmdlet and the VHD creation. I assumed incorrectly.
When I tried to delete the VHD I got this error:

I figured there was still a job creating the VHD so I scanned Virtualization CIM Classes and found the CIM_ConcreteJob class, after a quick poke this seemed to be what I was looking for.
Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob | select InstanceID, Caption, Description, Name, Status, StatusDescriptions, JobStatus, PercentComplete, Cancellable
InstanceID : "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"
Caption : Virtual Hard Disk Creation
Description : Creating Virtual Hard Disk
Name : Virtual Hard Disk Creation
Status : OK
StatusDescriptions : {Job is running}
JobStatus : Job is running
PercentComplete : 7
Cancellable : True
Now, about killing it: CIM_ConcreteJob.RequestStateChange(RequestedState, TimeoutPeriod). Looks simple enough!
Alright, lets try to request a safe/clean termination with a 30 second timeout.
$job = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'
$job | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = (Get-Date).AddSeconds(30)}
ReturnValue PSComputerName
----------- --------------
32773
Annnd the return code isn’t in the list. On the upside that looks like a common return code (we’ll come back to that in a minute). Let’s try the Kill command:
$job | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 5;TimeoutPeriod = (Get-Date).AddSeconds(30)}
----------- --------------
32773
Great, same error. The doc says you can supply a null to indicate there’s no timeout requirement so we’ll try that instead:
$job | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 5;TimeoutPeriod = $null}
ReturnValue PSComputerName
----------- --------------
32775
Sigh, At least it’s different error. Alright, assumption rechecking time. First, what is the specific Job implementation?
$job | Get-Member -View Base
#TypeName: Microsoft.Management.Infrastructure.CimInstance#root/virtualization/v2/Msvm_StorageJob
Looks like this is an Msvm_StorageJob, let’s see if it’s implementation of RequestStateChange has any juicy details!
The good news is that we have the return codes, the bad news is that they don’t have any descriptions or names. Let’s add a new assumption for a minute and assume that the GetError method’s return values apply to the RequestStateChange method. Now our errors mean Invalid Parameter
and Invalid State
although that doesn’t get us very far.
Scraped Values
Code | Description |
---|---|
Failed | (32768) |
Access Denied | (32769) |
Not Supported | (32770) |
Status is unknown | (32771) |
Timeout | (32772) |
Invalid parameter | (32773) |
System is in used *[sic]* | (32774) |
Invalid state for this operation | (32775) |
Incorrect data type | (32776) |
System is not available | (32777) |
Out of memory | (32778) |
Method Parameters Checked - Transition Started | (4096) |
Invalid State Transition | (4097) |
Use of Timeout Parameter Not Supported | (4098) |
Busy | (4099) |
Quality Microsoft documentation

Alright, maybe it is an invalid parameter. Possibly the .Net DateTime to CIM_DATETIME conversion getting botched? Let’s see if the same thing happens using WMIExplorer.

Good News: Calling convention in powershell at least matches the WMI Explorer results. Well, we have another option. The New Disk Wizard IS cancellable so let’s see if we can’t reverse engineer how it works. First up we’re going to do a simple WMI trace using event viewer Using these instructions and we see multiple events for RequestStateChange on Msvm_StorageJob instances, hmm…
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; ProcessId = 57856; Protocol = DCOM; Operation = MI_Session::Invoke; User = NULL; Namespace = root\virtualization\v2
---
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; GroupOperationId = 117375; OperationId = 117376; Operation = Start IWbemServices::ExecMethod - root\virtualization\v2 : \\.\ROOT\virtualization\v2:Msvm_StorageJob.InstanceID="7C8C39C1-03ED-456B-9A31-1B7A456BA347"::RequestStateChange; ClientMachine = [REDACTED]; User = [REDACTED]\[REDACTED]; ClientProcessId = 57856; NamespaceName = 133044712970425349
---
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; GroupOperationId = 117375; OperationId = 112244; ClassName= Msvm_StorageJob; MethodName = RequestStateChange; ImplementationClass = Msvm_StorageJob; ClientMachine = [REDACTED]; User = [REDACTED]\[REDACTED]; ClientProcessId = 57856; NamespaceName = \\.\root\virtualization\v2
---
ProviderInfo for GroupOperationId = 117375; Operation = Provider::ExecMethod - VmmsWmiInstanceAndMethodProvider : Msvm_StorageJob.InstanceID="7C8C39C1-03ED-456B-9A31-1B7A456BA347"::RequestStateChange; HostID = 2188; ProviderName = VmmsWmiInstanceAndMethodProvider; ProviderGuid = {0c172fd4-1b2a-11da-994c-0008744f51f3}; Path =
---
Stop OperationId = 117376; ResultCode = 0x0
Unfortunately that didn’t show me the parameters (which makes sense, in WMI method parameters are packed into an object) but it does verify that we’re going about it the right way. To find the parameters we’re going to have to bust out API Monitor.


Aside I just want to point out how amazingly handy this tool is. It goes much deeper than procmon/sysmon/WPA/WPP traces (unless you somehow manage to get the Microsoft private symbols/TMFs) and has a ton of accellerators that make ‘getting the job done’ faster than if you have to bust out dnSpy/dotpeek/windbg/ida/etc. In this case it’s automatically capturing the contents of the objects in the parameter pointers and saving them. No manual breakpoint necessary. I’ve even used API Monitor to hijack a firmware update tool and edit it’s ioctls on the fly to force it to update a device that the OEM didn’t want updated. Long story.
Aside-Aside Sorry for the zoom & swoop effect, middle ground between “open full image in new tab” and “fancy overlay with buttons and stuff”.
You can see the documentation on how you pass parameters to a WMI Method here but the gist is that the wizard uses GetMethod to have WMI build you the __PARAMETERS object and PutMethod to populate the properties, then when ExecMethodAsync is called you just give it the pointer to your populated object. API monitor lets you see what values the Wizard is packing into that object before the call happens.
A quick analysis shows that mmc is building the request with RequestedState of 4 and a TimeoutPeriod of null. I’m totally certain that we tried that earlier (Narrator: We didn’t.) it must be something else going on. Let’s see if we can manually cancel a VHD creation started from the wizard!
$tjob = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "GUID-INSTANCE-ID-FROM-MMC"'
$tjob | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = $null}
ReturnValue PSComputerName
----------- --------------
4096
4096 isn’t listed as a storage job result but looking at the CIM_ConcreteJob docs that’s Method Parameters Checked - Transition Started (4096)
! It worked! So why can’t we cancel the VHD creation started via powershell? The two CIM jobs appeared identical!
Oh no…
Oh no…
Guess what combination of RequestedState’s and TimeoutPeriod’s we didn’t try?!
$jobc = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'
$jobc | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = $null}
ReturnValue PSComputerName
----------- --------------
4096
If I had tried a few different parameter combinations off the bat (honestly there weren’t that many) I would’ve probably figured this out 90 minutes ago. On the other hand, if this had been documented in any way I wouldn’t have had to guess all of that.
How to cancel a fixed VHD creation started by New-VHD
# Step 1. Find the InstanceID of your job
Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob | select InstanceID, Caption, Description, Name, Status, StatusDescriptions, JobStatus, PercentComplete, Cancellable
#I only had one job running so it was easy enough to figure out.
InstanceID : D65CC6A8-E7B4-45E4-A13D-CF7BAD642912
Caption : Virtual Hard Disk Creation
Description : Creating Virtual Hard Disk
Name : Virtual Hard Disk Creation
Status : OK
StatusDescriptions : {Job is running}
JobStatus : Job is running
PercentComplete : 7
Cancellable : True
# Step 2. Grab and cancel the Job
$jobc = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'
$jobc | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = $null}