none
Exception System.ArgumentException in dynamic object. RRS feed

  • Question

  • Hello,

    I have the following code:

    class Program
        {
            static string strProgId = "MyAutomation.Document";
            static dynamic pserver = null;
            static void Main(string[] args)
            {
                try
                {
                    Type tPserver = Type.GetTypeFromProgID(strProgId);
                    if (tPserver != null)
                    {
                        pserver = Activator.CreateInstance(tPserver);
                    }
                    pserver.About(null, 0);
                    pserver.OpenDataFile(IntPtr.Zero, true, "Test.dat");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                if (pserver != null)
                {
                    pserver.FileExit();
                }
            }
        }
    


    It creates dynamic object for DCOM automation server and calls two methods on it. The first call (About) works fine. The second one (OpenDataFile) throws exception (System.ArgumentException) with message: "Could not convert argument 0 for call to OpenDataFile." Note that argument 0 in both calls is the same, but for some reason the first one works and the second does not. The exception is thrown before method is called. I tried all sorts of casting without success. Looks like some sort of bug in the dynamic class wrapper. I have attached exception details and the method definition from ODL file.

    Exception details:

    -		e	{"Could not convert argument 0 for call to OpenDataFile."}	System.Exception {System.ArgumentException}
    +		Data	{System.Collections.ListDictionaryInternal}	System.Collections.IDictionary {System.Collections.ListDictionaryInternal}
    		HResult	0x80070057	int
    		HelpLink	null	string
    +		InnerException	null	System.Exception
    		Message	"Could not convert argument 0 for call to OpenDataFile."	string
    		ParamName	null	string
    		Source	"System.Dynamic"	string
    		StackTrace	"   at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)\r\n   at CallSite.Target(Closure , CallSite , ComObject , IntPtr , Boolean , String )\r\n   at System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)\r\n   at CallSite.Target(Closure , CallSite , Object , IntPtr , Boolean , String )\r\n   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid4[T0,T1,T2,T3](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)\r\n   at PurifyCOM.Program.Main(String[] args) in C:\\vlh\\Test\\MS.NET\\PurifyCOM\\Program.cs:line 23"	string
    +		TargetSite	{Void CheckThrowException(Int32, System.Dynamic.ExcepInfo ByRef, UInt32, System.String)}	System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}
    +		Static members		
    +		Non-Public members		
    

    Method definition from ODL file:

    [id(35)] boolean OpenDataFile(long hWnd, boolean bEmbed, BSTR* bsPfyFilePath );

    Wednesday, February 20, 2019 8:00 PM

Answers

  • After debugging the COM Invoke code, I found out that the types were different because "byref" flag was present in the input BSTR argument. After I changed the call like that:

    string strFile = "Test.dat";
    pserver.OpenDataFile(IntPtr.Zero, true, ref strFile);

    everything started working.

    I was not able to figure out why static reference doesn't work though.

    Thank you for your help.

    Victor.

    • Marked as answer by VictorLH Wednesday, March 6, 2019 7:13 PM
    Tuesday, March 5, 2019 5:24 PM

