.NET Corner

Jeans, .NET and Physics (eka The Quantum Boy)
posts - 14, comments - 10, trackbacks - 0

My Links

News

Archives

Post Categories

Saturday, October 17, 2009

C#, ActiveObject (Runnable)

This is a post after a long hibernation. Often in our product we need worker threads performing a given action when signaled. Thread pool threads (modified ThreadPool class, not the Microsoft supplied one) may not be ideal for this as these are rather foreground, "active" operations in contrast to the background callback model ThreadPool usually projects. Observing the repeating nature of such threads, I decided to patternize this model having  some resemblance to the IRunnable interface of Java. I've chosen a simpler implementation for ActiveObjects. More complex implementations can be found over net - here for example.

     /// <summary>
    /// Active Object (runnable) interface
    /// </summary>
    public interface IActiveObject
    {
        /// <summary>
        /// Initialize an active object
        /// </summary>
        /// <param name="name"></param>
        /// <param name="action"></param>
        void Initialize(string name, Action action);

        /// <summary>
        /// Signal the active object to perform its loop action.
        /// </summary>
        /// <remarks>
        /// Application may call this after some simple or complex condition evaluation
        /// </remarks>
        void Signal();

        /// <summary>
        /// Signals to shotdown this active object
        /// </summary>
        void Shutdown();
    }

Abiding by good design principles, the interface enforces a "shutdown" / "soft abort" mechanism for the active objects. A KISS implementation of the above interface is given beneath,

/// <summary>
    /// Implements a simple active object pattern implementation
    /// </summary>
    /// <remarks>
    /// Although there exists a vast number of active objects patterns (in Java they are just "runnable")
    /// scattered, one of the best I found is located at http://blog.gurock.com/wp-content/uploads/2008/01/activeobjects.pdf
    /// </remarks>
    public class ActiveObject : IActiveObject
    {
        /// <summary>
        /// Name of this active object
        /// </summary>
        private string m_Name;
       
        /// <summary>
        /// Underlying active thread
        /// </summary>
        private Thread m_ActiveThreadContext;

        /// <summary>
        /// Abstracted action that the active thread executes
        /// </summary>
        private Action m_ActiveAction;

        /// <summary>
        /// Primary signal object for this active thread.
        /// See the Signal() method for more.
        /// </summary>
        private AutoResetEvent m_SignalObject;

        /// <summary>
        /// Signal object for shutting down this active object
        /// </summary>
        private ManualResetEvent m_ShutdownEvent;

        /// <summary>
        /// Interal array of signal objects combining primary signal object and
        /// shutdown signal object
        /// </summary>
        private WaitHandle[] m_SignalObjects;
       
        public ActiveObject()
        {
        }

        public void Initialize(string name, Action action)
        {
            m_Name = name;
            m_ActiveAction = action;
            m_SignalObject = new AutoResetEvent(false);
            m_ShutdownEvent = new ManualResetEvent(false);
            m_SignalObjects = new WaitHandle[]
                                {
                                    m_ShutdownEvent,
                                    m_SignalObject
                                };

            m_ActiveThreadContext = new Thread(Run);
            m_ActiveThreadContext.Name = string.Concat("ActiveObject.", m_Name);
            m_ActiveThreadContext.Start();
        }
       
        private bool Guard()
        {
            int index = WaitHandle.WaitAny(m_SignalObjects);
            return index == 0 ? false : true;
        }
       
        /// <summary>
        /// Signal the active object to perform its loop action.
        /// </summary>
        /// <remarks>
        /// Application may call this after some simple of complex condition evaluation
        /// </remarks>
        public void Signal()
        {
            m_SignalObject.Set();
        }
       
        /// <summary>
        /// Signals to shotdown this active object
        /// </summary>
        public void Shutdown()
        {
            m_ShutdownEvent.Set();
           
            if (m_ActiveThreadContext != null)
            {
                m_ActiveThreadContext.Join();
            }
           
            m_ActiveThreadContext = null;
        }
       
        /// <summary>
        /// Core run method of this active thread
        /// </summary>
        private void Run()
        {
            try
            {
                while (Guard())
                {
                    try
                    {
                        m_ActiveAction();
                    }
                    catch (Exception ex)
                    {
                        Logger.Write(new LogData(string.Format("ActiveObject::Run - Name: {0}, Loop Error: {1}",
                                                               m_Name,
                                                               ex.Message),
                                             Component.WebAstra,
                                             LogLevel.Error));
                    }
                }
            }
            catch(Exception ex)
            {
                Logger.Write(new LogData(string.Format("ActiveObject::Run - Name: {0}, Error: {1}",
                                                       m_Name,
                                                       ex.Message),
                                     Component.WebAstra,
                                     LogLevel.Error));
            }
            finally
            {
                m_SignalObject.Close();
                m_ShutdownEvent.Close();
               
                m_SignalObject = null;
                m_ShutdownEvent = null;
            }
        }
    }

The module/functional entity that requires to be "active" can compose the ActiveObject within it and provide an appropriate Action delegate.

public class EntityAgent
{
    private IActiveObject m_PickupActiveObject;
  
    public EntityAgent(...)
    {
        m_PickupActiveObject = new ActiveObject();
    }

    public void Initialize()
    {
        m_PickupActiveObject.Initialize("Pickup", TryPickupInternalAsync);
    }

    public void SomeComplexConditionEvaluation()
    {
       //...

       m_PickupActiveObject.Signal();
    }

    private void TryPickupInternalAsync()
    {
         //Your loop action here
    }
}

Patterns may not be strict GoF-patterns but most of the times are learned or devised on the fly by designers, developers and architects. I'll keep this post short as Deewali celebration is going on frenzy outside. Happy Deewali to all and keep hacking.

Posted On Saturday, October 17, 2009 7:58 AM | Feedback (0) | Filed Under [ .NET Core ]

Sunday, November 09, 2008

COM, RPC and Heap Corruption - Windbg Part 2

Mr. Bugsy Elusive

We have a C# client and a Local COM server hosting a VoIP stack. We use COM
interop. for API and events to the COM server. Today after some changes, when we started to  load testing the system a nasty crash happened. From initial observation it's look
like some kind of Heap Corruption(may be leaked from stack boundary and stuff
like that).

I used DTW / Gflags to run the server(sipserver.exe) under windbg and
symbols are properly set. Stack traces refer to RPC calls as usual but how to
know which thread/function from client made the LPC/RPC call. Following are my
initial observations :

-------------------------------------------------------------
Microsoft (R) Windows Debugger Version 6.9.0003.113 X86
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine:
D:\projects\WEBAST~2\src\WebAstra\Console\bin\Release\SIPSER~1.EXE -Embedding
Symbol search path is:
D:\projects\WebAstra_Virtualized_Console\src\WebAstra\Console\bin\Release;SRV*c:\symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
ModLoad: 00400000 0042f000 sipServer.exe
ModLoad: 7c900000 7c9b0000 ntdll.dll
ModLoad: 7c800000 7c8f5000 C:\WINDOWS\system32\kernel32.dll
ModLoad: 10000000 101dd000
D:\projects\WEBAST~2\src\WebAstra\Console\bin\Release\sipPhone.dll
ModLoad: 77dd0000 77e6b000 C:\WINDOWS\system32\ADVAPI32.dll
ModLoad: 77e70000 77f01000 C:\WINDOWS\system32\RPCRT4.dll
ModLoad: 10200000 10320000
C:\WINDOWS\WinSxS\x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_f75eb16c\MSVCR80D.dll
ModLoad: 77c10000 77c68000 C:\WINDOWS\system32\msvcrt.dll
...
... (more)
...

Lets have an automatic exception analysis :-

0:001> !analyze -v

FAULTING_IP:
OLEAUT32!VARIANT_UserFree+c5
77152ff2 ff5108 call dword ptr [ecx+8]

EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 77152ff2 (OLEAUT32!VARIANT_UserFree+0x000000c5)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: f0f0f0f8
Attempt to read from address f0f0f0f8

FAULTING_THREAD: 00000c88

PROCESS_NAME: sipServer.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced
memory at "0x%08lx". The memory could not be "%s".

READ_ADDRESS: f0f0f0f8

NTGLOBALFLAG: 2000000

APPLICATION_VERIFIER_FLAGS: 0

ADDITIONAL_DEBUG_TEXT: Followup set via attribute from Frame 0 on thread c88

BUGCHECK_STR:
APPLICATION_FAULT_FILL_PATTERN_f0f0f0f0_NULL_POINTER_READ_f0f0f0f0_INVALID_POINTER_READ_f0f0f0f0

PRIMARY_PROBLEM_CLASS:
FILL_PATTERN_f0f0f0f0_NULL_POINTER_READ_f0f0f0f0_INVALID_POINTER_READ_f0f0f0f0

DEFAULT_BUCKET_ID:
FILL_PATTERN_f0f0f0f0_NULL_POINTER_READ_f0f0f0f0_INVALID_POINTER_READ_f0f0f0f0

LAST_CONTROL_TRANSFER: from 77e8ec24 to 77152ff2

STACK_TEXT:
041cf468 77e8ec24 041cf480 003345b0 041cf5cc OLEAUT32!VARIANT_UserFree+0xc5
041cf49c 77e88d3b 041cf5cc 003345b0 7712bc70 RPCRT4!NdrUserMarshalFree+0x38
041cf4e0 77e7ac25 77e8ebec 003345b0 00000003 RPCRT4!NdrComplexArrayFree+0x16f
041cf500 77e86ec8 00334590 00334590 7712bcaa RPCRT4!NdrPointerFree+0xa2
041cf52c 77ef3414 001cf5cc 00321a60 7712bcaa RPCRT4!NdrComplexStructFree+0xd1
041cf550 77ef333b 7713045a 00000008 77130442 RPCRT4!NdrpFreeParams+0x77
041cf564 77ef32df 03fdb8d8 00000001 002a21cc RPCRT4!NdrStubCall2+0x469
041cf984 77ef3bf3 00336cd8 002d8600 002e9e80 RPCRT4!NdrStubCall2+0x387
041cf9dc 7717e724 00336cd8 002e9e80 002d8600 RPCRT4!CStdStubBuffer_Invoke+0xc6
041cfa00 77600c15 0031bcc0 002e9e80 002d8600
OLEAUT32!CDispStubWrapper::Invoke+0xb7
041cfa40 77600bbf 002e9e80 0031b2a0 002eebf8 ole32!SyncStubInvoke+0x33
041cfa88 7752ad71 002e9e80 00318118 0031bcc0 ole32!StubInvoke+0xa7
041cfb60 7752ac96 002d8600 00000000 0031bcc0
ole32!CCtxComChnl::ContextInvoke+0xe3
041cfb7c 776007f5 002e9e80 00000001 0031bcc0 ole32!MTAInvoke+0x1a
041cfbac 77602df3 002e9e28 002d8600 0031bcc0 ole32!AppInvoke+0x9c
041cfc80 77600715 002e9e28 002d8ad8 0031b288
ole32!ComInvokeWithLockAndIPID+0x2c2
041cfccc 77e794a5 002f260c 0031b288 002f260c ole32!ThreadInvoke+0x1cd
041cfd00 77e7940a 776005d0 002f260c 041cfdec RPCRT4!DispatchToStubInC+0x38
041cfd54 77e79336 00000000 00000000 774ebfc8
RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x113
041cfd78 77e8a33c 002f260c 00000000 774ebfc8
RPCRT4!RPC_INTERFACE::DispatchToStub+0x84
041cfdb8 77e8a37d 002f260c 002f25c8 00000000
RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0xc0
041cfdf8 77e7bc99 002db8f0 002d77f8 002f2328
RPCRT4!LRPC_SCALL::DealWithRequestMessage+0x2cd
041cfe1c 77e7bbdd 002d7834 041cfe38 002f2328
RPCRT4!LRPC_ADDRESS::DealWithLRPCRequest+0x16d
041cff80 77e76c9f 041cffa8 77e76ac1 002d77f8
RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0x310
041cff88 77e76ac1 002d77f8 00000000 002d7a10 RPCRT4!RecvLotsaCallsWrapper+0xd
041cffa8 77e76c87 002ce9f8 041cffec 7c80b6a3
RPCRT4!BaseCachedThreadRoutine+0x79
041cffb4 7c80b6a3 002d7c48 00000000 002d7a10 RPCRT4!ThreadStartRoutine+0x1a
041cffec 00000000 77e76c6d 002d7c48 00000000 kernel32!BaseThreadStart+0x37

Let's see the stack

0:009> kb
ChildEBP RetAddr Args to Child
052bf468 77e8ec24 052bf480 0031b848 052bf5cc OLEAUT32!VARIANT_UserFree+0xc5
052bf49c 77e88d3b 052bf5cc 0031b848 7712bc70 RPCRT4!NdrUserMarshalFree+0x38
052bf4e0 77e7ac25 77e8ebec 0031b848 00000003 RPCRT4!NdrComplexArrayFree+0x16f
052bf500 77e86ec8 0031b828 0031b828 7712bcaa RPCRT4!NdrPointerFree+0xa2
052bf52c 77ef3414 002bf5cc 0436c730 7712bcaa RPCRT4!NdrComplexStructFree+0xd1
052bf550 77ef333b 7713045a 00000008 77130442 RPCRT4!NdrpFreeParams+0x77
052bf564 77ef32df 03bc8118 00000001 0436c3dc RPCRT4!NdrStubCall2+0x469
052bf984 77ef3bf3 00327220 002d7f48 0026e130 RPCRT4!NdrStubCall2+0x387
052bf9dc 7717e724 00327220 0026e130 002d7f48 RPCRT4!CStdStubBuffer_Invoke+0xc6
052bfa00 77600c15 003112f0 0026e130 002d7f48
OLEAUT32!CDispStubWrapper::Invoke+0xb7
052bfa40 77600bbf 0026e130 003109f0 002ed440 ole32!SyncStubInvoke+0x33
052bfa88 7752ad71 0026e130 0030f6c8 003112f0 ole32!StubInvoke+0xa7
052bfb60 7752ac96 002d7f48 00000000 003112f0
ole32!CCtxComChnl::ContextInvoke+0xe3
052bfb7c 776007f5 0026e130 00000001 003112f0 ole32!MTAInvoke+0x1a
052bfbac 77602df3 0026e0d8 002d7f48 003112f0 ole32!AppInvoke+0x9c
052bfc80 77600715 0026e0d8 002d8420 003109d8
ole32!ComInvokeWithLockAndIPID+0x2c2
052bfccc 77e794a5 002ef464 003109d8 002ef464 ole32!ThreadInvoke+0x1cd
052bfd00 77e7940a 776005d0 002ef464 052bfdec RPCRT4!DispatchToStubInC+0x38
052bfd54 77e79336 00000000 00000000 774ebfc8
RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x113
052bfd78 77e8a33c 002ef464 00000000 774ebfc8
RPCRT4!RPC_INTERFACE::DispatchToStub+0x84

