Welcome to MSDN Blogs Sign in | Join | Help

Debugging a managed 64 bit service

I was trying to put a breakpoint in the Main function of a managed process which was being launched by a managed service. I knew about the IFEO (Image file execution option) and I was trying it with vsjitdebugger.exe but it just would not attach. A little web search led me to great blog entries by Mike. The problem was that both the service and the process were not only managed but also 64 bit. Currently, it seems vsjitdebugger.exe does not support this scenario. Anyhow, Mike also has another blog entry where he explains a bit on how managed and native debuggers operate.

But I was still stuck on how to debug the service or the foo.exe launched by the service. The only clean way is to modify our code in the Main function (or OnStart if it is a service) and use Debugger.Launch() (so annoying).

public static void Main()

{

    //you can use #define DEBUG also so that this become debug only code 

    try

    {

      if (!Debugger.IsAttached)

        { 

            Debugger.Launch();

             Debugger.Break();
         }

    }

    catch (Exception ex)

    {

        //log and see which exception was thrown.
 

    } 

    // continue with your main function 

But then I was interested in how things work here.

Q . How does Debugger.Launch() know which debugger to start and where is it listed?

 Ans.


Posted by Varun Sekhri | 0 Comments
Filed under:

Some good books .....

If you dont know about the aha! factor then read the book -  Programming Pearls by Jon Bentley. The problems and the solutions will definitely make you say aha! :).

The next one on my reading list is The Medical Detectives by Berton Roueche

PS: I will update this blog entry whenever I happen to find such great books.

Posted by Varun Sekhri | 1 Comments

How do we talk with COM the language of events and delegates

I wrote this blog entry from a real world customer issue and noticed that how little documentation exists on this topic.

I assume that you already know what is a source interface in COM and what are IConnectionPoint and IConnectionPointContainer interfaces. Please pick up any COM book to learn that (I would reccommend Essential COM by Don Box). Also, a familiarity with the events and delegates would help.

The example code in this article are picked up from a real world scenario given by a customer.

 

Lets assume that we have a COM server which has a source interface as defined in the following IDL.

Note: The best way to learn from this article would be to

1) create a typelibrary from the IDL given above

2) create the interop dll using tlbimp.exe

3) Use ildasm to read what is written inside the interop dll.

//Start of the idl

import "oaidl.idl";

import "ocidl.idl";

[

object,

uuid(E3AA8979-3038-4CCD-BA37-13F29F1E61F1),

dual,

nonextensible,

helpstring("ILegacyComObject Interface"),

pointer_default(unique)

]

interface ILegacyComObject : IDispatch{

[id(1), helpstring("method DoSomething")] HRESULT DoSomething(void);

};

[

uuid(CD839D63-B5CB-4236-825F-9EF2C943261B),

version(1.0),

helpstring("AtlComClient 1.0 Type Library")

]

library AtlComClientLib

{

importlib("stdole2.tlb");

[

uuid(C23B1EFE-1A27-4200-B14C-5F2019E024C5),

nonextensible,

helpstring("_ILegacyComObjectEvents Interface")

]

dispinterface _ILegacyComObjectEvents

{

properties:

methods:

[id(1) ] VARIANT_BOOL CanDoSomething();

[id(2) ] void DoneSomething();

};

[

uuid(A2272EDE-8103-43A7-A819-286EAE2C2A0A),

helpstring("LegacyComObject Class")

]

coclass LegacyComObject

{

[default] interface ILegacyComObject;

[default, source] dispinterface _ILegacyComObjectEvents;

};

};

//End of the idl

 

 

 

In common terms we also say that the com server supports two "events" CanDoSomething and DoneSomething.

NOTE: ILegacyComObject is just some implemented interface by the com server.

 

When we create an inteop dll using tlbimp.exe it will contain the following classes and interfaces.

 

1) We need two delegates for the two events defined. Our naming convention is <interfacename>_<event name>EventHandler. Also note that since delegates are types so we will have two classes defined in the interop dll by the name:

_ILegacyComObjectEvents_CanDoSomethingEventHandler

_ILegacyComObjectEvents_DoneSomethingEventHandler

 

2) We have an interface defined as _ILegacyComObjectEvents with the correct ComVisible and GUID attributes.

 