All replies

  • The actual call you posted does not line up with the ODL you say it is using. In the ODL it is using long which maps to a 32-bit int. IntPtr is going to be a pointer of either 4 or 8 bytes depending upon how your app is compiled. The equivalent of that long would be an int in C#, not an IntPtr.

    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, February 20, 2019 10:31 PM
    Moderator
  • I tried using all possible integer types for this argument: int, long, short, Int32, Int64 with the same result. In the example above you just see the last iteration, where I tried IntPtr. No matter what type I supply in the first argument for this function, it fails. For example:

                    long h = 0;
                    bResult = pserver.OpenDataFile(h, true, "Test.dat");
    
    Throws exactly the same exception.

    Thursday, February 21, 2019 12:44 AM
  • Hi VictorLH,

    Thank you for posting here.

    For your question, I test the code, but I do not reproduce the same error with yours.

    Could you provide the files for me to test? You could upload on OneDrive for me to download.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, February 21, 2019 2:36 AM
    Moderator
  • If possible, did you try to remove the second and the third arguments of the function and perfom various experiments?

    Also try ‘BSTR’ instead of ‘BSTR*’.

    Thursday, February 21, 2019 8:03 AM
  • Go to definition on the method and post the C# signature for it. As Viorel mentioned, the third parameter is listed as a BSTR* which generally means a modifiable string. For that you'd be using a StringBuilder and not a string. But if your code is compiling then the ODL you posted isn't lining up with what code was generated for it.

    Michael Taylor http://www.michaeltaylorp3.net

    Thursday, February 21, 2019 2:40 PM
    Moderator
  • Hi Wendy,

    Thank you for your response. To reproduce this problem, you will have to download and install PurifyPlus. If you are OK with that, I will send downloading instructions for the eval version. The interface in question is part of the PurifyPlus automation interface.

    Thursday, February 21, 2019 6:06 PM
  • Hi Michael,

    Thank you for your response. Following your instructions, I tried this:

                    long h = 0;
                    StringBuilder strb = new StringBuilder("Test.dat");
                    bResult = pserver.OpenDataFile(h, true, strb);
    

    Got the same exception.

    I also tried ref strb with the same result.

    I will try to build a more isolated test case that doesn't require our automation interface and see if I can reproduce it there.

    Thursday, February 21, 2019 6:25 PM
  • Hi Michael,
    Following our conversation I tried to put together a simple COM interface defined like that:

    [
    	object,
    	uuid(1ae3d77d-94e4-40c3-a1bd-f5941efd79d4),
    	dual,
    	nonextensible,
    	pointer_default(unique)
    ]
    interface ITargetObject : IDispatch
    {
    	HRESULT NoArgs();
    	HRESULT OneArg(long larg);
    	HRESULT TwoArgs(long larg, BSTR* pbstr);
    };
    

    I call it dynamically from the following code:

    namespace DynamicInvoke
    {
        class Program
        {
            static dynamic InvokeTarget;
    
            static void Main(string[] args)
            {
                try
                {
                    Type tInvokeTarget = Type.GetTypeFromCLSID(new Guid("d9d4f285-3b50-4fa6-9ed9-796795b78583"));
                    InvokeTarget = Activator.CreateInstance(tInvokeTarget);
                    InvokeTarget.NoArgs();
                    InvokeTarget.OneArg(0);
                    InvokeTarget.TwoArgs(1, "Test");
                }
                catch(Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }
    
    Note that the second argument in TwoArgs method is BSTR* and I am passing immutable string in the call. Everything works fine and I can see the "Test" string marshalled to the native interface implementation. At the moment I have no idea why similar call works in my isolated implementation, but fails when I use it on our automation server. Is there any way to figure it out?

    Best regards,

    Victor.


    Thursday, February 21, 2019 11:24 PM
  • Go to definition on the OpenDataFile call in Visual Studio. It should jump you to the method declaration. Please post that here.

    Michael Taylor http://www.michaeltaylorp3.net

    Friday, February 22, 2019 1:40 AM
    Moderator
  • This is the definition from .odl file:

    [id(35)] boolean OpenDataFile(long hWnd, boolean bEmbed, BSTR* bsPfyFilePath );


    This is the definition from .h file:

    afx_msg BOOL OpenDataFile( long hWnd, BOOL bEmbed, BSTR FAR* bsPfyFilePath );

    This is the definition from the DISPATCH_MAP

    DISP_FUNCTION(CPurifyComDoc, "OpenDataFile", OpenDataFile, VT_BOOL, VTS_I4 VTS_BOOL VTS_PBSTR)

    This is how it looks in the .cpp file:

            BOOL CPurifyComDoc::OpenDataFile( long hWnd, BOOL bEmbed, BSTR FAR* bsPfyFilePath )

    And finally, this is how it looks in the object browser after I TlbImp the TLB:

            AutomationClass.OpenDataFile(int, bool, string)

    Friday, February 22, 2019 7:22 PM
  • Is there a reason why you need to use dynamic instead of importing the COM object directly? I'm wondering if the issue is in how the DLR identifies members and is having an issue converting your call to the equivalent COM call.

    Michael Taylor http://www.michaeltaylorp3.net

    Friday, February 22, 2019 7:32 PM
    Moderator
  • Hi Michael,

    At the moment I am prototyping, so I am using a single automation interface. In real project I will have to talk to 6 automation interfaces (3 shells for x86 and x64 processors). This will require importing 3 TLBs and implementing some sort of aggregation to be able to drive all those shells from a single outer interface. Using dynamic invoke in this situation would simplify the implementation. Besides, when I try to import COM object from the appropriate TLB, I am running into other issues.

    Friday, February 22, 2019 10:05 PM
  • Hi VictorLH,

    >>Besides, when I try to import COM object from the appropriate TLB, I am running into other issues.

    What is the problem?

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, February 26, 2019 5:36 AM
    Moderator
  • Hi Wendy,

    There are multiple problems with this TLB:

    1. If I try to import the TLB directly into project references using "Add reference/Browse", I am getting the following Error message: "A reference to <file name> could not be added. Make sure that the file is accessible, and that it is a valid assembly or COM component."

    2. If I use TlbImp, then the appropriate assembly does get created without errors, but C# code fails to resolve interfaces from this assembly. For example:

    IAutomation pserver = null;

    Type tPserver = Type.GetTypeFromProgID(strProgId); if (tPserver != null) { object o = Activator.CreateInstance(tPserver); pserver = (IAutomation)o; }

    This code throws the following exception:

    {"Unable to cast COM object of type 'System.__ComObject' to interface type 'PurifyAutomationNameSpace.IAutomation'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{39F46529-5F31-4D2A-BE55-5BB778D9997F}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."}

    When I view the imported TLB with OleView, I see the following interface definition:

        [
          uuid(39F46529-5F31-4D2A-BE55-5BB778D9997F)
        ]
        dispinterface IAutomation {
    

    In the imported assembly, the custom GUID attribute is defined like this:

      .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 33 39 46 34 36 35 32 39 2D 35 46 33 31   // ..$39F46529-5F31
                                                                                                      2D 34 44 32 41 2D 42 45 35 35 2D 35 42 42 37 37   // -4D2A-BE55-5BB77
                                                                                                      38 44 39 39 39 37 46 00 00 )                      // 8D9997F..
    

    On the surface it looks like the required interface is defined as needed, however C# code for whatever reason fails to query it.


    Thursday, February 28, 2019 6:22 PM
  • "however C# code for whatever reason fails to query it."

    COM casts don't work the same as type casts. When you cast a COM object to another type that triggers a call to QueryInterface which is defined on the COM class itself. That class then looks at the requested interface and returns back an object that implements that interface. If it can't then it fails the call. That is what you're seeing here. If the type (in C#) wasn't valid then it wouldn't have compiled. The issue is that you're making the call to the COM object and it is says it doesn't support the interface you're requesting. Assuming you're referencing the correct interface on the correct COM object then the problem lies in the COM object, not C#/.NET.

    So I would recommend you go back to the strProgId value. This UUID would need to be defined in the registry for a COM class that implements the interface you're querying. My gut instinct, if everything else was working, is that the ProgID is wrong. If you're using a version-agnostic version then note that it'll define the version-specific one. You need to make sure these things line up.

    The fact that VS couldn't load the COM object indicates a more fundamental issue to me. There are certainly some COM interfaces that cannot be converted but tlbimp is what VS would run so I wonder about bitness. Is the COM object registered as an x86 or x64 component? Is your app compiling for x86 or x64?


    Michael Taylor http://www.michaeltaylorp3.net

    Thursday, February 28, 2019 6:38 PM
    Moderator
  • I know. This is why I indicated in my post that for whatever reason C# code fails to query the required interface. The COM server in this case is a DCOM executable implemented as outofproc server. I double-checked that the registration is correct. If it was not, the call to Activator.CreateInstance would fail. In my case this is not happening. CreateInstance call works as expected, I can see that it launches the appropriate server and creates an object for this server. It is the (IAutomation)o that fails. Moreover, when I use dynamic object, most calls to this server work, but some throw invalid argument exception. The server in question has both 32 and 64 bit implementations. The C# code is compiled for "Any Platform". I was also thinking about bitness, so I tried x86 C# code calling x86 server. It failed to query interface the same way.

    Thank you,

    Victor.

    Thursday, February 28, 2019 7:02 PM
  • Hi VictorLH,

    The error is not caused by the C#, it would be better to check your COM.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, March 4, 2019 8:17 AM
    Moderator
  • After debugging the COM Invoke code, I found out that the types were different because "byref" flag was present in the input BSTR argument. After I changed the call like that:

    string strFile = "Test.dat";
    pserver.OpenDataFile(IntPtr.Zero, true, ref strFile);

    everything started working.

    I was not able to figure out why static reference doesn't work though.

    Thank you for your help.

    Victor.

    • Marked as answer by VictorLH Wednesday, March 6, 2019 7:13 PM
    Tuesday, March 5, 2019 5:24 PM