Un-assemble the last 20 instruction set.
0:009> u eip -20
OLEAUT32!VARIANT_UserFree+0x7b:
77152fd2 bf0000e938 mov edi,38E90000h
77152fd7 1dfeffff70 sbb eax,70FFFFFEh
77152fdc 08ff or bh,bh
77152fde 700c jo OLEAUT32!VARIANT_UserFree+0xbb (77152fec)
77152fe0 6a01 push 1
77152fe2 ff7508 push dword ptr [ebp+8]
77152fe5 e8cce10000 call OLEAUT32!BRECORD_UserFree (771611b6)
77152fea e93c1dfeff jmp OLEAUT32!VARIANT_UserFree+0xdb (77134d2b)
0:009> u eip +20
OLEAUT32!LPSAFEARRAY_Unmarshal+0xa1:
77153012 50 push eax
77153013 e8fb1ffdff call OLEAUT32!SafeArrayDestroy (77125013)
77153018 832600 and dword ptr [esi],0
7715301b e96c020000 jmp OLEAUT32!LPSAFEARRAY_Unmarshal+0x47a
(7715328c)
77153020 83fe0d cmp esi,0Dh
77153023 0f84cb1bfeff je OLEAUT32!LPSAFEARRAY_Unmarshal+0x126
(77134bf4)
77153029 81fe0d800000 cmp esi,800Dh
7715302f 0f84bf1bfeff je OLEAUT32!LPSAFEARRAY_Unmarshal+0x126
(77134bf4)

0:003> dds esp
03fbf460 04d9bef0
03fbf464 7712c0c8 OLEAUT32!`string'+0x1758
03fbf468 03fbf49c
03fbf46c 77e8ec24 RPCRT4!NdrUserMarshalFree+0x38
03fbf470 03fbf480
03fbf474 0439efd8
03fbf478 03fbf5cc
03fbf47c 7712bc70 OLEAUT32!`string'+0x1300
03fbf480 00000200
03fbf484 03fbf5cc
03fbf488 00000000
03fbf48c 55535243
03fbf490 00000003
03fbf494 7712bc70 OLEAUT32!`string'+0x1300
03fbf498 7712b884 OLEAUT32!`string'+0xf14
03fbf49c 03fbf4e0
03fbf4a0 77e88d3b RPCRT4!NdrComplexArrayFree+0x16f
03fbf4a4 03fbf5cc
03fbf4a8 0439efd8
03fbf4ac 7712bc70 OLEAUT32!`string'+0x1300
03fbf4b0 7712bcaa OLEAUT32!`string'+0x133a
03fbf4b4 03fbf5cc
03fbf4b8 0439ef01
03fbf4bc 00000001
03fbf4c0 00268124
03fbf4c4 00000000
03fbf4c8 00000002
03fbf4cc 00000000
03fbf4d0 00000000
03fbf4d4 03fbf4bc
03fbf4d8 00000005
03fbf4dc 00000000

0:011> du 7712bcaa
7712bcaa  ".ᅫ..Б..ᄂ莴"
0:011> dc 7712bcaa
7712bcaa  ffce0012 ffe00012 00060411 ffa40013  ................
7712bcba  000083b4 00000010 0411fff4 00130010  ................
7712bcca  83b4fcfe 00040001 fff40000 0020031a  .............. .
7712bcda  00000000 004c0606 004cffea 004cffe6  ......L...L...L.
7712bcea  0808ffe2 5b5c0808 00020011 0004031b  ......\[........
7712bcfa  00240029 5b080001 00020011 00000321  ).$....[....!...
7712bd0a  00240029 ffff0001 0000ffff ffa2004c  ).$.........L...
7712bd1a  00115b5c 03210002 00290000 00010004  \[....!...).....
0:011> dd 0549f5cc
0549f5cc  003399f0 043ae1f0 058b3308 058b362c
0549f5dc  043ae1a4 0000012c 00000020 04439830
0549f5ec  00000000 00000000 00000000 00000000
0549f5fc  00000000 00000000 00000001 00000005
0549f60c  00000000 00000000 77131d47 77131d52
0549f61c  0549f56c 00000000 00000000 00000000
0549f62c  7712b7c8 00000000 00000000 00000000
0549f63c  00000070 00000000 00000000 0549f5ac
0:011> dd 0549f5cc 16
                    ^ Range error in 'dd 0549f5cc 16'
0:011> dd 0549f5cc
0549f5cc  003399f0 043ae1f0 058b3308 058b362c
0549f5dc  043ae1a4 0000012c 00000020 04439830
0549f5ec  00000000 00000000 00000000 00000000
0549f5fc  00000000 00000000 00000001 00000005
0549f60c  00000000 00000000 77131d47 77131d52
0549f61c  0549f56c 00000000 00000000 00000000
0549f62c  7712b7c8 00000000 00000000 00000000
0549f63c  00000070 00000000 00000000 0549f5ac
0:011> dd 003399f0
003399f0  00339998 00000010 043ae140 000000b0
00339a00  00000006 00310c9c 00310c84 002ef490
00339a10  00000000 e0e0e0e0 00009000 23416315
00339a20  4226eb38 cf7e0280 4adb1a11 00000030
00339a30  18d5271d 4235da37 ee01ba9d b1a53ac1
00339a40  0000130c 00000006 03bc8118 00000134
00339a50  ffffffff 00000000 00000000 0549fbdc
00339a60  00000001 00000000 00000000 058b32e0
0:011> dd 043ae140
043ae140  72657355 e0e0e0e0 00000003 00000000
043ae150  00000000 00000000 00000000 00000000
043ae160  72657355 72657355 72657355 00000000
043ae170  00000000 00000000 00000000 00000000
043ae180  ffffffff 00000000 00000000 ffffffff
043ae190  00000000 00000000 ffffffff 00000000
043ae1a0  00000000 00000002 72657355 72657355
043ae1b0  00000004 00000000 79114003 0b68e0c0
0:011> dc 043ae140
043ae140  72657355 e0e0e0e0 00000003 00000000  User............
043ae150  00000000 00000000 00000000 00000000  ................
043ae160  72657355 72657355 72657355 00000000  UserUserUser....
043ae170  00000000 00000000 00000000 00000000  ................
043ae180  ffffffff 00000000 00000000 ffffffff  ................
043ae190  00000000 00000000 ffffffff 00000000  ................
043ae1a0  00000000 00000002 72657355 72657355  ........UserUser
043ae1b0  00000004 00000000 79114003 0b68e0c0  .........@.y..h.
0:011> dc 043ae1b0
043ae1b0  00000004 00000000 79114003 0b68e0c0  .........@.y..h.
043ae1c0  00004003 00000004 00000000 e0e0e0e0  .@..............
043ae1d0  00000004 00000000 0b684003 0b68de98  .........@h...h.
043ae1e0  00004003 00000004 00015454 00000000  .@......TT......
043ae1f0  e0e0e0e0 e0e0e0e0 e0e0e0e0 e0e0e0e0  ................
043ae200  e0e0e0e0 e0e0e0e0 e0e0e0e0 e0e0e0e0  ................
043ae210  e0e0e0e0 e0e0e0e0 e0e0e0e0 e0e0e0e0  ................
043ae220  e0e0e0e0 e0e0e0e0 e0e0e0e0 e0e0e0e0  ................

Not much clue till now. I thought I had to take a plunge into RPC debugging which is insanely complex. From a purely learning perspective, I also wanted to debug RPC/OLE problem when one might be interested in knowing the caller client thread/function (not the client process -  which I know). If you know the function (we have lot of calls in our real-time system) it's easy to guess the culprit responsible for the suspected double free pattern. People normally use RPC debugging extension for WinDbg.

0:011> |
  0     id: 130c name: sipServer.exe

0:001>!rpcexts.getcallinfo 0 0 FFFF 130c

Searching for call info ...
PID  CELL ID   ST PNO IFSTART  THRDCELL  CALLFLAG CALLID   LASTTIME CONN/CLN
----------------------------------------------------------------------------
130c 0000.0004 00 003 00000132 0000.0003 00000009 e0e0e0e0 05446e0f 0500.1d64
130c 0000.0005 02 006 23416315 0000.000b 00000009 0000002f 05446e0f 1554.0b8c
130c 0000.0006 00 002 18f70770 0000.0003 0000000b 00000006 05446237 0500.0514
130c 0000.0008 00 003 00000134 0000.0003 00000008 e0e0e0e0 05446e0f 1554.0b70
130c 0000.000a 00 003 00000134 0000.0002 00000008 e0e0e0e0 05446e0f 1554.0b70

Second row looks active (ST:=02) let's verify that's the same thread as the offending stack -

0:011> !rpcexts.getdbgcell 130c 0000.000b
Getting cell info ...
Thread
Status: Dispatched
Thread ID: 0x1068 (4200)
Associated Endpoint:: 0x0.1
Last update time (in seconds since boot):88370.703 (0x15932.2BF)

0:011> ~*
   0  Id: 130c.17cc Suspend: 1 Teb: 7ffdf000 Unfrozen
      Start: sipServer!wWinMainCRTStartup (0040a59c)
      Priority: 0  Priority class: 128  Affinity: 3
   1  Id: 130c.418 Suspend: 1 Teb: 7ffde000 Unfrozen
      Start: kernel32!BaseThreadStartThunk (7c810679)
      Priority: 0  Priority class: 128  Affinity: 3
   3  Id: 130c.140c Suspend: 1 Teb: 7ffdc000 Unfrozen
      Start: kernel32!BaseThreadStartThunk (7c810679)
      Priority: 0  Priority class: 128  Affinity: 3
   4  Id: 130c.328 Suspend: 1 Teb: 7ffdb000 Unfrozen
      Start: wdmaud!MixerCallbackThread (72d230e8)
      Priority: 15  Priority class: 128  Affinity: 3
   5  Id: 130c.2464 Suspend: 1 Teb: 7ffda000 Unfrozen
      Start: sipPhone!thread_main (10019df0)
      Priority: 0  Priority class: 128  Affinity: 3
   6  Id: 130c.9dc Suspend: 1 Teb: 7ffd9000 Unfrozen
      Start: dsound!CThread::ThreadStartRoutine (73f1b94b)
      Priority: 15  Priority class: 128  Affinity: 3
   7  Id: 130c.1030 Suspend: 1 Teb: 7ffd8000 Unfrozen
      Start: sipPhone!thread_main (10019df0)
      Priority: 0  Priority class: 128  Affinity: 3
   8  Id: 130c.21c8 Suspend: 1 Teb: 7ffd7000 Unfrozen
      Start: sipPhone!thread_main (10019df0)
      Priority: 15  Priority class: 128  Affinity: 3
  10  Id: 130c.940 Suspend: 1 Teb: 7ffd5000 Unfrozen
      Start: WINMM!timeThread (76b5aee7)
      Priority: 15  Priority class: 128  Affinity: 3
. 11  Id: 130c.1068 Suspend: 1 Teb: 7ffae000 Unfrozen
      Start: 00818732
      Priority: 0  Priority class: 128  Affinity: 3


