none
Marshalling Variant containing SafeArray of Variants RRS feed

  • Question

  • Hi all,

    I have an automation COM inproc object that I want to import in a C# project. The COM object has two regular interfaces and one outgoing interface for the client to receive data in a regular interval. I have used this COM object in Delphi, JScript, VBScript and never had any problems.

    Now I tried to write a C# client by adding a reference using the registered type library.
    The problem is that the usage of the COM object seems to result in memory corruption. For example the content of a text box is cleared when the outgoing interface is called and the data passed to this interface sometimes contain binary garbage.

    I suspect that something is wrong with the marshalling of the outgoing interface that is implemented by the generated interop assembly.

    The following code shows part of the IDL for the outgoing interface that the C# client is supposed to implement:

     dispinterface IRealtimeAdapterEvents
      {
        methods:
        ....
        HRESULT OnData([in] long ID, [in] VARIANT Data );
        ...    

      };

    The <Data> parameter is a Variant that contains a SafeArray of Variants. From what I can see in the Interop assembly this parameter is marshalled as MarshalAs(UnmanagedType.Struct)] and the corresponding event handler in C# looks like this:

    void RTA_OnData(int ID, object Data);

    Do you have any idea what could be wrong with this marshalling that explains the memory corruption?

    thanks in advance,
    Nicolas

    Friday, October 12, 2012 11:19 PM

Answers

  • Hello nicolasr75,

    1. [MarshalAs(UnmanagedType.Struct)] is correct. This indicates that the corresponding "Data" parameter on the unmanaged side is a VARIANT.

    2. I tried out a test COM server with the same event and implemented a client in C#.

    3. I could receive the event with the "Data" parameter containing an array of objects.

    4. In my test COM server I supplied an array of VARIANTs of VT_I4s :

    5. My C# client code could receive each VARIANT (marshaled across as a managed Object type) :

    void RTA_OnData(int ID, object Data)
    {
        if (Data.GetType() == typeof(object[]))
        {
            for (int i = 0; i < ((object[])Data).Length; i++)
            {
                MessageBox.Show(string.Format("{0:D}", (int)((object[])Data)[i]));
            }
        }
    }

    6. My COM server code (written in ATL) is as follows :

    STDMETHODIMP CRealtimeAdapter::StartAction(void)
    {
    	// TODO: Add your implementation code here	
    	VARIANT varArray[10];
    	
    	for (int i = 0; i < 10; i++)
    	{
    		VariantInit(&(varArray[i]));
    		V_VT(&(varArray[i])) = VT_I4;
    		V_I4(&(varArray[i])) = i;
    	}
    	
    	SAFEARRAY* pSafeArrayOfVariant = NULL;
    	
    	// Helper function to generate a SAFEARRAY of VARIANTs.
    	CreateSafeArray<VARIANT, VT_VARIANT>(varArray, 10, &pSafeArrayOfVariant);
    	
    	VARIANT varData;
    	
    	VariantInit(&varData);
    	
    	V_VT(&varData) = (VT_ARRAY | VT_VARIANT);
    	V_ARRAY(&varData) = pSafeArrayOfVariant;
    	
    	Fire_OnData(100, varData);
    	
    	VariantClear(&varData);
    
    	return S_OK;
    }
    

    7. In your code, what does each VARIANT (in the SAFEARRAY) contain ?

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Proposed as answer by Mike FengModerator Monday, October 15, 2012 10:37 AM
    • Marked as answer by nicolasr75 Tuesday, October 16, 2012 11:07 AM
    Saturday, October 13, 2012 3:59 AM

