none
slow performance of c# code using an old active X control RRS feed

  • Question

  • I would like to speed up some code that calls into an old active X control many times. 400K calls takes about 20 seconds when calling from C# into the control whereas in a separate test app, unmanaged MFC/C++ into the same control takes about 1-2 seconds or about 10x faster. I started to look at managed C++ but this is pretty new to me so I have some questions.

    In C# winforms app I instantiate the active X control. I would like to call into a C++ DLL passing in the com control and then have the C++ code call into the control many times. Is this possible? I'm not sure how I can take a "MyControl ^" which is slow and magically convert it into a "MyControl *" which is then possibly fast because no transition is taken from managed to unmanaged. I tried use the pin_ptr or address operator but that doesn't seem to be the way to go.

    Or maybe I should be just passing an hwnd around instead of the object pointers. But, then how would stuff like MFC DDX_Control work to wire things up when there is no MFC CDialog?

    thanks,

    Paul.

    Thursday, November 11, 2010 4:09 PM

Answers

  •  

    Thanks for the help BinaryCoder. Your approach works great!

    Just in case this helps someone else, here are the steps in detail:

    1. the starting point is a C# app which uses an active X control on a form - the goal is to
        speedup the calls into the control - unfortunately the source code is not available so a new
        method cannot be easily added to solve the problem w/ minimum amount of changes

    2. so, we have a "MyControl" in c# and we can call myControl.Method1() but the overhead is high

    3. to simpify testing, I created a managed C++ app which will contains all code so I can test managed
        vs managed code - after this test looks good, I can go back and have a c# exe + c++ dll

    4. inside the c++ managed app I have a form w/ the gui control on it - here the control is "MyControl^"
        which is the same as the c# "MyControl" - no special conversion needed between C# and managed C++ 
        - both are managed controls

    5. I divided the managed c++ code into two regions - managed using "#pragma managed" and unmanaged using
        "#pragma unmanaged" - in the final code I could probably get away w/ just an unmanaged C++ dll and just
        pass the IntPtr into the c++ code shown in the next step

    6. managed code:

       System::Object^ obj = myControl->GetOcx();
       IntPtr iPtr = Marshal::GetIUnknownForObject( obj );

       UnmanagedFunc( iPtr );

       Marshal::Release( iPtr );

    7. Unmanaged code:

       UnmanagedFunc( void* voidPtr )
        
         IUnknown* iUnknown = (IUnknown*)voidPtr;
         iUnknown->QueryInterface( IDD_Dispatch, (void**)&iDisp );

         ...code to call dispatch invoke...  ( iDisp->Invoke(...) )


    So, that is basically it. The c++ code runs about 10x faster and really the only tricky part
    of the code is the Invoke which requires params to be passed in reverse. In this test I
    didn't need to pull in MFC so the the amount of c++ code and the final binary image is
    pretty small.

    Paul.
     




    • Marked as answer by eryang Tuesday, December 7, 2010 8:23 AM
    Friday, November 12, 2010 9:47 PM
  • > 400K calls takes about 20 seconds

    Whoa..  I suppose there is no way you can redesign this control to take all of this data as one chunk instead of 400K calls that you want to complete in 1-2 seconds.

    Unforunately, I don't know enough about this topic to give more detailed advice (for example, to explain what could possible throw this off by a factor of 10), but maybe someone else will.

    > I would like to call into a C++ DLL passing in the com control

    This sounds promising.  I think you can use the Control's GetOcx method (http://msdn.microsoft.com/en-us/library/system.windows.forms.axhost.getocx.aspx) to get an object on which you can use Marshal methods on.  I think http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getiunknownforobject.aspx is the one you want.  This IntPtr (actually an IUnknown* on the C++ side) should pass easily to your C++ code where you will then be able to QueryInterface it and do what you need to do.

     

    • Marked as answer by PaulDev2 Friday, November 12, 2010 9:47 PM
    Friday, November 12, 2010 2:10 AM

All replies

  • > 400K calls takes about 20 seconds

    Whoa..  I suppose there is no way you can redesign this control to take all of this data as one chunk instead of 400K calls that you want to complete in 1-2 seconds.

    Unforunately, I don't know enough about this topic to give more detailed advice (for example, to explain what could possible throw this off by a factor of 10), but maybe someone else will.

    > I would like to call into a C++ DLL passing in the com control

    This sounds promising.  I think you can use the Control's GetOcx method (http://msdn.microsoft.com/en-us/library/system.windows.forms.axhost.getocx.aspx) to get an object on which you can use Marshal methods on.  I think http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getiunknownforobject.aspx is the one you want.  This IntPtr (actually an IUnknown* on the C++ side) should pass easily to your C++ code where you will then be able to QueryInterface it and do what you need to do.

     

    • Marked as answer by PaulDev2 Friday, November 12, 2010 9:47 PM
    Friday, November 12, 2010 2:10 AM
  •  

    Thanks for the help BinaryCoder. Your approach works great!

    Just in case this helps someone else, here are the steps in detail:

    1. the starting point is a C# app which uses an active X control on a form - the goal is to
        speedup the calls into the control - unfortunately the source code is not available so a new
        method cannot be easily added to solve the problem w/ minimum amount of changes

    2. so, we have a "MyControl" in c# and we can call myControl.Method1() but the overhead is high

    3. to simpify testing, I created a managed C++ app which will contains all code so I can test managed
        vs managed code - after this test looks good, I can go back and have a c# exe + c++ dll

    4. inside the c++ managed app I have a form w/ the gui control on it - here the control is "MyControl^"
        which is the same as the c# "MyControl" - no special conversion needed between C# and managed C++ 
        - both are managed controls

    5. I divided the managed c++ code into two regions - managed using "#pragma managed" and unmanaged using
        "#pragma unmanaged" - in the final code I could probably get away w/ just an unmanaged C++ dll and just
        pass the IntPtr into the c++ code shown in the next step

    6. managed code:

       System::Object^ obj = myControl->GetOcx();
       IntPtr iPtr = Marshal::GetIUnknownForObject( obj );

       UnmanagedFunc( iPtr );

       Marshal::Release( iPtr );

    7. Unmanaged code:

       UnmanagedFunc( void* voidPtr )
        
         IUnknown* iUnknown = (IUnknown*)voidPtr;
         iUnknown->QueryInterface( IDD_Dispatch, (void**)&iDisp );

         ...code to call dispatch invoke...  ( iDisp->Invoke(...) )


    So, that is basically it. The c++ code runs about 10x faster and really the only tricky part
    of the code is the Invoke which requires params to be passed in reverse. In this test I
    didn't need to pull in MFC so the the amount of c++ code and the final binary image is
    pretty small.

    Paul.
     




    • Marked as answer by eryang Tuesday, December 7, 2010 8:23 AM
    Friday, November 12, 2010 9:47 PM
  • Glad you got it working, and thanks for contributing that good writeup on what you did for the benefit of others here.

     

    Friday, November 12, 2010 11:31 PM