0:011> ~11 kb 20
ChildEBP RetAddr  Args to Child             
0549f468 77e8ec24 0549f480 058e8c10 0549f5cc OLEAUT32!VARIANT_UserFree+0xc5
0549f49c 77e88d3b 0549f5cc 058e8c10 7712bc70 RPCRT4!NdrUserMarshalFree+0x38
0549f4e0 77e7ac25 77e8ebec 058e8c10 00000003 RPCRT4!NdrComplexArrayFree+0x16f
0549f500 77e86ec8 058e8bf0 058e8bf0 7712bcaa RPCRT4!NdrPointerFree+0xa2
0549f52c 77ef3414 0049f5cc 04439830 7712bcaa RPCRT4!NdrComplexStructFree+0xd1
0549f550 77ef333b 7713045a 00000008 77130442 RPCRT4!NdrpFreeParams+0x77
0549f564 77ef32df 03bc8118 00000001 058b330c RPCRT4!NdrStubCall2+0x469
0549f984 77ef3bf3 00327288 002d7f48 003399f0 RPCRT4!NdrStubCall2+0x387
0549f9dc 7717e724 00327288 003399f0 002d7f48 RPCRT4!CStdStubBuffer_Invoke+0xc6
0549fa00 77600c15 00311588 003399f0 002d7f48 OLEAUT32!CDispStubWrapper::Invoke+0xb7
0549fa40 77600bbf 003399f0 00310c88 002ed440 ole32!SyncStubInvoke+0x33
0549fa88 7752ad71 003399f0 0030f960 00311588 ole32!StubInvoke+0xa7
0549fb60 7752ac96 002d7f48 00000000 00311588 ole32!CCtxComChnl::ContextInvoke+0xe3
0549fb7c 776007f5 003399f0 00000001 00311588 ole32!MTAInvoke+0x1a
0549fbac 77602df3 00339998 002d7f48 00311588 ole32!AppInvoke+0x9c
0549fc80 77600715 00339998 002d8420 00310c70 ole32!ComInvokeWithLockAndIPID+0x2c2
0549fccc 77e794a5 002ef464 00310c70 002ef464 ole32!ThreadInvoke+0x1cd
0549fd00 77e7940a 776005d0 002ef464 0549fdec RPCRT4!DispatchToStubInC+0x38
0549fd54 77e79336 00000000 00000000 774ebfc8 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x113
0549fd78 77e8a33c 002ef464 00000000 774ebfc8 RPCRT4!RPC_INTERFACE::DispatchToStub+0x84
0549fdb8 77e8a37d 002ef464 002ef420 00000000 RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0xc0
0549fdf8 77e7bc99 002effd8 002d7140 002ef180 RPCRT4!LRPC_SCALL::DealWithRequestMessage+0x2cd
0549fe1c 77e7bbdd 002d717c 0549fe38 002ef180 RPCRT4!LRPC_ADDRESS::DealWithLRPCRequest+0x16d
0549ff80 77e76c9f 0549ffa8 77e76ac1 002d7140 RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0x310
0549ff88 77e76ac1 002d7140 03fbfbdc 002ffbc0 RPCRT4!RecvLotsaCallsWrapper+0xd
0549ffa8 77e76c87 002ce340 0549ffec 7c80b6a3 RPCRT4!BaseCachedThreadRoutine+0x79
0549ffb4 7c80b6a3 0030d300 03fbfbdc 002ffbc0 RPCRT4!ThreadStartRoutine+0x1a
0549ffec 00000000 77e76c6d 0030d300 00000000 kernel32!BaseThreadStart+0x37

Yes that's the thread. Let's put the CONN/CLN (channel?) id - 1554.0b8c

0:011> !rpcexts.getdbgcell 130c 1554.0b8c
Getting cell info ...
Getting cell info failed with error 2

I supposed to get something like

Getting cell info ...
Connection
Connection flags: Exclusive
Authentication Level: Default
Authentication Service: None
Last Transmit Fragment Size: 24 (0x6F56D)
Endpoint for the connection: 0x0.1
Last send time (in seconds since boot):10595.565 (0x2963.235)
Last receive time (in seconds since boot):10595.565 (0x2963.235)
Getting endpoint info ...
Process object for caller is 0xFF9DF5F0

Seems like a dead end. Went ahead and read very few RPC debugging documents out there. There has to be something that I'm not doing correct. Let's restart,-


0:009> !rpcexts.getcallinfo 0 0 FFFF 130c
Searching for call info ...
PID  CELL ID   ST PNO IFSTART  THRDCELL  CALLFLAG CALLID   LASTTIME CONN/CLN
----------------------------------------------------------------------------
130c 0000.0004 00 003 00000132 0000.000b 00000009 e0e0e0e0 0b00b545 0500.0520
130c 0000.0005 02 006 23416315 0000.0002 00000009 0000002f 0b00ea01 1554.0fb0
130c 0000.0006 00 002 18f70770 0000.0002 0000000b 0000000a 0b009e04 0500.0514
130c 0000.0008 00 003 00000134 0000.0007 00000008 e0e0e0e0 0b00d281 1554.1940
130c 0000.0009 00 002 18f70770 0000.0002 0000000a 00000009 0b009cea 0500.0514
130c 0000.000a 00 003 00000134 0000.0002 00000008 e0e0e0e0 0b00d281 1554.1940
130c 0000.000c 00 002 18f70770 0000.0002 0000000a 00000008 0b00929a 0500.0514
130c 0000.000d 00 002 18f70770 0000.0002 0000000a 0000000b 0b00a1bd 0500.0514
130c 0000.000e 00 002 18f70770 0000.0002 0000000a 0000000c 0b00a382 0500.0514
130c 0000.000f 00 002 18f70770 0000.0002 0000000a 0000000d 0b00a3b1 0500.0514

Now the second row is our row (verified earlier from the thread stack),  we need to know about this in more detail.
I enabled RPC state information capturing as described as http://msdn.microsoft.com/en-us/library/cc267797.aspx

0:009> !rpcexts.getdbgcell 130c 0000.0005
Getting cell info ...
Call
Status: Dispatched
Procedure Number: 6
Interface UUID start (first DWORD only): 23416315
Call ID: 0x2f (47)
Servicing thread identifier: 0x0.2
Call Flags: cached, LRPC
Last update time (in seconds since boot):184609.281 (0x2D121.119)
Caller (PID/TID) is: 1554.fb0 (5460.4016)

Some pretty intersting information. Let's digest that. It says the call is bound to an inbound interface with GUID starts with 23416315. And indeed I have

[ uuid(23416315-EB38-4226-8002-7ECF111ADB4A) ]
    dispinterface _ISipPhone
    {
        properties:

        //...
    };

So we are on right track and this may be a "non-junk" call.  More importantly it shows client (Caller) identifier as 1554.fb0 (5460.4016) in the <ProcessID>.<ThreadID> format. Voila. Now I verified that indeed my client has pid 0x1554 (5460). I ran another instance of WinDbg attaching it to the client process and found out the thread number corresponding to fb0

120  Id: 1554.fb0
Suspend: 1 Teb: 7fedc000 Unfrozen
      Start: KERNEL32!BaseThreadStartThunk (7c810679)
      Priority: 0  Priority class: 32  Affinity: 3

I wanted to dump this thread's stack,

0:1232> ~120 kb 20
ChildEBP RetAddr  Args to Child             
0bd8d0d0 7c90e3ed 77e7ca99 000002d0 001d1828 ntdll!KiFastSystemCallRet
0bd8d0d4 77e7ca99 000002d0 001d1828 001d1828 ntdll!ZwRequestWaitReplyPort+0xc
0bd8d140 77e7a326 001b8b78 0bd8d168 776016bb RPCRT4!LRPC_CCALL::SendReceive+0x228
0bd8d14c 776016bb 2943eab8 29473008 0bd8d24c RPCRT4!I_RpcSendReceive+0x24
0bd8d168 776011a6 00000000 00000000 00000000 ole32!ThreadSendReceive+0xf5
0bd8d184 7760108a 0bd8d24c 0bd8d35c 29473008 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x13d
0bd8d264 7752ce5a 29473008 0bd8d35c 0bd8d34c ole32!CRpcChannelBuffer::SendReceive2+0xc8
0bd8d2d0 7752cdf2 29473008 0bd8d35c 0bd8d34c ole32!CAptRpcChnl::SendReceive+0xab
0bd8d324 77ef3db5 29473008 0bd8d35c 0bd8d34c ole32!CCtxComChnl::SendReceive+0x113
0bd8d340 77ef3ead 294711f4 0bd8d388 00000000 RPCRT4!NdrProxySendReceive+0x43
0bd8d71c 7719b61e 7712b7c8 7713042a 0bd8d738 RPCRT4!NdrClientCall2+0x1fa  <<-------------------------------------------
0bd8d730 7719c2e2 294711f4 00000001 79f1620c OLEAUT32!IDispatch_RemoteInvoke_Proxy+0x1b
0bd8d9f0 7a07fc17 294711f4 00000001 79f1620c OLEAUT32!IDispatch_Invoke_Proxy+0xb6
0bd8da70 7a081ae9 294711f4 00000001 79f1620c mscorwks!IsTypeVisibleFromCom+0x24a
0bd8dba0 7a08358c 294711f4 00000001 00000409 mscorwks!ConvertEnumVariantToMngEnum+0x20a
0bd8de00 7a11bd1f 0bd8deb8 0bd8dec0 0bd8debc mscorwks!IUInvokeDispMethod+0x9e4
*** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\5d56558f9990d0d16feb9372436ec3dc\mscorlib.ni.dll
0bd8dee8 79623fd9 00000000 00000409 07be3ad0 mscorwks!ReflectionInvocation::InvokeDispMethod+0x165
0bd8dfec 793cd81a 00000000 0129226c 0203084c mscorlib_ni+0x563fd9
0bd8e060 79e7be1b 0bd8e2ac 0202e4dc 01352d9c mscorlib_ni+0x30d81a
0bd8e080 79e7bd9b 0bd8e160 00000004 0bd8e120 mscorwks!CallDescrWorker+0x33
0bd8e100 79e7bce8 0bd8e160 00000004 0bd8e120 mscorwks!CallDescrWorkerWithHandler+0xa3
0bd8e24c 79e7bbd0 793cd670 0bd8e354 0bd8e2c4 mscorwks!MethodDesc::CallDescr+0x19c
0bd8e264 79e802f4 793cd670 0bd8e354 0bd8e2c4 mscorwks!MethodDesc::CallTargetWorker+0x20
0bd8e278 7a040850 0bd8e2c4 0bd8e664 038847a4 mscorwks!MethodDescCallSite::CallWithValueTypes_RetArgSlot+0x18
0bd8e56c 7a040a0c 0bd8e664 045c2a40 038847a4 mscorwks!CLRToCOMLateBoundWorker+0x398
0bd8e610 0091bc1e 07be3ad0 0bd8e664 9033e655 mscorwks!CLRToCOMWorker+0xaa
WARNING: Frame IP not in any known module. Following frames may be wrong.
0bd8e694 79f23aa2 00922010 01424524 0208655c 0x91bc1e
0bd8e6a8 05d3efd5 0bd8e734 0208655c 0208658c mscorwks!COMNlsInfo::nativeChangeCaseString+0x18f
...
0bd8e890 79e7bd9b 0bd8e964 00000001 0bd8e930 mscorwks!CallDescrWorker+0x33
0bd8e910 79e7bce8 0bd8e964 00000001 0bd8e930 mscorwks!CallDescrWorkerWithHandler+0xa3

Still not sure about the caller. It seemed that I needed  sos.dll [Son Of Strike, please strike now or be silent ever after :)]
(WinDbg extension dll for managed debugging).

0:1232> .load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll

and let's make the suspected thread, the current thread.

0:1232>~120 s

With !clrstack I got something like

0:120>!clrstack
OS Thread Id: 0x230 (39)
ESP       EIP    
0668e260 7c90eb94 [GCFrame: 0668e260]
0668e600 7c90eb94 [HelperMethodFrame_PROTECTOBJ: 0668e600] System.RuntimeType.InvokeDispMethod(System.String, System.Reflection.BindingFlags, System.Object, System.Object[], Boolean[], Int32, System.String[])
0668e688 79623fd9 System.RuntimeType.InvokeMember(System.String, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object, System.Object[], System.Reflection.ParameterModifier[], System.Globalization.CultureInfo, System.String[])
0668e790 793cd81a System.RuntimeType.ForwardCallToInvokeMember(System.String, System.Reflection.BindingFlags, System.Object, Int32[], System.Runtime.Remoting.Proxies.MessageData ByRef)
0668ea14 79e7be1b [GCFrame: 0668ea14]
0668ede4 79e7be1b [ComPlusMethodFrameGeneric: 0668ede4] WebAstra.Application.VoIP.UniversalCOMServer.Interop._ISipPhone.PlaceCall(UInt32, System.String, System.Object, Int32 ByRef, Int32 ByRef)
0668ee04 06382578 WebAstra.Application.VoIP.UniversalCOMServer.UCOMSipClient.PlaceCall(UInt32, System.String, WebAstra.Application.VoIP.UniversalCOMServer.UCOMCallData, Int32 ByRef)
0668ee3c 05e3efc5 WebAstra.Application.VoIP.UniversalCOMServer.UCOMProvider.PlaceCall(WebAstra.Common.VoIP.CallContext)
0668eee0 05e3e948 WebAstra.Application.Core.VoIP.VoIPManager.PlaceCall(WebAstra.Common.VoIP.CallContext)
0668ef1c 05e3e222 WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, WebAstra.Common.VoIP.IVoIPLineConfigContext, System.String)
0668ef50 05e3e108 WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, System.String)
0668ef68 05e3ded1 WebAstra.Application.DialerManagement.DialerBase.PlaceCall(WebAstra.Application.DialerManagement.DestinationInfo)
0668ef94 05e3cf3d WebAstra.Application.DialerManagement.DialerBase.StartCall(System.Object)
0668efd8 05e3cdb5 WebAstra.Application.DialerManagement.DialerBase.StartCallInternal(System.Object)
0668f000 05e3cd3e WebAstra.Application.DialerManagement.DialerBase+<>c__DisplayClass6.<DialOneCall>b__4(System.Object, System.EventArgs)

0668f298 79e7be1b [CustomGCFrame: 0668f298]
0668f27c 79e7be1b [GCFrame: 0668f27c]
0668f260 79e7be1b [GCFrame: 0668f260]
0668f468 79e7be1b [HelperMethodFrame_1OBJ: 0668f468] System.RuntimeMethodHandle._InvokeMethodFast(System.Object, System.Object[], System.SignatureStruct ByRef, System.Reflection.MethodAttributes, System.RuntimeTypeHandle)
0668f4d8 793cf049 System.RuntimeMethodHandle.InvokeMethodFast(System.Object, System.Object[], System.Signature, System.Reflection.MethodAttributes, System.RuntimeTypeHandle)
0668f524 7940029f System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo, Boolean)
0668f560 793ac6da System.Delegate.DynamicInvokeImpl(System.Object[])
0668f574 0453f5e0 WebAstra.Common.EventService.InvokeDelegate(System.Delegate, System.Object[])
0668f9e0 79e7be1b [HelperMethodFrame_PROTECTOBJ: 0668f9e0] System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr, System.Object[], System.Object, Int32, Boolean, System.Object[] ByRef)
0668fb24 794bc360 System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(System.RuntimeMethodHandle, System.Object[], System.Object, Int32, Boolean, System.Object[] ByRef)
0668fb44 794bbde3 System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Messaging.IMessageSink)
0668fbb0 794b2489 System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(System.Object)
0668fbc0 793d87af System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(System.Object)
0668fbc8 793608fd System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0668fbe0 793d8898 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(System.Object)
0668fd70 79e7be1b [GCFrame: 0668fd70]