All replies

  • Hello nicolasr75,

    1. [MarshalAs(UnmanagedType.Struct)] is correct. This indicates that the corresponding "Data" parameter on the unmanaged side is a VARIANT.

    2. I tried out a test COM server with the same event and implemented a client in C#.

    3. I could receive the event with the "Data" parameter containing an array of objects.

    4. In my test COM server I supplied an array of VARIANTs of VT_I4s :

    5. My C# client code could receive each VARIANT (marshaled across as a managed Object type) :

    void RTA_OnData(int ID, object Data)
    {
        if (Data.GetType() == typeof(object[]))
        {
            for (int i = 0; i < ((object[])Data).Length; i++)
            {
                MessageBox.Show(string.Format("{0:D}", (int)((object[])Data)[i]));
            }
        }
    }

    6. My COM server code (written in ATL) is as follows :

    STDMETHODIMP CRealtimeAdapter::StartAction(void)
    {
    	// TODO: Add your implementation code here	
    	VARIANT varArray[10];
    	
    	for (int i = 0; i < 10; i++)
    	{
    		VariantInit(&(varArray[i]));
    		V_VT(&(varArray[i])) = VT_I4;
    		V_I4(&(varArray[i])) = i;
    	}
    	
    	SAFEARRAY* pSafeArrayOfVariant = NULL;
    	
    	// Helper function to generate a SAFEARRAY of VARIANTs.
    	CreateSafeArray<VARIANT, VT_VARIANT>(varArray, 10, &pSafeArrayOfVariant);
    	
    	VARIANT varData;
    	
    	VariantInit(&varData);
    	
    	V_VT(&varData) = (VT_ARRAY | VT_VARIANT);
    	V_ARRAY(&varData) = pSafeArrayOfVariant;
    	
    	Fire_OnData(100, varData);
    	
    	VariantClear(&varData);
    
    	return S_OK;
    }
    

    7. In your code, what does each VARIANT (in the SAFEARRAY) contain ?

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Proposed as answer by Mike FengModerator Monday, October 15, 2012 10:37 AM
    • Marked as answer by nicolasr75 Tuesday, October 16, 2012 11:07 AM
    Saturday, October 13, 2012 3:59 AM
  • Thanks for all the information and your code example!

    It did unfortunately not solve my problem but it nevertheless led me to the conclusion that the SafeArray parameter is not the problem! So, thanks for that, I consider it to be an answer to my question and keep searching for other causes of the memory corruption.

    Nicolas

    Tuesday, October 16, 2012 11:07 AM
  • Hello nicolasr75,

    Do the VARIANTs in your SafeArray of VARIANTs contain BSTRs by any chance ?

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/


    • Edited by Lim Bio Liong Tuesday, October 16, 2012 3:29 PM Corrected typo error.
    Tuesday, October 16, 2012 3:28 PM
  • Hello Bio,

    > Do the VARIANTs in your SafeArray of VARIANTs contain BSTRs by any chance ?

    they could, but currently I am only testing with integers? Is anything special about BSTRs?

    By the way, the problem I described disappeared after I installed Visual Studio Express 2012!!! I did not port the project to 2012, but somehow the installation of VSE2012 influenced the current installation (VSE 2010). Out of the sudden all the strange errors disappeared!

    So, next time I experience somthing this strange, I probably should first try it on another developer machine...

    Nick

    Sunday, October 21, 2012 5:17 PM
  • Hello nicolasr75,

    >> Is anything special about BSTRs?

    1. Yes, BSTRs need to be allocated properly (using SysAllocString()).

    2. Many developers misunderstand the nature of a BSTR and perform allocation like the following :

    BSTR bstr = L"My BSTR";

    The above code does not allocate a BSTR. Such coding may lead to problems when marshaling BSTRs to C#.

    3. For more information please refer to :

    The Importance of Proper BSTR Allocation.

    http://limbioliong.wordpress.com/2011/08/13/the-importance-of-proper-bstr-allocation/

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Monday, October 22, 2012 2:31 AM
  • Thanks Bio, I know about the requirements on the unmanaged side. But as far as I understand, on the managed side, I simply need to care about "string", hopefully without any pitfalls.

    Nick

    Monday, October 22, 2012 8:13 PM