3) Tlbimp.exe also creates a second event interface, designated by the "_Event" suffix added to the name of the original interface. This second event interface has DoneSomething and CanDoSomething events as members. It also has add and remove methods for the event delegates. In this example, the interface is called _ILegacyComObjectEvents_Event.

 4) The coclass generated would implement ILegacyComObject and _ILegacyComObjectEvents_Event (Note) and be named as LegacyComObjectClass. For points 3 and 4 also msdn has some good article http://msdn2.microsoft.com/en-us/library/k639e386.aspx

5) We generate a class <source interface name>_SinkHelper which implements the source interface methods (or in other words it implements _ILegacyComObject interface). Thus we would pass an instance of this class everytime IConnectionPoint::Advise is called. This class also stores the cookie returned from Advise.

 

Here is the pseudo code of the class

 

class _ILegacyComObjectEvents_SinkHelper : _ILegacyComObjectEvents

{

  //store the cookie returned from the IConnectionPoint::Advise call

  public Int m_dwCookie;

// store the delegates from the user

public _ILegacyComObjectEvents_CanDoSomethingEventHandler m_CanDoSomethingDelegate;

 

  public _ILegacyComObjectEvents_DoneSomethingEventHandler m_DoneSomethingDelegate;

 

 ILegacyComObject_SinkHelper ()

{

                m_dwCookie = NULL;

m_CanDoSomethingDelegate= NULL;

m_DoneSomethingDelegate = NULL;

}

 

bool CanDoSomething()

{

          if (m_CanDoSomethingDelegate ! = NULL)

return m_CanDoSomethingDelegate.Invoke(); //Invoke the delegate

          return 0;   // notice the default value

// See the Notes below for the return type

}

void DoneSomething()

{

          if (m_DoSomethingDelegate ! = NULL)

                return m_DoSomethingDelegate.Invoke(); //Invoke the delegate

          return;

}

}

 

6) We generate an EventProvider with name <source interface name>_EventProvider

This is the main class which provides the inter-operation between the COM server and the client. Note that it also implements the IDisposable interface.

 

Here is the pseudoCode.

class _ILegacyComObjectEvents_EventProvider: IDisposable, _ILegacyComObjectEvents_Event

{

// store the IConnectionPoint object from the COM server here

IConnectionPoint m_ConnectionPoint;

 

// runtime will provide the implementation of this

IConnectionPointContainer m_ConnectionPointContainer;

 

//arraylist of object of type SinkHelpers

ArrayList m_aEventSinkHelpers;

 

public _ILegacyComObject_EventProvider(Object A1)

{

  m_ConnectionPointContainer = (IConnectionPointContainer)A1;

}

 

void Dispose()

{

_ILegacyComObjectEvents_EventProvider::Finalize();

GC.SuppressFinalize();

}

void Finalize()

{

                // save yourself from the threading issues

System.Threading.Monitor::Enter(this);

           try

{

for (each x in m_aEventSinkHelpers)

{

  // call unadivse on each connection made

  m_ConnectionPoint.UnAdvise(((_ILegacyComObject

Events_SinkHelper)x).m_dwCookie);

}

System.Runtime.InteropServices.Marshal::ReleaseComObject(m_ConnectionPoint);

          }//end try

      catch (Exception)

     {

               

      }//end catch

      finally

      {

System.Threading.Monitor::Exit(this);

      }//end finally

}//end of Finalize

 

// This function gets the IConnectionPoint implementation from the server for the given

// interface. Also intializes the arraylist of EventSinkHelpers.

void Init ()

{

m_ConnectionPointContainer.FindConnectionPoint(guid of _ILegacyComObject,

                                                                                    &m_ConnectionPoint);

                Initialize the arraylist m_aEventSinkHelpers;

}//end of Init

 

public void add_CanDoSomething(_ILegacyComObjectEvents_CanDoSomethingEventHandler A_1)

{

//declare some temporary variables.

_ILegacyComObjectEvents_SinkHelper V_0;

// Save yourserlf from the threading issues.

 System.Threading.Monitor::Enter(this);

try

{

                // first check whether we have IConnectionPoint with us

 if (m_ConnectionPoint==NULL)

                                              Init();

V_0 = new _ILegacyComObjectEvents_SinkHelper();

//call the advise method

m_ConnectionPoint.Advise(V_0, &V_1);

// store the delegate and the cookie

V_0.m_dwCookie = V_1;

V_0.m_CanDoSomethingDelegate = A_1;

//add the EventSinkHelper to the arraylist

m_aEventSinkHelpers.Add((object) V_0);

} //end try

finally

{

   System.Threading.Monitor::Exit(this);

}//end finally

}// end add_CanDoSomething

 

// We have a similar implementation for add_DoneSomething

 

public void remove_CanDoSomething (_ILegacyComObjectEvents_CanDoSomethingEventHandler A_1)

{

                //declare some temporary variables

_ILegacyComObjectEvents_SinkHelper V_2;

System.Threading.Monitor::Enter(this);

try

{

for (each x in m_aEventSinkHelpers)

{

                                if ((_ILegacyComObjectEvents_SinkHelper) x.m_CanDoSomethingDelegate == A_1)

                                {

                                                m_aEventSinkHelpers.Remove(x);

                                                V_2 = (_ILegacyComObjectEvents_SinkHelper)x;

                                                m_ConnectionPoint.UnAdvise(V_2.m_dwCookie);

}             

}

 if (m_aEventSinkHelpers is empty)

    System.Runtime.InteropServices.Marshal::ReleaseComObject(m_ConnectionPoint);

}//end of try

finally

{

System.Threading.Monitor::Exit(this);

}

}//end of remove_CanDoSomething

 

// We have a similar implementation for remove_DoneSomething

 

} //end of class _ILegacyComObject_EventProvider

 

 