Now I know who is calling. But one doubt. If we go back to the server's debugging session for getting the client (Caller) information

0:009> !rpcexts.getdbgcell 130c 0000.0005
Getting cell info ...
Call
Status: Dispatched
Procedure Number: 6
Interface UUID start (first DWORD only): 23416315
Call ID: 0x2f (47)
Servicing thread identifier: 0x0.2
Call Flags: cached, LRPC
Last update time (in seconds since boot):184609.281 (0x2D121.119)
Caller (PID/TID) is: 1554.fb0 (5460.4016)

There is a Procedure Number of 6. Now documentation says this has to be the numeric positional identifier (0-based) of the function of the target interface. Now _ISipPhone's 7th function can't be it as I know now the client stack. I little thought made me aha ! _ISipPhone is a dispinterface, why are you forgetting the IDispatch functions. IDispatch interface definition conforms that 7-th is indeed the Invoke(...) which is getting called from client with late bound calling convention. Verification complete. But the bug remains.

Its seems sos.dll has another excellent command called !DumpStack. It first allows WinDbg to unwind/walk unmanaged part of the stack and the supply managed symbols and parsing information. Simply, I got the whole stack of the thread both with managed and unmanaged calls listed :-

0:120> !DumpStack
OS Thread Id: 0x230 (39)
Current frame: ntdll!KiFastSystemCallRet
ChildEBP RetAddr  Caller,Callee
0668d850 7c90e3ed ntdll!ZwRequestWaitReplyPort+0xc
0668d854 77e7ca99 RPCRT4!LRPC_CCALL::SendReceive+0x228, calling ntdll!NtRequestWaitReplyPort
0668d8ac 774fd1ef ole32!COleStaticMutexSem::Release+0x17, calling ntdll!RtlLeaveCriticalSection
0668d8c0 77e7a326 RPCRT4!I_RpcSendReceive+0x24
0668d8cc 776016bb ole32!ThreadSendReceive+0xf5, calling RPCRT4!I_RpcSendReceive
0668d8e8 776011a6 ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x13d, calling ole32!ThreadSendReceive
0668d904 7760108a ole32!CRpcChannelBuffer::SendReceive2+0xc8, calling ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall
0668d920 77e8eec9 RPCRT4!NdrpUserMarshalMarshall+0x51
0668d95c 77e8ee60 RPCRT4!NdrUserMarshalMarshall+0xb2, calling RPCRT4!NdrpUserMarshalMarshall
0668d968 771339bc OLEAUT32!RequiredDcomProtocolVersion+0xb, calling OLEAUT32!LateInitRpcDll
0668d974 771339a3 OLEAUT32!NeedTrailingPadding+0x11, calling OLEAUT32!RequiredDcomProtocolVersion
0668d988 77135029 OLEAUT32!VARIANT_UserMarshal+0x23e, calling OLEAUT32!NeedTrailingPadding
0668d9a0 77e8eec9 RPCRT4!NdrpUserMarshalMarshall+0x51
0668d9e4 7752ce5a ole32!CAptRpcChnl::SendReceive+0xab, calling ole32!CRpcChannelBuffer::SendReceive
0668da10 77e88ed2 RPCRT4!NdrpComplexArrayMarshall+0x39e
0668da50 7752cdf2 ole32!CCtxComChnl::SendReceive+0x113, calling ole32!CAptRpcChnl::SendReceive
0668daa4 77ef3db5 RPCRT4!NdrProxySendReceive+0x43
0668dac0 77ef3ead RPCRT4!NdrClientCall2+0x1fa, calling RPCRT4!NdrProxySendReceive
0668daf4 7c910551 ntdll!RtlFreeHeap+0x1e9, calling ntdll!RtlpFreeToHeapLookaside
0668dafc 7c91056d ntdll!RtlFreeHeap+0x647, calling ntdll!_SEH_epilog
0668db58 7c914d8f ntdll!RtlAppendUnicodeToString+0x50, calling ntdll!memmove
0668dbbc 7c91056d ntdll!RtlFreeHeap+0x647, calling ntdll!_SEH_epilog
0668dbc0 77dde0ae ADVAPI32!BaseRegOpenClassKeyFromLocation+0x143, calling ntdll!RtlFreeHeap
0668dd08 7c910732 ntdll!RtlpAllocateFromHeapLookaside+0x42, calling ntdll!_SEH_epilog
0668dd34 7c910732 ntdll!RtlpAllocateFromHeapLookaside+0x42, calling ntdll!_SEH_epilog
0668dd38 7c9106ab ntdll!RtlAllocateHeap+0x1c2, calling ntdll!RtlpAllocateFromHeapLookaside
0668dd3c 7c9106eb ntdll!RtlAllocateHeap+0xeac, calling ntdll!_SEH_epilog
0668dd48 79e74e5c mscorwks!HelperMethodFrame::LazyInit+0x17, calling  (JitHelp: CORINFO_HELP_GET_THREAD)
0668dd50 79e74e3f mscorwks!HelperMethodFrame::HelperMethodFrame+0x1d, calling mscorwks!HelperMethodFrame::LazyInit
0668dd60 79e7cb94 mscorwks!HelperMethodFrame_1OBJ::HelperMethodFrame_1OBJ+0x14, calling mscorwks!HelperMethodFrame::HelperMethodFrame
0668dd64 79e78686 mscorwks!GCFrame::GCFrame+0x55, calling mscorwks!GCFrame::Init
0668dd7c 79e7ad60 mscorwks!SetObjectReferenceUnchecked+0x18
0668dd8c 79f4e1a5 mscorwks!VariantData::SetObjRef+0x16, calling mscorwks!SetObjectReferenceUnchecked
0668dd94 79f6ac4a mscorwks!COMVariant::SetFieldsObject+0x118, calling mscorwks!GetTypeHandleForCVType
0668dd98 79f6ac95 mscorwks!COMVariant::SetFieldsObject+0x28a, calling mscorwks!Frame::Pop
0668dd9c 79f6ac9d mscorwks!COMVariant::SetFieldsObject+0x292, calling mscorwks!HelperMethodFrameRestoreState
0668ddb0 7c9106eb ntdll!RtlAllocateHeap+0xeac, calling ntdll!_SEH_epilog
0668dde0 79f6ab59 mscorwks!COMVariant::SetFieldsObject+0x20, calling mscorwks!LazyMachStateCaptureState
0668de28 79e7c00a mscorwks!MethodDesc::GetMultiCallableAddrOfCode+0xa2, calling mscorwks!MethodDesc::GetNativeCode
0668de74 79380874 (MethodDesc 0x7924d490 +0x134 System.Variant..ctor(System.Object)), calling mscorwks!COMVariant::SetFieldsObject
0668de9c 7719b61e OLEAUT32!IDispatch_RemoteInvoke_Proxy+0x1b, calling RPCRT4!NdrClientCall2
0668deb0 7719c2e2 OLEAUT32!IDispatch_Invoke_Proxy+0xb6, calling OLEAUT32!IDispatch_RemoteInvoke_Proxy
0668df74 7750b622 ole32!InterlockedDecRefCnt+0x44, calling KERNEL32!InterlockedCompareExchange
0668df90 7750b6a8 ole32!CStdIdentity::CInternalUnk::Release+0x3d, calling ole32!InterlockedDecRefCnt
0668dfa8 79e84403 mscorwks!ReleaseTransitionHelper+0x5f
0668dfac 79e84425 mscorwks!ReleaseTransitionHelper+0x14b, calling mscorwks!_SEH_epilog4

0668dfc4 79e7185d mscorwks!Thread::EnterRuntimeNoThrow+0x94, calling ntdll!RtlSetLastWin32Error
0668dfc8 79e71864 mscorwks!Thread::EnterRuntimeNoThrow+0x9b, calling mscorwks!_EH_epilog3
0668dfdc 7750c979 ole32!CStdIdentity::CInternalUnk::QueryMultipleInterfaces+0x99, calling ole32!CStdIdentity::CInternalUnk::QueryMultipleInterfacesLocal
0668e040 79e843a1 mscorwks!SafeReleaseHelper+0x12e, calling mscorwks!_EH_epilog3
0668e044 79e842ea mscorwks!SafeRelease+0x2f, calling mscorwks!SafeReleaseHelper
0668e04c 79e842ff mscorwks!SafeRelease+0x44, calling mscorwks!_EH_epilog3
0668e074 79e842ff mscorwks!SafeRelease+0x44, calling mscorwks!_EH_epilog3

0668e078 79e9c628 mscorwks!SafeComRelease<ITypeLibImporterNotifySink>+0x12, calling mscorwks!SafeRelease
0668e084 79e9c610 mscorwks!BaseWrapper<IClassFactory *,FunctionBase<IClassFactory *,&DoNothing<IClassFactory *>,&SafeComRelease<IClassFactory>,2>,0,&CompareDefault<IClassFactory *>,2>::~BaseWrapper<IClassFactory *,FunctionBase<IClassFactory *,&DoNothing<IClassFactory *>,&SafeComRelease<IClassFactory>,2>,0,&CompareDefault<IClassFactory *>,2>+0x22, calling mscorwks!SafeComRelease<IMetaDataImport>
0668e088 79e9c608 mscorwks!BaseWrapper<IClassFactory *,FunctionBase<IClassFactory *,&DoNothing<IClassFactory *>,&SafeComRelease<IClassFactory>,2>,0,&CompareDefault<IClassFactory *>,2>::~BaseWrapper<IClassFactory *,FunctionBase<IClassFactory *,&DoNothing<IClassFactory *>,&SafeComRelease<IClassFactory>,2>,0,&CompareDefault<IClassFactory *>,2>+0x2b, calling mscorwks!_EH_epilog3
0668e0ac 79e9c608 mscorwks!BaseWrapper<IClassFactory *,FunctionBase<IClassFactory *,&DoNothing<IClassFactory *>,&SafeComRelease<IClassFactory>,2>,0,&CompareDefault<IClassFactory *>,2>::~BaseWrapper<IClassFactory *,FunctionBase<IClassFactory *,&DoNothing<IClassFactory *>,&SafeComRelease<IClassFactory>,2>,0,&CompareDefault<IClassFactory *>,2>+0x2b, calling mscorwks!_EH_epilog3
0668e0b0 79e9c5e7 mscorwks!Wrapper<ISymUnmanagedBinder *,&DoNothing<ISymUnmanagedBinder *>,&SafeComRelease<ISymUnmanagedBinder>,0,&CompareDefault<ISymUnmanagedBinder *>,2>::~Wrapper<ISymUnmanagedBinder *,&DoNothing<ISymUnmanagedBinder *>,&SafeComRelease<ISymUnmanagedBinder>,0,&CompareDefault<ISymUnmanagedBinder *>,2>+0x1d, calling mscorwks!_EH_epilog3
0668e0d4 79e9c5e7 mscorwks!Wrapper<ISymUnmanagedBinder *,&DoNothing<ISymUnmanagedBinder *>,&SafeComRelease<ISymUnmanagedBinder>,0,&CompareDefault<ISymUnmanagedBinder *>,2>::~Wrapper<ISymUnmanagedBinder *,&DoNothing<ISymUnmanagedBinder *>,&SafeComRelease<ISymUnmanagedBinder>,0,&CompareDefault<ISymUnmanagedBinder *>,2>+0x1d, calling mscorwks!_EH_epilog3
0668e0d8 79e9c5c9 mscorwks!SafeComHolder<IXMLParser,Wrapper<IXMLParser *,&DoNothing<IXMLParser *>,&SafeComRelease<IXMLParser>,0,&CompareDefault<IXMLParser *>,2> >::~SafeComHolder<IXMLParser,Wrapper<IXMLParser *,&DoNothing<IXMLParser *>,&SafeComRelease<IXMLParser>,0,&CompareDefault<IXMLParser *>,2> >+0x1d, calling mscorwks!_EH_epilog3
0668e0fc 79e9c5c9 mscorwks!SafeComHolder<IXMLParser,Wrapper<IXMLParser *,&DoNothing<IXMLParser *>,&SafeComRelease<IXMLParser>,0,&CompareDefault<IXMLParser *>,2> >::~SafeComHolder<IXMLParser,Wrapper<IXMLParser *,&DoNothing<IXMLParser *>,&SafeComRelease<IXMLParser>,0,&CompareDefault<IXMLParser *>,2> >+0x1d, calling mscorwks!_EH_epilog3
0668e100 79f389b8 mscorwks!RCW::SafeQueryInterfaceRemoteAware+0xa9, calling mscorwks!_EH_epilog3
0668e128 79f389b8 mscorwks!RCW::SafeQueryInterfaceRemoteAware+0xa9, calling mscorwks!_EH_epilog3
0668e12c 7a03dfb7 mscorwks!RCW::GetIDispatch+0x16, calling mscorwks!RCW::SafeQueryInterfaceRemoteAware
0668e130 79f6d184 mscorwks!BaseWrapper<tagVARIANT *,FunctionBase<tagVARIANT *,&DoNothing<tagVARIANT *>,&EmptyVariant,2>,0,&CompareDefault<tagVARIANT *>,2>::~BaseWrapper<tagVARIANT *,FunctionBase<tagVARIANT *,&DoNothing<tagVARIANT *>,&EmptyVariant,2>,0,&CompareDefault<tagVARIANT *>,2>+0x2b, calling mscorwks!_EH_epilog3
0668e170 7a07fc17 mscorwks!IsTypeVisibleFromCom+0x24a
0668e1f0 7a081ae9 mscorwks!ConvertEnumVariantToMngEnum+0x20a, calling mscorwks!IsTypeVisibleFromCom+0x1c1
0668e294 79e79290 mscorwks!ClassLoader::EnsureLoaded+0xb7, calling mscorwks!_EH_epilog3
0668e298 79e7befa mscorwks!Binder::FetchClass+0x6d, calling mscorwks!ClassLoader::EnsureLoaded
0668e2b0 79e7c4bb mscorwks!Binder::CheckInit+0xb, calling mscorwks!MethodTable::IsClassInited
0668e2b8 79e819ac mscorwks!Binder::GetClass+0x60, calling mscorwks!Binder::CheckInit
0668e2e8 7a0801ef mscorwks!DispInvokeConvertObjectToVariant+0x129, calling mscorwks!OleVariant::CreateByrefVariantForVariant
0668e320 7a08358c mscorwks!IUInvokeDispMethod+0x9e4, calling mscorwks!ConvertEnumVariantToMngEnum+0xb6
0668e3b8 7a083412 mscorwks!IUInvokeDispMethod+0x74f, calling mscorwks!_alloca_probe_16
0668e3c4 79f364ba mscorwks!ComObject::GetComIPFromRCWEx+0x9b, calling mscorwks!_EH_epilog3
0668e3c8 7a08384e mscorwks!IUInvokeDispMethod+0x395, calling mscorwks!_alloca_probe_16
0668e3d0 7a0833b9 mscorwks!IUInvokeDispMethod+0x22a, calling mscorwks!ComObject::GetComIPFromRCWEx
0668e3d4 79f364ba mscorwks!ComObject::GetComIPFromRCWEx+0x9b, calling mscorwks!_EH_epilog3
0668e3d8 7a08383f mscorwks!IUInvokeDispMethod+0x386, calling mscorwks!_alloca_probe_16
0668e434 7a0832bb mscorwks!IUInvokeDispMethod+0x130, calling mscorwks!_alloca_probe_16
0668e470 7750b622 ole32!InterlockedDecRefCnt+0x44, calling KERNEL32!InterlockedCompareExchange
0668e48c 7750b6a8 ole32!CStdIdentity::CInternalUnk::Release+0x3d, calling ole32!InterlockedDecRefCnt
0668e4a4 79e84403 mscorwks!ReleaseTransitionHelper+0x5f
0668e53c 79e843a1 mscorwks!SafeReleaseHelper+0x12e, calling mscorwks!_EH_epilog3
0668e540 79e842ea mscorwks!SafeRelease+0x2f, calling mscorwks!SafeReleaseHelper
0668e548 79e842ff mscorwks!SafeRelease+0x44, calling mscorwks!_EH_epilog3

