Friday, February 3, 2017

Powershell Howto: Arrays of Stucts/Objects Using [pscustomobject]

First off, I am not a regular powershell scripter, but I was writing a powershell script to automate some e2e (end to end) testing.  I recently finished a feature in one our DEHes (deployment extension handler used for installing appx packages in Windows), so we needed a test for our nightly test passes.

I wanted to make an array of appx packages that I wanted to test in different scenarios, I also wanted to keep track of other data in the element like expected outcomes for trying to deploy the app in various developer settings.  In C, I would simply define a structure with the data I wanted for each element, and then define a static array of these structs for each appx package.

In powershell, you can use a pscustomobject to make something like a C struct.  Thy syntax is like this:

$obj = [pscustomobject]@{name="good appx";canSideLoad=$true;canInstallDevMode=$true;path="c:\test\packages\test_app_1.1.34.0_good_appx\"}

Now, to make that an array of objects:
$packages = @(

Aside: You can use a hash table if you have a two item tuple.  The syntax for a hash table looks like this:
$packagePaths = @{"case1" = "\\path1..."; "case2" = "\\path2...";}

How do you walk the array of [pscustomobject] using a for loop?

for ($i = 0; $i -lt $packages.Count; $i++) {
    $basePath = $packages[$i].path

Thursday, April 14, 2016

How to Find a RPC Server's Clients Via WinDbg

Need to level up your Windows RPC debugging skills?  Read on.

I have been debugging a service stop hang.  The simple conclusion is that my service had RPC clients hanging around.  This is a little how to for how to use windbg to figure out who the offending client is.

The first thing is obvious, the SCM is blocked on thread 0 because my service main has not exited, so let's find that thread.  Obviously it is thread 1. What is that thread blocked on?  It is trying to close out its RPC server interfaces.

What is RPC waiting for?  Normally it is outstanding clients.

0:001> ~* kn

   0  Id: 1668.17fc Suspend: 1 Teb: 00331000 Unfrozen
 # ChildEBP RetAddr
00 0008fc74 775bddba ntdll!KiFastSystemCallRet [d:\rs1\minkernel\ntos\rtl\i386\userdisp.asm @ 818]
01 0008fc78 749533d0 ntdll!NtWaitForSingleObject+0xa [d:\rs1.obj.x86fre\minkernel\ntdll\daytona\objfre\i386\usrstubs.asm @ 217]
WARNING: Stack unwind information not available. Following frames may be wrong.
02 0008fcec 74953312 KERNELBASE!WaitForSingleObjectEx+0xb0
03 0008fd00 76b5214c KERNELBASE!WaitForSingleObject+0x12
04 0008fdc0 76b5deb1 sechost!StartServiceW+0x1dc
05 0008fe70 76b5102c sechost!RpcClientCapabilityCheck+0x851
06 0008feac 00ab32c0 sechost!StartServiceCtrlDispatcherW+0x5c
07 0008fed0 76aba954 myhost+0x32c0
08 0008fee4 7759a16a KERNEL32!BaseThreadInitThunk+0x24
09 0008ff2c 7759a139 ntdll!__RtlUserThreadStart+0x2b [d:\rs1\minkernel\ntdll\rtlstrt.c @ 997]
0a 0008ff3c 00000000 ntdll!_RtlUserThreadStart+0x1b [d:\rs1\minkernel\ntdll\rtlstrt.c @ 914]

   1  Id: 1668.400 Suspend: 1 Teb: 00336000 Unfrozen
 # ChildEBP RetAddr
00 0087f480 775c04ca ntdll!KiFastSystemCallRet [d:\rs1\minkernel\ntos\rtl\i386\userdisp.asm @ 818]
01 0087f484 7495dc38 ntdll!NtDelayExecution+0xa [d:\rs1.obj.x86fre\minkernel\ntdll\daytona\objfre\i386\usrstubs.asm @ 2769]
WARNING: Stack unwind information not available. Following frames may be wrong.
02 0087f4ec 7495db8f KERNELBASE!SleepEx+0x98
03 0087f4fc 76c111d0 KERNELBASE!Sleep+0xf
04 (Inline) -------- RPCRT4!PauseExecution+0xb [d:\rs1\minio\rpc\runtime\mtrt\threads.cxx @ 88]
05 0087f538 76be2609 RPCRT4!RPC_SERVER::UnregisterIf+0x2f996 [d:\rs1\minio\rpc\runtime\mtrt\hndlsvr.cxx @ 4838]
06 0087f574 76be2318 RPCRT4!RPC_SERVER::DeactivateInterfaceGroup+0xf3 [d:\rs1\minio\rpc\runtime\mtrt\hndlsvr.cxx @ 11256]
07 0087f5a0 69d78b0b RPCRT4!RPC_SERVER::CloseInterfaceGroup+0xe5 [d:\rs1\minio\rpc\runtime\mtrt\hndlsvr.cxx @ 10484]
08 0087f5b0 69d7c04d my_svc!DestroyRpcServices+0x1b [s:\epix_dev\onecore\base\devices\my_svc\rpcif.cpp @ 540]
09 0087f658 69d7ca5a my_svc!MySvcStop+0x27d [s:\epix_dev\onecore\base\devices\my_svc\srventry.cpp @ 198]
0a 0087f660 77584eee my_svc!MySvcStopThreadProc+0xa [s:\epix_dev\onecore\base\devices\my_svc\srventry.cpp @ 350]
0b 0087f6c4 77575cc2 ntdll!TppSimplepExecuteCallback+0x6e [d:\rs1\minkernel\threadpool\ntdll\simple.c @ 376]
0c 0087f8c8 76aba954 ntdll!TppWorkerThread+0x902 [d:\rs1\minkernel\threadpool\ntdll\worker.c @ 1075]
0d 0087f8dc 7759a16a KERNEL32!BaseThreadInitThunk+0x24
0e 0087f924 7759a139 ntdll!__RtlUserThreadStart+0x2b [d:\rs1\minkernel\ntdll\rtlstrt.c @ 997]
0f 0087f934 00000000 ntdll!_RtlUserThreadStart+0x1b [d:\rs1\minkernel\ntdll\rtlstrt.c @ 914]

   2  Id: 1668.1118 Suspend: 1 Teb: 00337000 Unfrozen
 # ChildEBP RetAddr
00 008efc68 775bdd9a ntdll!KiFastSystemCallRet [d:\rs1\minkernel\ntos\rtl\i386\userdisp.asm @ 818]
01 008efc6c 77575666 ntdll!NtWaitForWorkViaWorkerFactory+0xa [d:\rs1.obj.x86fre\minkernel\ntdll\daytona\objfre\i386\usrstubs.asm @ 209]
02 008efe7c 76aba954 ntdll!TppWorkerThread+0x2a6 [d:\rs1\minkernel\threadpool\ntdll\worker.c @ 876]
WARNING: Stack unwind information not available. Following frames may be wrong.
03 008efe90 7759a16a KERNEL32!BaseThreadInitThunk+0x24
04 008efed8 7759a139 ntdll!__RtlUserThreadStart+0x2b [d:\rs1\minkernel\ntdll\rtlstrt.c @ 997]
05 008efee8 00000000 ntdll!_RtlUserThreadStart+0x1b [d:\rs1\minkernel\ntdll\rtlstrt.c @ 914]

   3  Id: 1668.dc0 Suspend: 1 Teb: 00338000 Unfrozen
 # ChildEBP RetAddr

Lets figure out what is going on with unregistering the interface.  So, we jump to frame 5.  Well it looks like some of the locals are optimized out.


0:001> .frame 5
05 0087f538 76be2609 RPCRT4!RPC_SERVER::UnregisterIf+0x2f996 [d:\rs1\minio\rpc\runtime\mtrt\hndlsvr.cxx @ 4838]
0:001> ?? RpcInterface
class RPC_INTERFACE * 0x00000000
0:001> dv -v
@ebx                                 this = 0x0087f4c8
0087f540          RpcInterfaceInformation = 0x69d22450
@edi                      ManagerTypeUuid = 0x00000000
0087f548           WaitForCallsToComplete = 1
0087f54c                      FromIfGroup = 0x005f4be0
0087f518                           cursor = 0
                  RpcStatus =
@esi                         RpcInterface = 0x00000000
    fNewValidInterfaceFound =
                     Status =
0087f524                         NullUUID = {00000000-0000-0000-0000-000000000000}
                    IfGroup =
                    Address =


But, the stack tells me that thread 1 is sleep spinning, waiting for something.  Let's see what RPC calls are outstanding.

Looks like there is MyIf RPC context hanging out still.


0:001> !rpcexts.listcalls

RPC_SERVER at 0x5e3180
&RpcAddressDictionary(RPC_SIMPLE_DICT) - 0x5e31b4
Printing 1 items in dictionary: 5e31b4 with 4 slots

(0): 0x5f6e60 - LRPC_ADDRESS
Printing 1 items in dictionary: 5f6ee8 with 4 slots

(0): 0x5f8c10 - LRPC_SMYIF_TYPE

0:001> !obj 0x5f8c10
Dumping LRPC_SMYIF...

LpcServerPort(HANDLE)   - 0x174
Address     - 0x5f6e60
&Bindings(LRPC_SBINDING_DICT)  - 0x5f8c34
&MyIfMutex   - 0x5f8c50
&SContextDict    - 0x5f8c68

0:001> dt RPCRT4!LRPC_SMYIF 0x5f8c10 
   +0x000 __VFN_table : 0x76ba9500 
   +0x004 MagicLong        : 0x89abcdef
   +0x008 ObjectType       : 0n32768
   +0x00c RefCount         : INTERLOCKED_INTEGER (0x1)
   +0x010 MyIfID    : 1
   +0x014 CtxCollection    : 0x005f1a80 ContextCollection
   +0x018 TableIndex       : 0
   +0x01c LpcServerPort    : 0x00000174 Void
   +0x020 Address          : 0x005f6e60 LRPC_ADDRESS
   +0x024 Bindings         : LRPC_SBINDING_DICT
   +0x040 MyIfMutex : MUTEX
   +0x058 SContextDict     : LRPC_SCONTEXT_DICT
   +0x078 MsgNumbers       : tagLrpcMessageNumbers
   +0x080 CloseMessageNumber : 4
   +0x084 IoTarget         : tagLRPC_IO_TARGET
   +0x090 BindingsCollectionLock : RPC_SRWLOCK
   +0x094 ActiveCallsListHead : _LIST_ENTRY [ 0x5f89ec - 0x5f89ec ]
   +0x09c CausalFlowsTable : GenericTable
   +0x0e0 CachedCalls      : LRPC_CACHED_SCALLS
   +0x0f0 Flags            :  (0x0)
   +0x0f8 SectionCache     : LRPC_SECTION_CACHE
   +0x158 PipeHistoryData  : (null) 
   +0x15c CancelHistoryInformation : LRPC_CANCEL_HISTORY_INFORMATION
   +0x170 CorrelationId    : 0x1124cb
   +0x174 DisconnectNotificationEvent : (null) 


Lets figure out what outstanding calls there are for that context by looking at the active calls list from above.

Let's take a look at the first one.


0:001> ?0x5f8c10 +94
Evaluate expression: 6261924 = 005f8ca4
0:001> !obj 0x5f89ec
0:001> dt rpcrt4!LRPC_SCALL
   +0x000 __VFN_table : Ptr32
   +0x004 MagicLong        : Uint4B
   +0x008 ObjectType       : Int4B
   +0x00c RefCount         : INTERLOCKED_INTEGER
   +0x010 pAsync           : Ptr32 _RPC_ASYNC_STATE
   +0x014 AsyncStatus      : Int4B
   +0x018 CallingThread    : Ptr32 THREAD
   +0x01c ReservedNotificationsLock : CSpinLock
   +0x020 CachedNotificationInfo : RPC_RESERVED_NOTIFICATION_INFO
   +0x03c CachedNotificationInfoAvailable : Int4B
   +0x04!opj 0 ReservedNotifications : RPC_RESERVED_NOTIFICATION_INFO_DICT2
   +0x06c ActivityID       : _GUID
   +0x07c DispatchBuffer   : Ptr32 Void
   +0x080 Flags            : SCALL_CompositeFlags
   +0x084 SubscribeInfo    : Ptr32 NotificationSubscriptionInformation
   +0x088 ActiveContextHandles : ServerContextHandle_DICT
   +0x0a4 AsyncCallbackState : Ptr32 ASYNC_SEC_CALLBACK_STATE
   +0x0a8 Association      : Ptr32 LRPC_SASSOCIATION
   +0x0ac SBinding         : Ptr32 LRPC_SBINDING
   +0x0b0 DebugCell        : Ptr32 tagDebugCallInfo
   +0x0b4 DebugCellTag     : Int4B
   +0x0b8 RpcMessage       : _RPC_MESSAGE
   +0x0e4 RuntimeInfo      : _RPC_RUNTIME_INFO
   +0x0f0 ClientPrincipalName : Ptr32 Wchar
   +0x0f4 ClientRequest    : Ptr32 _LRPC_REQUEST_MESSAGE
   +0x0f8 Response         : Ptr32 _LRPC_RESPONSE_MESSAGE
   +0x0fc AlpcPortSection  : Ptr32 Void
   +0x100 LargeReplyDataBuffer : Ptr32 Void
   +0x104 LargeRequestDataBuffer : Ptr32 Void
   +0x108 LargeReplyDataSize : Uint4B
   +0x10c LargeReplyViewSize : Uint4B
   +0x110 ClientId         : _CLIENT_ID
   +0x118 ObjectUuidFlag   : Int4B
   +0x11c ObjectUuid       : RPC_UUID
   +0x12c CallId           : Uint4B
   +0x130 SContext         : Ptr32 LRPC_SCONTEXT
   +0x134 ActiveCallsEntry : _LIST_ENTRY
   +0x13c CausalFlowEntry  : _LIST_ENTRY
   +0x144 CallCausalFlowNumber : Uint4B
   +0x148 AdditionalCallData : LRPC_SCALL::
   +0x14c SystemHandles    : LRPC_SYSTEM_HANDLE_DATA
   +0x15c TokenAttributes  : RPCP_ALPC_TOKEN_ATTR
0:001> ?0x5f89ec  -134
Evaluate expression: 6260920 = 005f88b8
0:001> !obj 005f88b8
Dumping LRPC_SCALL ...

AsyncStatus    - 0x0
pAsync     - 0x5f21b0
CallingThread    - 0x0
&ActiveContextHandles(ServerContextHandle_DICT)  - 0x5f8940
DispatchBuffer    - 0x5fff78
pMyIf(LRPC_MYIF)  - 0x5f8c10
pSBinding(LRPC_SBINDING)  - 0x5e9a10
ObjectUuidFlag    - 0x1
ObjectUuid    - 49541cea-a719-4e75-8d58-a3a7bfff960e
CallId     - 0x2
ClientId.UniqueProcess(CLIENT_ID.HANDLE) - 0x730
ClientId.UniqueThread (CLIENT_ID.HANDLE) - 0x1580
RefCount    - 0x1
SContext    - 0x0


That is very useful.  We now know the thread and process ID of the caller.  You can now go crazy with !process and !thread.  You can even use WinDbg to attach and debug the caller directly if you want.

This is also useful.  It tells you which method in your interface it is actually calling.

0:001> !dict 0x5f8940

Printing 1 items in dictionary: 5f8940 with 4 slots

(0): 0x5e0a40

0:001> dt rpcrt4!ServerContextHandle 0x5e0a40
   +0x000 ContextChain     : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x008 UserContext      : 0x008aad58 Void
   +0x00c UserRunDown      : 0x69d6b930     void  MySvc!HDMYIF_rundown+0
   +0x018 RpcInterface     : 0x005f6580 RPC_INTERFACE
   +0x01c CtxGuard         : CTXT_HANDLE_INFO
   +0x028 Node             : GenericTable >::NodeType
   +0x04c UserStrict       : 0n0
   +0x050 Lock             : SWMRLock
   +0x064 OwnerSID         : (null)
   +0x068 ReferenceCount   : 0n2
   +0x06c Flags            : 1
   +0x070 DeadlockTag      : 0n0
0:001> !teb
TEB at 00336000
    ExceptionList:        0087f4dc
    StackBase:            00880000
    StackLimit:           0087c000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 00336000
    EnvironmentPointer:   00000000
    ClientId:             00001668 . 00000400
    RpcHandle:            00000000
    Tls Storage:          0033602c
    PEB Address:          00330000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0
0:001> dt rpcrt4!LRPC_SCALL 005f88b8
   +0x000 __VFN_table : 0x76ba9150
   +0x004 MagicLong        : 0x89abcdef
   +0x008 ObjectType       : 0n8192
   +0x00c RefCount         : INTERLOCKED_INTEGER (0x1)
   +0x010 pAsync           : 0x005f21b0 _RPC_ASYNC_STATE
   +0x014 AsyncStatus      : 0n0
   +0x018 CallingThread    : (null)
   +0x01c ReservedNotificationsLock : CSpinLock
   +0x020 CachedNotificationInfo : RPC_RESERVED_NOTIFICATION_INFO
   +0x03c CachedNotificationInfoAvailable : 0n1
   +0x040 ReservedNotifications : RPC_RESERVED_NOTIFICATION_INFO_DICT2
   +0x06c ActivityID       : _GUID {00000000-0000-0000-0000-000000000000}
   +0x07c DispatchBuffer   : 0x005fff78 Void
   +0x080 Flags            : AssocClose+0x8000 (0x8800)
   +0x084 SubscribeInfo    : 0x005f5ae8 NotificationSubscriptionInformation
   +0x088 ActiveContextHandles : ServerContextHandle_DICT
   +0x0a4 AsyncCallbackState : (null)
   +0x0a8 Association      : 0x005f8c10 LRPC_SMYIF
   +0x0ac SBinding         : 0x005e9a10 LRPC_SBINDING
   +0x0b0 DebugCell        : (null)
   +0x0b4 DebugCellTag     : 0n0
   +0x0b8 RpcMessage       : _RPC_MESSAGE
   +0x0e4 RuntimeInfo      : _RPC_RUNTIME_INFO
   +0x0f0 ClientPrincipalName : (null)
   +0x0f4 ClientRequest    : 0x005fff20 _LRPC_REQUEST_MESSAGE
   +0x0f8 Response         : 0x005ecd70 _LRPC_RESPONSE_MESSAGE
   +0x0fc AlpcPortSection  : (null)
   +0x100 LargeReplyDataBuffer : (null)
   +0x104 LargeRequestDataBuffer : (null)
   +0x108 LargeReplyDataSize : 0
   +0x10c LargeReplyViewSize : 0
   +0x110 ClientId         : _CLIENT_ID
   +0x118 ObjectUuidFlag   : 0n1
   +0x11c ObjectUuid       : RPC_UUID
   +0x12c CallId           : 2
   +0x130 SContext         : (null)
   +0x134 ActiveCallsEntry : _LIST_ENTRY [ 0x5f8ca4 - 0x5f8ca4 ]
   +0x13c CausalFlowEntry  : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x144 CallCausalFlowNumber : 1
   +0x148 AdditionalCallData : LRPC_SCALL::
   +0x14c SystemHandles    : LRPC_SYSTEM_HANDLE_DATA
   +0x15c TokenAttributes  : RPCP_ALPC_TOKEN_ATTR
0:001> dx -r1 (*((RPCRT4!_RPC_MESSAGE *)0x5f8970))
(*((RPCRT4!_RPC_MESSAGE *)0x5f8970))                 [Type: _RPC_MESSAGE]
    [+0x000] Handle           : 0x5f88b8 [Type: void *]
    [+0x004] DataRepresentation : 0x10 [Type: unsigned long]
    [+0x008] Buffer           : 0x5fff78 [Type: void *]
    [+0x00c] BufferLength     : 0x1c [Type: unsigned int]
    [+0x010] ProcNum          : 0x2 [Type: unsigned int]
    [+0x014] TransferSyntax   : 0x5f65c4 [Type: _RPC_SYNTAX_IDENTIFIER *]
    [+0x018] RpcInterfaceInformation : 0x5f65ac [Type: void *]
    [+0x01c] ReservedForRuntime : 0x5f899c [Type: void *]
    [+0x020] ManagerEpv       : 0x0 [Type: void *]
    [+0x024] ImportContext    : 0x404 [Type: void *]
    [+0x028] RpcFlags         : 0x9000 [Type: unsigned long]
0:001> dx -r1 (*((RPCRT4!_RPC_SYNTAX_IDENTIFIER *)0x5f65c4))
(*((RPCRT4!_RPC_SYNTAX_IDENTIFIER *)0x5f65c4))                 [Type: _RPC_SYNTAX_IDENTIFIER]
    [+0x000] SyntaxGUID       : {8A885D04-1CEB-11C9-9FE8-08002B104860} [Type: _GUID]
    [+0x010] SyntaxVersion    [Type: _RPC_VERSION]
0:001> dt RPCRT4!RPC_CLIENT_INTERFACE 0x5f65ac
   +0x000 Length           : 0x44
   +0x004 InterfaceId      : _RPC_SYNTAX_IDENTIFIER
   +0x018 TransferSyntax   : _RPC_SYNTAX_IDENTIFIER
   +0x02c DispatchTable    : 0x69d22444 RPC_DISPATCH_TABLE
   +0x030 RpcProtseqEndpointCount : 0
   +0x034 RpcProtseqEndpoint : (null)
   +0x038 Reserved         : 0
   +0x03c InterpreterInfo  : 0x69d22424 Void
   +0x040 Flags            : 0x4000000
0:001> dx -r1 (*((RPCRT4!_RPC_SYNTAX_IDENTIFIER *)0x5f65b0))
(*((RPCRT4!_RPC_SYNTAX_IDENTIFIER *)0x5f65b0))                 [Type: _RPC_SYNTAX_IDENTIFIER]
    [+0x000] SyntaxGUID       : {850CEE52-3038-4277-B9B4-E05DB8B2C35C} [Type: _GUID]
    [+0x010] SyntaxVersion    [Type: _RPC_VERSION]
0:001> dx -r1 (*((RPCRT4!RPC_DISPATCH_TABLE *)0x69d22444))
(*((RPCRT4!RPC_DISPATCH_TABLE *)0x69d22444))                 [Type: RPC_DISPATCH_TABLE]
    [+0x000] DispatchTableCount : 0xe [Type: unsigned int]
    [+0x004] DispatchTable    : 0x69d217bc [Type: void (**)(_RPC_MESSAGE *)]
    [+0x008] Reserved         : 0 [Type: long]

Tuesday, September 8, 2015

WinRT ABI Debugging Workflow

VS can debug a WinRT app's code, but how do you debug the actual ABI?  Here are some handy tricks to deploying and testing the ABI.

Setting Up the Test Box

For my systems programming, I like to have a development machine, and a separate test machine.  This should be obvious, but yes, you can hose your development machine by replacing system binaries with modified ones.  At this point, many of my coworkers simply use VMs.  These days, you can grab a VHD directly off of the build share, so you don't even have to install the OS anymore.  Maybe I am old fashioned, but I actually like to have a physical PC to test on.  Generally it is faster and more reliable.  Furthermore, I have less distractions, so I can debug faster.

What you need to install & configure:
  1. Install a correct build of the OS.
  2. Install WinDBG.  I guess it is part of the WDK?
  3. Copy over the symbols locally.  I think you can also get these from the WDK?  You can also try to rely on the symbol server.
  4. Set you PC to "Developer mode"
    Settings->Update & Security->For developers->Developer mode
  5. Install Remote Tools For VS 2015 :
  6. Run the remote debugging configuration:
    Start -> All Apps -> Visual Studio 2015 -> Remote Debugging Configuration -> Remote debugger

    Click "Configure remote debugging"

  7. Then you will see something like this:

    This means your test box is ready.

Setting Up the Dev Box

To use the fancy new remote app testing feature, you just need to:
  1. Install VS2015.
  2. Install the Windows SDK from the same build installed on your test machine.


  1. Dev Box: Write a test app to test the ABI functionality of the ABI.
  2. Dev Box: Build the app in VS2015 for the architecture of the test machine.
  3. Test Box: Run the "Remote debugger" desktop app
  4. Dev Box: Hit the down arrow for start debugging, and select "Remote machine."  If it is the first time you have done this, it will have a dialog to select your test machine.  Select it and hit play.  If you don't see your test machine, see #3.

  5. Test: Validate that your test app is running and working.
  6. Dev: Hit the stop button.

    Why did we bother to start the app to just stop it without any debugging?  It is mainly to just get the app deployed to the test machine.
  7. Test: find the name of the app package for your test app.

    plmdebug /query

    The output should look something like this:
    Package full name: 02985851.Mint.comPersonalFinance_1.2.0.2529_x86__xm34sne40gbwr
    Package state: Unknown
    Package full name: 1ED5AEA5.AngryBirdsSpace_1.6.1.0_x86__p2gbknwb5d8r2
    Package state: Unknown

    In case you were wondering PLM stands for the Process Lifetime Manager.
  8. Test: Then you run:

    plmdebug /enabledebug [package full name here]  "c:\debuggers\windbg.exe"

    This will set the correct image execution options so that windbg will attach to the app container before the app starts running.  Whenever your app starts, you will get windbg window to pop.  This works for everything: foreground apps, background tasks, app activation contracts, etc.
  9. Test: Once, windbg launches, set your break points in your code as you would with a normal Win32 code, and let the debugger go.  
  10. Test: Run through your test app that executes the code you are interested in.

Friday, August 14, 2015

How Can you Find Who is Using What Device Programmatically in Windows?

Well, that is not an easy question to answer.  The short answer is that there is no API to do what you are asking.  In fact the OS isn't really designed to allow that, so some of the information required to figure it out 100% is not preserved in the kernel's object manager.  In other words, you can't, not 100% anyway.  Even Mark Russinovich could figure out how to make a sysinternal tool to answer the question.  With that said, it is possible to figure out quite a lot about who is using what.

First off, people use devices by opening a handle to them using CreateFile.  You typically pass in a device interface path that you discovered using SetupAPI, or one of the other APIs Windows has.  The IO Manager will resolve the device directly, or it also has other branch points where it can involve drivers on the stack to resolve the device.  Ultimately the client gets a handle to the device.  I will leave it at that.  The important thing to know is that devices that are being used have handles backed by objects that the kernel's object manager tracks.

Devices being used will have open handles, so you can look at the handles; however, they don't always tell you enough information of what they are for.  With experience, you can recognize some types of handles, and even figure out what interfaces the match to.  Still, some other handles to device, will not have that information: they can be symlinked, they could just be a named object created directly by the device driver (assuming the set the right permissions, etc.), or a lot of other reasons.

Next, are we talking about user mode or kernel mode?  Drivers in KM can open handles to other devices and they will not be directly visible to a UM process.  You will not be able to see a lot of the references, unless you are discovering them in KM.  If you are talking about a UM tool, then the tool could open other processes in UM and enumerate their handles, assuming it has enough permissions.  It would probably need TCB (trusted computing base) to open them all.  The UM tool will not have visibility into the KM handles.

Do you think you could write such a tool?  It would be a great way to learn how IOMGR works, and PnP.  I could write one, but it still would not be 100% complete.

A good starting place to learn about this is another tool called KD (the Kenrel Debugger).  Dump all of the handles in the system, and go spelunking.  That will give you a lot of experience you would need to write the tool I was talking about.


How to Capture Control Events (eg. Ctrl-C) in a C/C++ Windows Console App

I was recently working on a tool of mine that implements a RPC client for my RPC server.  I wanted to let the win32 console app run until the user hits ctrl-c or ctrl-break.  I guess you can also do something like getchar, but I didn't want to.  Also, I could just let the user hit ctrl-c and it would kill the app.  I do use RPC handle rundowns to clean up the RPC server state; however, I wanted to clean up and exit "safely" within the app.

So, without further ado, here is the code for using SetConsoleCtrlHandler in Windows for handling console control signals.

There are a few caveats you need to think about though when implementing control handlers.  For some events, there may be other handlers already registered, so your's may not get called.  This happens, for instance, when using a debugger.  Processes with a window message pump will also get some events through the pump that are not synchronized the process signal handler.

    User mode only.

#include "precomp.h"

HANDLE g_hControlEvent = NULL;

///// ctl event handling
BOOL WINAPI ControlHandlerCallback(
    _In_ DWORD dwCtrlType)
    HRESULT hr = S_OK;
    BOOL bControlEventHandled = FALSE;

    wprintf(L"got event %i\n", dwCtrlType);

    switch (dwCtrlType) {
    case CTRL_C_EVENT:

        if (!SetEvent(g_hControlEvent)) {
            hr = HRESULT_FROM_WIN32(GetLastError());
            NT_ASSERT(hr == S_OK);

        if (S_OK == hr) {
            bControlEventHandled = TRUE;


    return bControlEventHandled;
}  // ControlHandlerCallback

HRESULT RegisterControlHandler()
    HRESULT hr = S_OK;

    g_hControlEvent = NULL;
    g_hControlEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (!g_hControlEvent) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto exit;

    if (!SetConsoleCtrlHandler(ControlHandlerCallback, TRUE)) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto exit;

    wprintf(L"press ctrl+c to stop\n");


    return hr;
}  // RegisterControlHandler

HRESULT UnregisterControlHandler()
    HRESULT hr = S_OK;

    if (!SetConsoleCtrlHandler(ControlHandlerCallback, FALSE)) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto exit;

    if (!CloseHandle(g_hControlEvent)) {
        hr = HRESULT_FROM_WIN32(GetLastError());

    g_hControlEvent = NULL;


    return hr;
}  // UnregisterControlHandler

HRESULT WaitForControlEvent()
    HRESULT hr = S_OK;

    if (!g_hControlEvent) {
        // called before registring
        hr = E_UNEXPECTED;
        goto exit;

    if (WAIT_OBJECT_0 != WaitForSingleObject(g_hControlEvent, INFINITE)) {
        hr = E_FAIL;
        goto exit;


    return hr;
}  // WaitForControlEvent

Monday, July 6, 2015

MIDL: [ptr] vs [ref] vs [unique] vs [optional]

If you author IDL files for RPC, COM, or WRL, you will probably need to pass arrays.  There are a few parameter attributes you can use for pointers, but it may be unclear which one to use.  You may have seen [ptr], [ref], [unique], and maybe [optional].  Straight off, even though optional seems like a logical choice for emulating SAL's _opt, it is actually a red hearing.  For some inexplicable reason, optional only applies to VARIANT structs according to MSDN.

That leaves us with: ptr, ref, and unique.

In general, I would recommend using ref for const type in parameters, and unique for opt type in parameters.  You should probably stay away from ptr pointers due to the RPC runtime overhead unless you have a good reason to use them.  Also you may experiment with the MIDL compiler and see what SAL annotations are generated with which IDL attributes.  There is a lot more nuance, so dive into the MSDN articles if you need more depth on the topic.

Thursday, March 19, 2015

What Have I Been Working On the Past Year?

Winodws 10. It is interesting to hear my boss' historical perspective. It is good inside info if you don't work at Microsoft.