EventProvider does the main work for us. In general every time a user adds a delegate

1)      Get the IConnectionPointer if not available from the COM server and store it in the EvenProvider.

2)      Create a new instance of EvenSinkHelper.

3)      Call Advise and give the EventSinkHelper implementation to the server.

 

Here is something unexpected which the users will notice.

 

In COM, a user may create and give one implementation of _ILegacyComObject to the server. When the event occurs the server will call both _ILegacyComObject::CanDoSomething and _ILegacyComObject::DoneSomething. Client calls only one IConnectionPoint::Advise.

 

However in the managed code the user would need to do something like

 

private LegacyComObject m_legacyComObject;

m_legacyComObject = new LegacyComObject();

//add the delegate for CanDoSomething

m_legacyComObject.CanDoSomething += m_legacyComObject_CanDoSomething;

//add the delegate for DoneSomething

m_legacyComObject.DoneSomething += m_legacyComObject_DoneSomething;

 
// m_legacyComObject_CanDoSomething and m_legacyComObject_DoneSomething are two functions implemented in the user class.

 

Because we treat the two methods as two separate events we need to make two calls to _ILegacyComObjectEvents_EventProvider::add_CanDoSomething and _ILegacyComObjectEvents_EventProvider::add_DoneSomething

Looking at the pseudo code above this would lead to two separate connections or two separate Advise calls and which is inefficient.

In this case, for example, in the first call we create _ILegacyComObjectEvents_SinkHelper instance and call IConnectionPoint::Advise on it. Also notice that _ILegacyComObjectEvents_SinkHelper::m_DoneSomethingDelegate will remain NULL. If unmanaged COM server invokes this DoneSomething event then we would simply return a default value. The delegates are allowed to return only the primitive types or the value types.

 

Bitness of the loaded typelibrary

If you use LoadTypeLib or LoadTypeLibEx then Ole Automation always convert the typelibrary according to the process bitness. So if we load a 32 bit typelibrary using LoadTypeLib/LoadTypeLibEx in a 64 bit process then it will be automagically converted to a 64 bit typelib in memory and vice versa.

As you may see that this is a one time perf hit but also enables users who for some reasons (best known to them) are unable to obtain a 64 bit or 32 bit typelibrary. Also, note that by definition ITyepLib::GetLibAttr would always show the persisted bitness of the typelibrary (i.e. the one present on the disk).

Marshaling BSTRs in COM/Interop or P/Invoke

I saw people making mistakes with BSTR marshaling in the COM/Interop space. Unfortunately msdn does not have many good examples on this subject. I will share some specific code examples which may help. The examples are only for reference which may be made more robust as needed.

What is so special about BSTR?

As http://msdn2.microsoft.com/en-us/library/ms221105.aspx states BSTRs are special strings defined by Ole Automation for which memory is allocated by the system when users call SysAllocString/SysAllocStringLen. Memory allocated needs to be freed by a corresponding call of SysFreeString. Size of the BSTR is stored at 4 bytes before the actual BSTR pointer returned by SysAllocString/SysAllocStringLen on both 32bit and 64 bit platform.

How to marshal a BSTR in COM/Interop or P/Invoke calls?

Marshal a BSTR following the recommendation given at http://msdn2.microsoft.com/en-us/library/s9ts558h.aspx i.e. by using MarshalAsAttribute.