0668e568 79f4d7d4 mscorwks!SecurityStackWalk::SpecialDemand+0x4e
0668e574 79e74e3f mscorwks!HelperMethodFrame::HelperMethodFrame+0x1d, calling mscorwks!HelperMethodFrame::LazyInit
0668e580 7a11bd1f mscorwks!ReflectionInvocation::InvokeDispMethod+0x165, calling mscorwks!IUInvokeDispMethod
0668e5f4 7a11bbf7 mscorwks!ReflectionInvocation::InvokeDispMethod+0x40, calling mscorwks!LazyMachStateCaptureState
0668e668 79623fd9 (MethodDesc 0x79238ff0 System.RuntimeType.InvokeMember(System.String, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object, System.Object[], System.Reflection.ParameterModifier[], System.Globalization.CultureInfo, System.String[])), calling mscorwks!ReflectionInvocation::InvokeDispMethod
0668e74c 79e74a61 mscorwks!JIT_Stelem_Ref+0x25, calling mscorwks!JIT_Writeable_Thunks_Buf
0668e76c 793cd81a (MethodDesc 0x792390a8 +0x1aa System.RuntimeType.ForwardCallToInvokeMember(System.String, System.Reflection.BindingFlags, System.Object, Int32[], System.Runtime.Remoting.Proxies.MessageData ByRef))
0668e7e0 79e7be1b mscorwks!CallDescrWorker+0x33
0668e800 79e7bd9b mscorwks!CallDescrWorkerWithHandler+0xa3, calling mscorwks!CallDescrWorker
0668e880 79e7bce8 mscorwks!MethodDesc::CallDescr+0x19c, calling mscorwks!CallDescrWorkerWithHandler
0668e8c0 79e7b6e8 mscorwks!MetaSig::MetaSig+0x38, calling MSVCR80!memcpy [F:\SP\vctools\crt_bld\SELF_X86\crt\src\intel\memcpy.asm:101]
0668e93c 79e787b7 mscorwks!MethodTable::GetCl+0x8, calling mscorwks!MethodTable::GetClassCtorInfoIfExists
0668e94c 79e7c4e4 mscorwks!MethodTable::IsClassInited+0x23, calling mscorwks!DomainLocalModule::GetClassFlags
0668e95c 79e7c4bb mscorwks!Binder::CheckInit+0xb, calling mscorwks!MethodTable::IsClassInited
0668e988 79e7bbf2 mscorwks!MethodDesc::CallDescr+0x1f, calling mscorwks!_alloca_probe_16
0668e9cc 79e7bbd0 mscorwks!MethodDesc::CallTargetWorker+0x20, calling mscorwks!MethodDesc::CallDescr
0668e9e4 79e802f4 mscorwks!MethodDescCallSite::CallWithValueTypes_RetArgSlot+0x18, calling mscorwks!MethodDesc::CallTargetWorker
0668e9f8 7a040850 mscorwks!CLRToCOMLateBoundWorker+0x398, calling mscorwks!MethodDescCallSite::Call_RetArgSlot
0668ea74 7a08368a mscorwks!IUInvokeDispMethod+0xafe, calling mscorwks!__security_check_cookie
0668eb30 79e787b7 mscorwks!MethodTable::GetCl+0x8, calling mscorwks!MethodTable::GetClassCtorInfoIfExists
0668eb40 79e7c4e4 mscorwks!MethodTable::IsClassInited+0x23, calling mscorwks!DomainLocalModule::GetClassFlags
0668eb50 79e7c4bb mscorwks!Binder::CheckInit+0xb, calling mscorwks!MethodTable::IsClassInited
0668ebdc 7a11bd4f mscorwks!ReflectionInvocation::InvokeDispMethod+0x195, calling mscorwks!Frame::Pop
0668ebe0 7a11bc02 mscorwks!ReflectionInvocation::InvokeDispMethod+0x1ad, calling mscorwks!_EH_epilog3
0668ebfc 79f4b65a mscorwks!IsComWrapperClass+0x9, calling mscorwks!TypeHandle::GetMethodTable
0668ec94 7a11bc02 mscorwks!ReflectionInvocation::InvokeDispMethod+0x1ad, calling mscorwks!_EH_epilog3
0668ec98 79623fd9 (MethodDesc 0x79238ff0 System.RuntimeType.InvokeMember(System.String, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object, System.Object[], System.Reflection.ParameterModifier[], System.Globalization.CultureInfo, System.String[])), calling mscorwks!ReflectionInvocation::InvokeDispMethod
0668ecec 7a040a0c mscorwks!CLRToCOMWorker+0xaa, calling mscorwks!CLRToCOMLateBoundWorker
0668ed6c 79f1549e mscorwks!DoSpecialUnmanagedCodeDemand+0x60, calling mscorwks!ETWTraceStartup::TraceEvent
0668ed70 79f154a3 mscorwks!DoSpecialUnmanagedCodeDemand+0x65, calling mscorwks!_EH_epilog3
0668ed90 0091bc1e 0091bc1e, calling mscorwks!CLRToCOMWorker
0668edcc 06382578 (MethodDesc 0x45c2a40 +0x48 WebAstra.Application.VoIP.UniversalCOMServer.UCOMSipClient.PlaceCall(UInt32, System.String, WebAstra.Application.VoIP.UniversalCOMServer.UCOMCallData, Int32 ByRef)), calling 00938b92
0668edec 06382578 (MethodDesc 0x45c2a40 +0x48 WebAstra.Application.VoIP.UniversalCOMServer.UCOMSipClient.PlaceCall(UInt32, System.String, WebAstra.Application.VoIP.UniversalCOMServer.UCOMCallData, Int32 ByRef)), calling 00938b92
0668ee14 79f23aa2 mscorwks!COMNlsInfo::nativeChangeCaseString+0x18f, calling mscorwks!_EH_epilog3
0668ee28 05e3efc5 (MethodDesc 0x45c3838 +0x4c5 WebAstra.Application.VoIP.UniversalCOMServer.UCOMProvider.PlaceCall(WebAstra.Common.VoIP.CallContext)), calling (MethodDesc 0x45c2a40 +0 WebAstra.Application.VoIP.UniversalCOMServer.UCOMSipClient.PlaceCall(UInt32, System.String, WebAstra.Application.VoIP.UniversalCOMServer.UCOMCallData, Int32 ByRef))
0668eeb8 05e3e948 (MethodDesc 0x374ad38 +0x90 WebAstra.Application.Core.VoIP.VoIPManager.PlaceCall(WebAstra.Common.VoIP.CallContext)), calling 00938b06
0668eed8 05e3e948 (MethodDesc 0x374ad38 +0x90 WebAstra.Application.Core.VoIP.VoIPManager.PlaceCall(WebAstra.Common.VoIP.CallContext)), calling 00938b06
0668eef0 05e3e604 (MethodDesc 0x5e49ce8 +0x36c WebAstra.Common.Utils.Converter+Phone.ApplyNormalizationPlan(System.String, WebAstra.Common.VoIP.IVoIPLineConfigContext)), calling (MethodDesc 0x792377f8 +0 System.String.Concat(System.String, System.String))
0668ef00 05e3e212 (MethodDesc 0x374b6d8 +0x9a WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, WebAstra.Common.VoIP.IVoIPLineConfigContext, System.String)), calling (MethodDesc 0x5e4a548 +0 WebAstra.Common.VoIP.CallContext..ctor(WebAstra.Common.VoIP.CallOwner, System.String, WebAstra.Common.VoIP.IVoIPLineConfigContext, WebAstra.Common.VoIP.VoIPProtocol, Int64))
0668ef14 05e3e222 (MethodDesc 0x374b6d8 +0xaa WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, WebAstra.Common.VoIP.IVoIPLineConfigContext, System.String)), calling (MethodDesc 0x374ad38 +0 WebAstra.Application.Core.VoIP.VoIPManager.PlaceCall(WebAstra.Common.VoIP.CallContext))
0668ef40 05e3e108 (MethodDesc 0x374b6b8 +0xc0 WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, System.String)), calling (MethodDesc 0x374b6d8 +0 WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, WebAstra.Common.VoIP.IVoIPLineConfigContext, System.String))
0668ef5c 05e3ded1 (MethodDesc 0x59ee440 +0x69 WebAstra.Application.DialerManagement.DialerBase.PlaceCall(WebAstra.Application.DialerManagement.DestinationInfo)), calling (MethodDesc 0x374b6b8 +0 WebAstra.Application.CallManagement.CallManager.PlaceCall(WebAstra.Common.VoIP.CallOwner, System.String))
0668ef78 05e3db70 (MethodDesc 0x4421c90 +0x50 WebAstra.Common.Data.DataStore+LeadsRow.ChangeState(WebAstra.Common.Campaign.LeadState)), calling (MethodDesc 0x3d6fbd0 +0 WebAstra.Common.Data.DataStore+LeadsTable.ChangeState(LeadsRow, WebAstra.Common.Campaign.LeadState))
0668ef8c 05e3cf3d (MethodDesc 0x59ee3d8 +0xcd WebAstra.Application.DialerManagement.DialerBase.StartCall(System.Object))
0668ef90 05e37ac1 (MethodDesc 0x374ad88 +0x81 WebAstra.Application.Core.VoIP.VoIPManager.get_ConcurrentCallCount()), calling 0442dd78
0668efd0 05e3cdb5 (MethodDesc 0x59ee3d0 +0x5d WebAstra.Application.DialerManagement.DialerBase.StartCallInternal(System.Object))
0668eff8 05e3cd3e (MethodDesc 0x5e48020 +0xe WebAstra.Application.DialerManagement.DialerBase+<>c__DisplayClass6.<DialOneCall>b__4(System.Object, System.EventArgs)), calling (MethodDesc 0x59ee3d0 +0 WebAstra.Application.DialerManagement.DialerBase.StartCallInternal(System.Object))
0668effc 79e7be1b mscorwks!CallDescrWorker+0x33
0668f010 79e7bd9b mscorwks!CallDescrWorkerWithHandler+0xa3, calling mscorwks!CallDescrWorker
0668f090 79e7bce8 mscorwks!MethodDesc::CallDescr+0x19c, calling mscorwks!CallDescrWorkerWithHandler
0668f0d0 79e7b6e8 mscorwks!MetaSig::MetaSig+0x38, calling MSVCR80!memcpy [F:\SP\vctools\crt_bld\SELF_X86\crt\src\intel\memcpy.asm:101]
0668f0e8 79e7bc7a mscorwks!MethodDesc::CallDescr+0xbb, calling mscorwks!_alloca_probe_16
0668f164 79f13442 mscorwks!JIT_MonReliableEnter+0x25, calling mscorwks!LazyMachStateCaptureState
0668f168 79e74e5c mscorwks!HelperMethodFrame::LazyInit+0x17, calling  (JitHelp: CORINFO_HELP_GET_THREAD)
0668f180 79e7cb94 mscorwks!HelperMethodFrame_1OBJ::HelperMethodFrame_1OBJ+0x14, calling mscorwks!HelperMethodFrame::HelperMethodFrame
0668f19c 79e7bbf2 mscorwks!MethodDesc::CallDescr+0x1f, calling mscorwks!_alloca_probe_16
0668f1e0 79e7bbd0 mscorwks!MethodDesc::CallTargetWorker+0x20, calling mscorwks!MethodDesc::CallDescr
0668f1f8 79e802f4 mscorwks!MethodDescCallSite::CallWithValueTypes_RetArgSlot+0x18, calling mscorwks!MethodDesc::CallTargetWorker
0668f20c 7a11df2d mscorwks!InvokeImpl+0x43f, calling mscorwks!MethodDescCallSite::Call_RetArgSlot
0668f228 7a11dd7f mscorwks!InvokeImpl+0x28e, calling mscorwks!_alloca_probe_16
0668f244 7a11dc21 mscorwks!InvokeImpl+0xe5, calling mscorwks!_alloca_probe_16
0668f2b4 7c915041 ntdll!bsearch+0x42
0668f2d0 7c915233 ntdll!RtlpLocateActivationContextSection+0x15a, calling ntdll!bsearch
0668f3c0 79f13442 mscorwks!JIT_MonReliableEnter+0x25, calling mscorwks!LazyMachStateCaptureState
0668f3c4 79e74e5c mscorwks!HelperMethodFrame::LazyInit+0x17, calling  (JitHelp: CORINFO_HELP_GET_THREAD)
0668f3dc 79e7cb94 mscorwks!HelperMethodFrame_1OBJ::HelperMethodFrame_1OBJ+0x14, calling mscorwks!HelperMethodFrame::HelperMethodFrame
0668f400 7a11e212 mscorwks!RuntimeMethodHandle::InvokeMethodFast+0xbd, calling mscorwks!InvokeImpl
0668f45c 7a11e176 mscorwks!RuntimeMethodHandle::InvokeMethodFast+0x24, calling mscorwks!LazyMachStateCaptureState
0668f4c0 793cf049 (MethodDesc 0x792441b8 +0x49 System.RuntimeMethodHandle.InvokeMethodFast(System.Object, System.Object[], System.Signature, System.Reflection.MethodAttributes, System.RuntimeTypeHandle)), calling mscorwks!RuntimeMethodHandle::InvokeMethodFast
0668f50c 7940029f (MethodDesc 0x7923e670 +0x167 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo, Boolean)), calling (MethodDesc 0x792441b8 +0 System.RuntimeMethodHandle.InvokeMethodFast(System.Object, System.Object[], System.Signature, System.Reflection.MethodAttributes, System.RuntimeTypeHandle))
0668f520 79e8066a mscorwks!ClassLoader::LoadArrayTypeThrowing+0x17, calling mscorwks!TypeHandle::GetSignatureCorElementType
0668f544 793ac6da (MethodDesc 0x79237e00 +0x5a System.Delegate.DynamicInvokeImpl(System.Object[])), calling (MethodDesc 0x7923e670 +0 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo, Boolean))
0668f56c 0453f5e0 (MethodDesc 0x45cbd98 +0x60 WebAstra.Common.EventService.InvokeDelegate(System.Delegate, System.Object[]))
0668f578 79e8319a mscorwks!SigPointer::GetTypeHandleThrowing+0x2d9, calling mscorwks!Binder::FetchElementType
0668f580 79e77c32 mscorwks!_EH_epilog3_GS+0xa, calling mscorwks!__security_check_cookie
0668f584 79e8276d mscorwks!SigPointer::GetTypeHandleThrowing+0xe97, calling mscorwks!_EH_epilog3_GS
0668f590 79e7871a mscorwks!CorSigEatCustomModifiers+0x1c, calling mscorwks!CorSigEatAnyVASentinel
0668f5ac 79e786cd mscorwks!CorSigEatCustomModifiersAndUncompressElementType+0x19, calling mscorwks!CorSigEatCustomModifiers
0668f5d0 79e7be1b mscorwks!CallDescrWorker+0x33
0668f5e0 79e7bd9b mscorwks!CallDescrWorkerWithHandler+0xa3, calling mscorwks!CallDescrWorker
0668f660 7a12608d mscorwks!CallDescrWithObjectArray+0x3fa, calling mscorwks!CallDescrWorkerWithHandler
0668f6c4 79e8a7fd mscorwks!MetaSig::SizeOfActualFixedArgStack+0x12, calling mscorwks!MetaSig::ForceSigWalk
0668f6c8 7a125e02 mscorwks!CallDescrWithObjectArray+0xa9, calling mscorwks!ClrSafeInt<unsigned long>::addition
0668f6d4 7a125e0e mscorwks!CallDescrWithObjectArray+0xb5, calling mscorwks!_alloca_probe_16
0668f75c 7a1264ee mscorwks!CStackBuilderSink::PrivateProcessMessage+0x273, calling mscorwks!CallDescrWithObjectArray
0668f7d4 7c80b64e KERNEL32!_BaseDllInitialize+0x74, calling KERNEL32!ConDllInitialize
0668f7e8 7c80b663 KERNEL32!_BaseDllInitialize+0x43b, calling KERNEL32!__security_check_cookie
0668f8f0 7c919bd3 ntdll!LdrpSnapThunk+0xbd, calling ntdll!LdrpNameToOrdinal
0668f924 7c910732 ntdll!RtlpAllocateFromHeapLookaside+0x42, calling ntdll!_SEH_epilog
0668f95c 7c910732 ntdll!RtlpAllocateFromHeapLookaside+0x42, calling ntdll!_SEH_epilog
0668f990 7a0c2b9b mscorwks!WKS::gc_heap::try_allocate_more_space+0x7b6, calling mscorwks!WKS::gc_heap::adjust_limit_clr
0668f9d4 7a1262a6 mscorwks!CStackBuilderSink::PrivateProcessMessage+0x5a, calling mscorwks!LazyMachStateCaptureState
0668fae0 79e74e3f mscorwks!HelperMethodFrame::HelperMethodFrame+0x1d, calling mscorwks!HelperMethodFrame::LazyInit
0668faf0 79e85a7b mscorwks!HelperMethodFrame_PROTECTOBJ::HelperMethodFrame_PROTECTOBJ+0x14, calling mscorwks!HelperMethodFrame::HelperMethodFrame
0668fb08 794bc360 (MethodDesc 0x7913b3a8 +0x20 System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(System.RuntimeMethodHandle, System.Object[], System.Object, Int32, Boolean, System.Object[] ByRef)), calling mscorwks!CStackBuilderSink::PrivateProcessMessage
0668fb28 794bbde3 (MethodDesc 0x79243d90 +0x1ab System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Messaging.IMessageSink)), calling (MethodDesc 0x7913b3a8 +0 System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(System.RuntimeMethodHandle, System.Object[], System.Object, Int32, Boolean, System.Object[] ByRef))
0668fba4 794b2489 (MethodDesc 0x7926e630 +0x51 System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(System.Object))
0668fbb8 793d87af (MethodDesc 0x7925a608 +0x2f System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(System.Object))
0668fbc0 793608fd (MethodDesc 0x7913b948 +0x81 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object))
0668fbd4 793d8898 (MethodDesc 0x7925a618 +0x6c System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(System.Object)), calling (MethodDesc 0x7913b948 +0 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object))
0668fbec 79e7be1b mscorwks!CallDescrWorker+0x33
0668fbf0 76f61178 WLDAP32!_DllMainCRTStartup+0x52, calling WLDAP32!DllMain
0668fbfc 79e7bd9b mscorwks!CallDescrWorkerWithHandler+0xa3, calling mscorwks!CallDescrWorker
0668fc7c 79f36962 mscorwks!DispatchCallBody+0x1e, calling mscorwks!CallDescrWorkerWithHandler
0668fc9c 79f3690c mscorwks!DispatchCallDebuggerWrapper+0x3d, calling mscorwks!DispatchCallBody
0668fce8 79f3dc8b mscorwks!TableFreeSingleHandleToCache+0x50, calling mscorwks!DecrementMP
0668fd00 79f3699b mscorwks!DispatchCallNoEH+0x51, calling mscorwks!DispatchCallDebuggerWrapper
0668fd34 7a076ef6 mscorwks!Holder<DelegateInfo *,&AcquireDelegateInfo,&ReleaseDelegateInfo,0,&CompareDefault<DelegateInfo *>,2>::~Holder<DelegateInfo *,&AcquireDelegateInfo,&ReleaseDelegateInfo,0,&CompareDefault<DelegateInfo *>,2>+0xbb, calling mscorwks!DispatchCallNoEH
0668fd94 79ed8a2c mscorwks!Thread::UserResumeThread+0xfb
0668fda4 79ed89ca mscorwks!Thread::DoADCallBack+0x355, calling mscorwks!Thread::UserResumeThread+0xae
0668fdc8 79e71864 mscorwks!Thread::EnterRuntimeNoThrow+0x9b, calling mscorwks!_EH_epilog3
0668fe00 79e71914 mscorwks!PEImage::LoadImage+0x1e1, calling mscorwks!_SEH_epilog4
0668fe38 79ed88f1 mscorwks!Thread::DoADCallBack+0x541, calling mscorwks!Thread::DoADCallBack+0x2a5
0668fe74 7a0e4d96 mscorwks!Thread::DoADCallBack+0x575, calling mscorwks!Thread::DoADCallBack+0x4d4
0668fe9c 7a0e4dc3 mscorwks!ManagedThreadBase::ThreadPool+0x13, calling mscorwks!Thread::DoADCallBack+0x550
0668feb0 7a077c23 mscorwks!QueueUserWorkItemCallback+0xa6, calling mscorwks!ManagedThreadBase::ThreadPool
0668ff08 79f8b7a5 mscorwks!ThreadpoolMgr::ExecuteWorkRequest+0x40
0668ff20 79f8b341 mscorwks!ThreadpoolMgr::WorkerThreadStart+0x225, calling mscorwks!ThreadpoolMgr::ExecuteWorkRequest
0668ff4c 79e7593f mscorwks!EEHeapFree+0xa5, calling mscorwks!_EH_epilog3
0668ff94 79ed8e36 mscorwks!Thread::intermediateThreadProc+0x49
0668ffa0 79ed8e24 mscorwks!Thread::intermediateThreadProc+0x37, calling mscorwks!_alloca_probe_16
0668ffa4 7906d14a *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll -
mscorjit+0xd14a, calling mscorjit+0xdfe4
0668ffb4 7c80b6a3 KERNEL32!BaseThreadStart+0x37
0668ffbc 7906d14a mscorjit+0xd14a, calling mscorjit+0xdfe4

Pretty detailed. Now by looking at the bold segments of the stack, it seems there is something wrong.  We are calling an API (PlaceCall)  which expects and IDispatch type as parameter (we are creating the object just prior to making the call), then why so many fictitous Release calls. Who is releasing what?  Some more code-review showed a horrible and inexcusable mistake, the dispatch interface implemenation of the object we are passing to our API call, which is of CCmdTarget derived class type, doesn't have the MFC specific lock increment (ref count) in the ::ctor,
   
    AfxOleLockApp();

And rest becomes clear. The moral is if you violate COM rule, things will bite you in hard way.

Enough for today. I need a beer as soon :)

Posted On Sunday, November 09, 2008 9:44 AM | Feedback (0) | Filed Under [ .NET Core ]

Saturday, November 08, 2008

The Performance Bug

Its very tough to get into every possible part of modern software development process :- Debugging, Testing, Building, performance Optimization etc. Pardon me for skipping some sub-processes. But I was never hyper-aware about performance issues and It was always an afterthought of development process. But in the last couple of weeks I was continuously cracking my head in subtle issues related to performance optimization. For example when I am profiling a piece of test-case code for a database driver software I found following code:
for(int i=0; i < 750000; i++)
{
IDBCommand command = new XXXCommand();
command.CommandText = "Your [query/stored procedure] here";
//Some more code here...
}
The profiler showing a huge performance bottleneck at the piece of code where CommandText property is set. This seemed a bit strange to me. When I reverse engineered(I had no access to source code base tree till that) and found out following code at the set{} block of the property
public string CommandText
{
get
{
//...
}
set
{
//Code here...
if (string.Compare(this.m_commandText, value) != 0)
{
}
}
}
The profiler I'm using(AQTime4 : The best mixed mode profiler I have ever used although I didn't have chance to look into the Numega's DevPartner Studio) was very helpful in pointing out that the 90.1% of the time the method spends in exceuting the benign String.Compare method. Strange!!! "Alice in Perfmonland". A quick look at the .NET FCL's String::Compare method revealed what was going on.
public static int Compare(string strA, string strB)
{
return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None);
}
A navigation to the CompareInfo::Compare reveals a staggering information enough to solve the performance bottleneck. Its internally (For any method that CLR implements internally we have a MethodImplOptions.InternalCall flag with MethodImpl attribute) calling the unsafe function Compare. In all cases such 'internal' methods are implemented in native-C. Look into the 'Rotor' for good session of hacking the .NET InternalCall methods.
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe int Compare( void* pSortingTable, int win32LCID, string string1, string string2, CompareOptions options);

This function internally calls CompareString Win32 API
After discovering, I quickly prescribed that If you don't need some terrible Culture based UNICODE string comparison use != or == operator. Both the operator uses String::Equals which is 80% faster as far as my benchmark is concerned. To rise another step if you are comparing for empty string don't do :-

String.Compare(str1,String.Empty)

Do instead :-

str1.Length != 0

The last one is more faster although more subtle. This kind of optimizations are only visible if you have installed a pretty high loop(say 750000) into the test-code areas you suspect to have a performance bottleneck root. Performance Optimization is vast vast area and I just started taking a plunge into it. There are lot of topics to cover if somebody is interested and that goes down to IA-32 level Intel optimization techniques. For me the most interesting thing is that "How you can improve the performance of my .NET assembly by studying how JIT compiler generates fast IA-32 instructions and how could you optimize it even more by subtly changing the high-level code" Watch out for a mighty bite of "Performance Bug". Sooner or later you will be bitten. The more early it is , the more better you will be as a developer. See you soon.