How to pass BSTR as an In/Out parameter? 

http://msdn2.microsoft.com/en-us/library/x3txb6xc.aspx targets only simple strings (LPTSTR) where the buffer is passed to the P/Invoke call using StringBuilder. BSTR's are special in the sense that the buffer for the BSTR used will be provided by system.

So lets say if the hypothetical unmanaged P/Invoke call is

//A very good usage scenario is using IErrorInfo::GetDescription to get a BSTR

// and sending it back to the managed code

__declspec(dllexport)

void bstrtest(BSTR *x)

{

      *x = SysAllocString(L"Something");

}

 

Then corresponding managed call should be

[DllImport("emptydll.dll")]

        extern static void bstrtest(ref IntPtr dummy);

static void Main(string[] args)

{

      IntPtr dummy = IntPtr.Zero;

      bstrtest(ref dummy); //get the bstr

      String something = Marshal.PtrToStringBSTR(dummy); //convert it to the managed string

      Console.WriteLine(something); //test we have got it

      // now free the unmanaged BSTR. Do not forget this or there will be leaks.

      Marshal.FreeBSTR(dummy);

}

 

How to marshal a structure (with reference-type fields) by reference?

Consider the following code

 

[StructLayout(LayoutKind.Sequential)]

public struct TestStruct

{

       [MarshalAs(UnmanagedType.I4)]

       public int dumyInt;

       [MarshalAs(UnmanagedType.BStr)]

       public string dummyString;

}

public static void Main()

{

       TestStruct dummyTestStruct = new TestStruct();

       dummyTestStruct.dumyInt = 10;

       dummyTestStruct.dummyString = "stringTestStruct";

       //get the size of the structure

       int testStructSize = Marshal.SizeOf(dummyTestStruct);

       //to pass the TestStruct as a reference we need IntPtr

       IntPtr testStructRef = Marshal.AllocCoTaskMem(testStructSize);

       //finally marshal the structure

       Marshal.StructureToPtr(dummyTestStruct, testStructRef, false);

       // now use TestStuctRef for an unmanaged function call

       // free the allocated Memory 

       Marshal.FreeCoTaskMem(testStructRef);

}

We just leaked the memory for the BSTR inside TestStruct. Remember the BSTR field inside the structure is a reference type.

When marshaling dummyTestStruct using Marshal.StructureToPtr the Runtime called SysAllocString to allocate the memory for BSTR and the reference TestStruct.dummyString points to that BSTR. In the above code Marshal.DestroyStructure should have been called which will appropriately call SysFreeString. So following is the correct code (snippet)

Marshal.StructureToPtr(dummyTestStruct, testStructRef, false);

// now use TestStuctRef for an unmanaged function call

// free the allocated Memory 

Marshal.DestroyStructure(testStructRef, dummyTestStruct.GetType());

Marshal.FreeCoTaskMem(testStructRef);

Key take away points:

1) The Marshal class has many useful functions for manipulating BSTR's http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemRuntimeInteropServicesMarshalClassTopic.asp

2) It is very important to mark BSTR with MarshalAsAttribute so that Runtime specifically knows how the application wants this string to be marshaled. Forgetting this will result in heap corruption with similar call stack as shown below.

00 0811e7f4 7c85079b ntdll!DbgBreakPoint

01 0811e804 7c8720c6 ntdll!RtlpPageHeapStop+0x72

02 0811e880 7c873305 ntdll!RtlpDphReportCorruptedBlock+0x199

03 0811e8b0 7c8734c3 ntdll!RtlpDphNormalHeapFree+0x32

04 0811e908 7c8766b9 ntdll!RtlpDebugPageHeapFree+0x146

05 0811e970 7c860386 ntdll!RtlDebugFreeHeap+0x1ed

06 0811ea48 7c81d77d ntdll!RtlFreeHeapSlowly+0x37

07 0811eb2c 776b83a6 ntdll!RtlFreeHeap+0x11a

08 0811eb40 776b84c4 ole32!CRetailMalloc_Free+0x1c

09 0811eb50 7a0afc1a ole32!CoTaskMemFree+0x13

0a 0811eb5c 79f281ff mscorwks!DefaultMarshalOverrides<WSTRMarshalerBase>::UnmarshalCLRToNativeByrefInOut+0x2a

 

2) Be careful when passing BSTR as In/Out parameter.

 

3) Avoid memory leaks by freeing the memory for the reference type fields in a structure.

Posted by Varun Sekhri | 1 Comments
 
Page view tracker