-Thanks
Deb.

Posted On Saturday, November 08, 2008 6:36 AM | Feedback (0) | Filed Under [ .NET Core ]

Career @ Microsoft

Microsoft.DevelopmentPost msdev = new Microsoft.DevelopmentPost(); 
msdev.Candidate = this;
msdev.DieForProductDevelopment = true;
msdev.HasLatestDotNetKnowledge = true;
msdev.FollowMsExperts = new MsExperts[] { “Andrew Hejlsberg”, “Brad Abrams”, “Chris Anderson”, "Matt Pietrek" };
msdev.CrazyAboutList.Add(CrazyRepository.CLR);
msdev.CrazyAboutList.Add(CrazyRepository.PLINQ);
msdev.CrazyAboutList.Add(CrazyRepository.DynamicLanguage);
msdev.CrazyAboutList.Add(CrazyRepository.Monad);
msdev.CrazyAboutList.Add(CrazyRepository.ManagedOperatingSystem);
msdev.CrazyAboutList.Add(CrazyRepository.DynamicProgramming);
msdev.CrazyAboutList.Add(CrazyRepository.PlatformInteroperability);
msdev.CrazyAboutList.Add(CrazyRepository.LambdaExpression);
msdev.CrazyAboutList.Add(CrazyRepository.CCR);
try
{
msdev.Resume.Submit();
}
catch
{
msdev.Resume.Reschedule(System.DateTime.Now.AddHours(1));
}

-Thanks
Deb.

Posted On Saturday, November 08, 2008 6:27 AM | Feedback (1) | Filed Under [ .NET Core ]

The BSOD Factor



In a nice sunday afternoon I opened a MC++ sorry oldy MFC project in VS.NET.So far so good. Now i was going to insert a merely innocent breakpoint before firing the program. Mouse cursor changes to hourglass changes to infamous BSOD(Blue Screen Of Death for Windows). I ignored the memory dump and rebooted but Windows XP was unable to start. It seems some boot information table or partition table might have been corrupted in the process. Sad but inevitable in a developer's life.But I hade some serious unbacked-up data(Always be a good child and regularly backup your critical data) on the disk. Hope it could be retrieved. The entire evening I tried to restore the data using my liable Knoppix(Get the latest here) distribution.But I got stuck to a single point. How to transfer files to USB devices(pen-drive /hard-drive) from knoppix(knoppix is happily detecting windows partitions - hda1,hda2...) The next day(A gloomy monday) I handed the laptop(I forgot it was a nice VAIO laptop with 3.4 GHz,2 GB RAM) to PSS(to which it belongs) lab people. The PSS support attendee was a dumb one(real jerk grrrr...) and had no honest intention to help me out. Even he was sensitive about knoppix (I gave him the idea) in a Microsoft world.This is the kind-of-a-guy for which Bill-And-Co is cursed in GNU/GPL and 'Ux world. If a linux distribution can detect windows partitions and data could be recovered what's the problem? Microsoft implements .NET for FreeBSD (Which is a Unix variation), Mono.NET is roaring, Rotor(see my earlier post: Compiling The CLR) is rotating fast enough. Some people (and read some redundant people living inside The Giant also) are so shaky about new tricks/hacks that they will resist the light that wants to come in! So I patted myself and told something positive should be there in an otherwise negative condition.Let's practise some dark art - Linux at Microsoft. It seems that my knoppix had become aged v3.7 instead of the current v5.3.1. But I didn't want to download the ISO, burn and then start the rescue process. V3.7 got to support USB storage devices.I tried to mount the device's filesystem to a pre-created mount point namely(/mnt/usb)

[knoppix-root#] mount -t usbdevfs /dev/s.. /mnt/usb

Wait a minute. Is the device's name correct.I later found out how to figure out device names that are plugged/hot-plugged(USB stuff) into the system. The magic command is dmesg

[knoppix-root#] dmesg

Lots of dump. Lets apply a grep sd on that to search for usb devices(which are basically used SCSI emulation devices).

[knoppix-root#] dmesg | grep sd
----========================== --
--========================== :sda at sda1.. etc

But what should be the file system. I watched earlier in XP that it was FAT32. Must be. Linux can work with FAT32 and other 80 different file systems. In linux world FAT32 is known as vfat

[knoppix-root#] mount -t vfat /dev/sda1 /mnt/usb

No error! Hurray. Let's copy. Alas.."readonly filesystem". It seems that file-system that I mounted to /mnt/usb is readonly. But just how? After some sweating hours in front of Google(lazy programmers' guide) I found the culprit. The fstab (File System Table) has to be edited with proper user permission(as a part of linux security enforcement). The file is in /etc/ folder. But in knoppix /etc is readonly in normal shell.So /etc/fstab has to be copied to writable ramdisk(wonderful concept for On-CD linux) at /home/knoppix and edited.

[fstab entry] /dev/sda1 /mnt/usb vfat noauto,users,exec,rw 0 0

By root console I copied it back to /etc/fstab and start restoring my data. Red light in the drive is glowing...The world is not enough.... 

Re: Beg your pardon for putting this linux post in a .NET world. But I think it is excusable.

Posted On Saturday, November 08, 2008 4:08 AM | Feedback (0) | Filed Under [ .NET Core ]

WinDbg , Crash Dump .. Associated Headches - Part 1

For a long tome I kept myself away from this wonderful but ugly looking tool as I was the die-hard fan of a Compuware(Numega then) masterpiece - SoftIce. Hey I still have it installed inside of a Win98 Virtual Box in my XP host. Though SoftIce has got its new incarnation - DriverStudio, not the WinDbg. If you haven't downloaded it, get it Debugging tools for Windows. But I'm not going to give basics of WinDbg usage here.  Nop. But quality articles are galore. One thing I must mention that if your technology stack contains anything .NET, there is a nice WinDbg extension DLL(always keep an eye on software extensibility model) exists at the .NET framework installation folder. It's sos.dll(Son Of Strike). This guy will help to inspect virually anything in .NET world from method table to managed stack

This encounter with Windbg is actually initiated from requirements at my office. I was analyzing some crash dump generated by our driver library. Boy they are fatty. But still they are smaller than the earlier days. Actually these beasts are tagged as Mini Dump which is much light-weight than the full fledged dumps often called Core Dump. Mini Dumps are very different than their older counterparts in the following way

  • Instead of saving the entire process space, only certain sections are saved. There is no point in saving copies of modules such as Kernel32.dll; if the version number is included, it is easy to get a copy from a Windows CD. The actual memory heap of the application is by default not saved in a minidump; it is not required to debug a surprisingly high percentage of crashes. You can save the heap if you need to, however.
  • The minidump save code works to get accurate and full information for modules, including their names, paths, version information, and internal timestamps.
  • The minidump save code also gets the list of threads, their contexts (that is, register sets), and the memory behind their stacks.
  • The whole file is compressed, further reducing its size. A minidump for Notepad is around 6K on Windows XP, almost 300 times smaller than the previous crash dump of the same process.

Now when do we generates dump? Certainly not always. Instead one of these scary moments when your software "sees" end of days at customer premises. Most of the softwares now-a-days employs structure exception handling(SEH) and displays custom user-friendly UI if a fault occurs. But sometimes it is necessary for the developers to analyze what's exactly happened when the fault occurred. Handling the exception is certainly not the answer. Instead with some configuration changes(may be application configuration or infamous Windows Registry backdoor) the "on site" guy enables the dump generation facility and reproduces the fault once more. Following is a vanilla piece of code found somewhere in this context.

[C#]
try
{
     //where fault occurs...
}
catch(Exception e)
{
   if (TraceLevel == TraceLevel.GenerateDump)
   {
          DumpGenerator.WriteCrashDump();
   }
}

Couple of pointers here. First the check inside the exception handler is very important..I repeat..very very important. Here dump is generated as an elevated level of application(or driver) tracing configuration(again done by our beloved "on site" guy). Not always your application(or driver) should inflate the customer disks with fatty DMP files. Another subtle point - I didn't pass the exception object as a parameter to the dump generator function. As this is managed code the exception object will only contain the exception information pertaining to the managed stack. But we need an unmanaged Exception_Pointers struct to pass around. That could be generated within the function with the help of  GetLastError() API. Still there is a catch - if you P/Invoke the GetLastError call from C#, always remember that there is chance that CLR will invalidate the error while doing its own stuff when control returns from unmanaged world(where error occurred) to managed world by Interop Marshallar. Look at this entry of Adam Nathan's blog for more detail.

The most important parameter to the MiniDumpWrite function is the first and second.

typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
  DWORD ThreadId;
  PEXCEPTION_POINTERS ExceptionPointers;
  BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;


Naturally the exception pointers parameter is what I was talking about a liitle bit ago and its retrieval by GetLastError or Marshal.GetLastWin32Error. The first parameter identifies the thread which throws the exception.

Now from here the fun begins. I successfully able to analyze all but one DMP file by inspecting managed and unmanaged stack. But whenever I tried to open the offending DMP file, my WinDbg crashed. Oh God! Who will analyze the dump that WinDbg will generate? Purplexing !
Just before crash I saw a strange output at the WinDbg's console. Something related to invalid thraed id.

ERROR: Unable to find system thread 2
ERROR: The thread being debugged has either exited or cannot be accessed
ERROR: Many commands will not work properly

Now I was performing a suite of test cases while dump was generated. It was utmost important to me to know which process actually generated the dump so that I could narrow down the troubled section. Fortunately there is a tool in the same folder where WinDbg usually resides - dumpchk.exe. With

>>  dumpchk -e "offending_dump_file.dmp"

I was instantly presented with enough information to determine the offending test case. But that doesn't solve the problem of WinDbg's unusal crash. Later I found out(through debugging - without dumpchk that even would have been impossible)  that when ExceptionPointers parameter is not null(means exception actually happened at unmanaged layer of our code) we are pushing the "passed" thread id to this structure before calling the write dump function.Although if you pass null or 0 to this ExceptionPointers parameter dump will be generated but its unmanaged stack will be ignored. So to track back how we were passing the thread id to this function, I discovered following code

#ifdef  DOTNET20
     threadId = System.Threading.Thread.ManagedThreadId();
#else
    threadId = AppDomain.GetCurrentThreadId();
#endif

Surely the Appdomain.GetCurrentThreadId API has been deprecated but It does something the newer API doesn't and neither supposed to that.
Managed threads has nothing to do with unmanaged threads. The mapping between managed thread to unmanage thread is tricky. The newer API is just a hash  value used to determine a managed threads uniquely. It doesn't corresponds to the unmanaged thread. The mapping between these thraeds of two worlds could be controlled by a CLR host or could be changed by future versions of .NET Fx. Due to this unstability Microsoft recommends that you are not supposed to assume 1:1 correspondence between manage and unmanaged thread while using AppDomain.GetCurrentThreadId() in your code. Although in 2.0 it happens to return the unmanaged id but who knows. Sonner or later this code will definitely break. Check at this blogs about manage and unmanaged threads - 1, 2

With "not-so-offending" dump files your "!analyze -v" session will be peaceful. It's 3 AM here at India. I'm tired..See you guys

Posted On Saturday, November 08, 2008 3:56 AM | Feedback (3) | Filed Under [ .NET Core ]

AI RoboForm, BHO and C#

Remember the awesome AI RoboForm. Its the smartest of all web form fillers exist on earth. In case you haven't used it you can found it here. From password memorization (I'm extremely shaky about trusting any third party software expecially those deals with password, CCs etc.), to secure transfer of user data profile to removable devices by Pass2Go RoboForm has it all. Although its seems easy until I myself went on creating some proof of concept equivalent to this.
To achieve anything closer to this I need to create something called plugin. You must be saying a - 2yr-old kid also know about plugins. What's your point? To be exact I need to create something called BHOs or browser helper objects. These are some browser specific extensibility modules for doing things that the core browser engine not supposed to do. But this same thing can open up a whole new dimension to virus writers and hackers. You know its easy to sit into the lap of Internet Explorer and watch your credit card number and CCV passing by. At certain point of time(most probably around 5.5 release) Microsoft stopped the support for writing IE plugins but the model came back in the name of BHOs. As a part of SP2 patchset of XP, a BHO control manager option has been added to IE. My BHO has to be listed there in order to justify itself as a BHO!


The first thing you should do to write a BHO in managed language is to recreate the C++/COM interface signature of IObjectWithSite in C#.

[ComVisible(true),
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
 Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352") ]
 public interface IObjectWithSite
 {
  [PreserveSig]
  int SetSite ([MarshalAs(UnmanagedType.IUnknown)]object site);
  
  [PreserveSig]
  int GetSite (ref Guid guid, out IntPtr ppvSite);
 }

 Now I'm going to implement this interface. To supply the GUID for this class I called up guidgen.exe which could easily be accessed from Tools >> Create GUID


My class implementing the IObjectWithSite interface is following. Now as the same GUID has to be registered with windows registry I made the declaration of it as const string(just like static but accessible from attribute)

[ComVisible(true),
 ClassInterface(ClassInterfaceType.None),
 Guid(SiteWatcherBHO.BHO_GUID)]
 public class SiteWatcherBHO : IObjectWithSite
 {
     public const string BHO_GUID = "FFF9DE52-E5A5-4217-B938-DFF5EA95B4CD"; // << Your GUID goes here    

     //SetSite(), GetSite() impl...
 }

Before implementing meat of our BHO lets set the framework of its registration. While registering the BHO we have to create a subkey at
SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\ with name same as of the generated GUID tagged around the class implementing IObjectWithSite. To simplify the registering process of COM modules(BHOs fall into this category) written as a .NET module Microsoft has provided an utility called RegAsm. One of the interesting switches associated with RegAsm is /codebase. Upon providing this with the commandline, RegAsm automatically searches for a public static method in the assembly which is tagged with the ComRegisterFunction attribute. The benefit is tremendous. You can do any kind of registration work inside the function. As some COM modules require the creation of some special key at some special location this technique proves to be very useful. But if required you can go online, update your online database, call webservice ..anything inside this special method and the fun part is that all this is being done under the cosy corner of .NET. But to be good citizen and play fair you shold pair up your registration function and should provide a static method tagged with ComUnregisterFunction attribute. Reason is simple when your uninstaller calls to unregister the module, depending upon your scenario you should partially or totally undo what you have done in the registration function.

public class SiteWatcherBHO : IObjectWithSite
   {
      [ComRegisterFunction]
      internal static void RegisterFunction(Type typeToRegister) {}

      [ComUnregisterFunction]
      internal static void UnregisterFunction(Type typeToRegister) {}
   }

Below I will show the code I used in my BHO class

[ComRegisterFunction]
  public static void RegisterBHO(Type t)
  {
   try
   {
    Microsoft.Win32.Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{" + SiteWatcherBHO.BHO_GUID + "}");
   }
   catch(Exception e)
   {
#ifdef DEBUG

    System.Windows.Forms.MessageBox.Show(e.ToString());
#endif
   }
  }

See the const declaration of the GUID comes handy here. Another important point is the presence of preprocessor directives. Now in the production release of my CCNumberCollector BHO I'm not supposed to show any messages. Not even a log. Just devour the entire exception like what has been shown here. "Leave no trace" is The mottow for all "underground" guys. Now GetSite() and SetSite() both will be called during the creation and termination of the object we are implementing here(means our BHO object) but each time with a different argument for the site parameter. For SetSite() the site will be non-null while creation and null while termination. Now lets fix the job of our BHO. In this sample BHO I will fillup the username and password of my yahoo mail account and auto-login into it. Saying this I need an appropriate place to do all the fillup stuff. DocumentComplete event is an ideal for such scenario. Now the site object passed at SetSite() is very very precious. Because it is our beloved SHDocVw.WebBrowser instance. Once we have this at our disposal and we added a reference to MSHTML module(through Interop obviously) we can play anything with IE. To generate the Winform equivalent of IE ActiveX you have to use the AxImp tool. For the second task tlbimp is necessary.

1. aximp c:\windows\system\shdocvw.dll

2. tlbimp mshtml.tlb

The code at SetSite() implementation should look something like below

                 if (site != null)
                {
                    webBrowser = (WebBrowser)site;
                    if (webBrowser != null)
                    {
                        webBrowser.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(webBrowser_DocumentComplete);
                    }
                }
                else
                {
                    if (webBrowser != null)
                    {
                        webBrowser.DocumentComplete -= new DWebBrowserEvents2_DocumentCompleteEventHandler(webBrowser_DocumentComplete);
                        webBrowser = null;
                    }
                }

We store the IE web browser instance in a private member of type SHDocVw.WebBrowser. Now what our DocumentComplete event handler does is very simple. If the current URL looks like a yahoo mail, it fills the username and password boxes and auto-login by executing JScript code dynamically on the parent window containing the HTML document.

      IHTMLDocument2 document = (IHTMLDocument2)webBrowser.Document;

   if (document != null)
   {
      //See if we are in codeproject
      string url = document.location.href;

      if (url.IndexOf("mail.yahoo.com") >= 0)
      {
          string sJScript = @"var ctlUserID = document.getElementById('username');
                               var ctlPassword = document.getElementById('passwd');

                                if ((ctlUserID != null) && (ctlPassword != null))
                                {
                                    ctlUserID.value = 'debasish.bose@gmail.com'
;
                                     ctlPassword.value = '*******;
                                }";

          document.parentWindow.execScript(sJScript, "JScript");

          sJScript = "login_form.submit();";
          document.parentWindow.execScript(sJScript, "JScript");

          return;
       }
    }

Hardcoding the password is the crudest method I can imagine right now. It just for demonstration purpose. In reality you should be fetching it into a SecureString from some encrypted password repository(which may prove useful in building something like a master password or SSO system). That's it. Now how to capture the CC numbers is upto you. Oh by the way I bought a cute PSP and enjoying every moment with it. My N-Gage is crying like anything.


Posted On Saturday, November 08, 2008 3:49 AM | Feedback (4) | Filed Under [ .NET Core ]

Binary Tree, C# and Delegates - Part 2

Well the "brain-brush" series continues. One of my favourite binary tree question would be given a preorder and inorder traversal strings write a function to build the tree. Most of the rubbish text books provide non-programmatic solution to this and rest append it as a to-do. Today I will proceed step-by-step to solve this and that too with same ingredients - delegate, generics and C#2.0. Disclaimer still holds: "This is just a fundamental recapitulation of binary tree and data structure as a whole. Geeks excuse me please for this series.

In the earlier post I discussed the simple data structure of treenode and binary tree. Just to repeat once following is the skeleton of my binary tree class

public class BinaryTree
{
        //root node
        public TreeNode root;

       //Algorithms...InOrder etc.
}

Now for simplicity lets assume that the binary treenode contains single character data. So instead of embedding our tree-building algo inside this generic BinaryTree class I specialized ot to create a subclass called CharBinaryTree. Lets construct the base logic Assume we have following binary tree -


Preorder traversal string and Inorder traversal strings are  124536 and 425163 respectively Lets now back calculate and see how one might can possibly build the tree. Firstly preorder string always helps to get the root right. VLR. Does it ring a bell? But then how to differntiate between left subtree(LT) and right subtree(RT). Well simple. Split the inorder string based on the pivotal root. Left substring is LT and right substring is RT. Now we have to recurse. That's fine. But what are the pivotal indexes for LT and RT. Simple - if you can see dry-run the first recursion frame

root := 1, LT := 425, RT := 63
pivot(LT) := pivot+1, pivot(RT) := pivot + len(LT) + 1.

Naturally these pivotal characters will be the roots of the respective subtrees. Enough said. Let's dump the C# code right now.

private TreeNode MakeTree(string inorderString, int preOrderIndex)
{
     TreeNode p = new TreeNode(' ');

     // terminating condition 
     if (inorderString.Length == 1)
     {
         char.TryParse(inorderString, out p.nodeData);
     }
     else
     {
         char pivot = this.preorderString[preOrderIndex];
         int rootIndexInorder = inorderString.IndexOf(pivot);
         string left  = "";
         string right = "";

         if (rootIndexInorder > 0)
         {
             left = inorderString.Substring(0, rootIndexInorder);
             right = inorderString.Substring(rootIndexInorder + 1);

             p.nodeData = pivot;

             if (left.Length != 0)
                p.left = MakeTree(left, preOrderIndex + 1);

             if (right.Length != 0)
                p.right = MakeTree(right, preOrderIndex + left.Length + 1);
                    
          }

     }

     return p;
}
 

This goes the private "workhorse" function as described in my earlier post of the DS-BrainBrush series. Lets see the driver function 

public TreeNode MakeTree(string inorderString, string preorderString)
{
     //Couple of pre-checks
     if ((inorderString.Length == 0) ||
         (preorderString.Length == 0))
     {
         throw new ArgumentException("Traversal strings shouldn't be empty");
     }

     if (inorderString.Length != preorderString.Length)
     {
         throw new ArgumentException("Traversal strings should have identical length");
     }

     //Make a copy of traversal strings
     this.preorderString = preorderString;
     this.inorderString = inorderString;

     //Make a call to "workhorse"
     this.root = this.MakeTree(this.inorderString, 0);

     return this.root;
  }  

Everything fits nicely in the "workhorse-driver" model. In the next post of this series I will show how to create a special binary tree called Binary Expression Tree(BET) heavily used in world of lexical analyzer, parser and compilers. See you soon.

 

Posted On Saturday, November 08, 2008 3:37 AM | Feedback (2) | Filed Under [ .NET Core ]

Binary Tree, C# and Delegates - Part 1

During the last week I was brushing up my Data Structure fundamentals. It was great to get back the days of C style pointers, link list, binary trees etc. But at the same time I want to play around the anonymous delegates of C# 2.0. Both my wishes were granted...My data structures were based on C# generics. The TreeNode structure and the action delegate were both templatized(i.e. based on Generics

public delegate void Action<T>(TreeNode<T> p);
public class TreeNode<T>
{
        //Data
        public T nodeData;

        //Self pointers
        public TreeNode<T> left;
        public TreeNode<T> right;

       //More stuff...
}

Pretty simple stuff. The task I wanted to implement first is to do a inorder walk down the tree without using recursion. My primary objective in this blog is to resurrect the dying spirit of Data Structure fundamentals ,not to show a new discovery or geeky stuff. Now to do an Inorder walk non-recursively we need a stack. You name it , System.Collections.Generic has it.

//declare a stack
Stack<TreeNode<T>> s = new Stack<TreeNode<T>>();
TreeNode<T> ptr = nodePtr;
               
do
{
  while (ptr != null)
  {
     s.Push(ptr);
     ptr = ptr.left;
  }

  ptr = s.Pop();

  action(ptr);

  ptr = ptr.right;

}while (s.Count != 0);

Let's give the interface of the function. 

private void InOrder(Action<T> action, TreeNode<T> nodePtr)

Another short pointer for the usage of private qualifier. For all(well almost) traversal algorithms of the tree it is always wise to keep two versions. One private method taking a generic TreeNode*(in C) or TreeNode(in C#) and another top level public function calling the private counterpart with the root as a value of the TreeNode* or TreeNode refrence variable. The private function is often called "workhorse.

public void InOrder(Action<T> action)
{
    this.InOrder(action, root);
}

Now the funniest part. The calling portion

tree.InOrder(delegate(TreeNode<int> p)
             {
                 lblPrintTree.Text += p.nodeData.ToString();
             });

We are injecting our own callback function to the InOrder tree traversal algorithm through the anonymous generic delegate . C#2.0 is good.  Some guys might think that this is oldy but LINQ, PLINQ, Lambda Expressions are good but more and more language features of csharp are more of a syntactic sugars than anything else. Soon I will publish couple of posts in the context of same topic - "Data Structure Fundamentals" brain-brush. See part 2.

Posted On Saturday, November 08, 2008 3:34 AM | Feedback (0) | Filed Under [ .NET Core ]

IntPtr, 64 bit Porting and Fun

Guys I was busy in some HLD for past two weeks or so. I'm back now into my favourite blogspace  - GWB.

Yesterday when our 64 bit porting team is working for the immediate 64 bit release of our product, they came to me for suggestion on a topic which I think worth discussing. While they are happily ported unmanaged C++ code, they were facing some difficulties with porting managed code(which should be easier! right?). In unmanaged C++ world they handled pointer disparity between the platforms with smart #ifdefs and #define macros. But in managed C# layer we do have some unsafe pointer code that interoperate to the unmanaged C++ code. Situation become tricky. As there is no #define macros in C# world, they were unable to write ubiquitous code that would work on either platform.

In one particular situation we have a long native 64 bit pointer allocated in unmanaged layer and C# unsafe code would like to interoperate with it in following way.

                     ___ ___ ___ ___ ___ ___ ______
x ----------> |___|___|___|___|___|___|___|___|

In 32 bit platform we wanted to access first 4 bytes(32 bit pointer size) of x

... = *((int*)x)

In 64 bit platform ,analogously, we wanted to access first 8 bytes(64 bit pointer size) of x

... = *((__int64*)x)

And in both cases we also used to use integer pointer arithmatic by adding integer offset values to (int*)x or (__int64*)x (which hasn't been shown). No problem as such here. The problem relates to merging these two set of lines to one. I immediately scream "use UIntPtr...". But just how? Well we need to construct a UIntPtr out of the long value - x. In looking through Reflector the specified ctor appears like below

public unsafe UIntPtr(UInt64 value)
{
      this.m_value = (void*) ((uint) value);
}

But mine was a 32 bit CLR. What I guess that in 64 bit this ctor will not truncate the long value. It should be something like below

public unsafe UIntPtr(UInt64 value)
{
      this.m_value = (void*) value;
}

A look into Rotor confirmed my guess. One can check IntPtr.cs file here

public unsafe UIntPtr(UInt64 value)
00051         {
00052             #if WIN32
00053                
m_value = (void *)checked((uint)value);
00054             #else
00055                
m_value = (void *)value;
00056             #endif
00057         }

The consequence is significant. This means m_value holds platform-specific integer pointer and so does the return value of ToPointer() which just exposes the m_value.

public unsafe void* ToPointer()
{
      return this.m_value;
}


So the code should be something like below

UIntPtr uiptr = new UIntPtr(x);   // Invoke the specified ctor
... = *(uiptr.ToPointer());

But the explicit construction of the UIntPtr doesn't look elegant. Then I looked at the following casting operator of UIntPtr which takes a long(64 bit) value and convert it to an UIntPtr -

public static explicit operator UIntPtr(UInt64 value)
{
      return new UIntPtr(value);
}

This conversion operator internally invokes the same ctor we are interested with. So now the revised code looks like

... = *(((UIntPtr)x).ToPointer());
 
64 bit porting of managed application sometimes can create little surprises. When managed code heavily deals with P/Invoke, COM Interop and unsafe(c#) code porting becomes non-trivial. If interested you can look at this official MSDN 32-to-64 bit migration guide for managed code http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/64migrate.asp.

By the way I'm reading two wonderful books  -  both are theoretically non-windows books - "Art of Unix Programming" and "Innovation Happens Elsewhere". Do lots of programming4fun.

Posted On Saturday, November 08, 2008 3:05 AM | Feedback (0) | Filed Under [ .NET Core ]

Powered by: