Link to home
Start Free TrialLog in
Avatar of Clearly
Clearly

asked on

.Net Interop Problem with LPArray : COM Client throws Exception

I need to know how to properly marshal the data from my legacy app to a .Net component. The integrity of the data must be kept. No memory leaks must occur in the solution. The solution should never fault. My legacy app uses the following IDL interface definition:
 
interface IMyInterface2 : IUnknown
{
 HRESULT doit( [in, size_is(msgLength)]UCHAR* msg, [in] ULONG msgLength );
};
 
When my legacy COM Client calls my .Net component that implements this interface, an access violation (0xC0000005) exception is thrown. I've tried everything and can not figure out why the exception is thrown in
the COM Client.
 
Here is the COM Client:
 
#include "stdafx.h"
#include "IMyInterface2_h.h"
#include "IMyInterface2_i.c"
 
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = S_OK;
hr = ::CoInitialize( NULL );
CLSID rcid = { 0x5F850215, 0x7AC2, 0x48b8, { 0xA8, 0xCF, 0xA0, 0x08, 0xF7, 0x33, 0xD6, 0x61 } };
 
{
CComPtr<IMyInterface2> iMyInterface2;
hr = iMyInterface2.CoCreateInstance( rcid, NULL, CLSCTX_INPROC_SERVER
);
UCHAR* pBytes = (UCHAR*)::CoTaskMemAlloc( 20 );
::ZeroMemory( pBytes, 20 );
iMyInterface2->doit( pBytes, 20 );
}
::CoUninitialize();
 
return 0;
}
 
The exception is thrown on the statement:
iMyInterface2->doit( pBytes, 20 );
The call never gets to the .Net component's method
 
And here is the .Net service component. The .Net component must be copied in the same directory as the COM client.
 
using System;
using System.Runtime.InteropServices;
 
namespace ComSrv
{
 [GuidAttribute("5F850215-7AC2-48b8-A8CF-A008F733D661")]
 public class MyClass : IMyInterface2
 {
  public void doit( byte[] msg, ulong msgLength )
  {
  }
 }
 
 [ComImport]
 [GuidAttribute("B55AFAEB-BC7D-4ca9-8065-F9A45FE378B6")]
 [InterfaceTypeAttribute  
ComInterfaceType.InterfaceIsIUnknown)]
 public interface IMyInterface2
 {
  void doit(
   [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex=1, ArraySubType=UnmanagedType.U1 )]byte[] msg, [In]ulong msgLength );
 }
}

Avatar of Michel Sakr
Michel Sakr
Flag of Canada image

does the com client calls your .net application using a user context that has enough permissions to do it? try to run the client as an administrator..
...It that .Net applications is pointing to a different memory processes..
Avatar of Clearly
Clearly

ASKER

THey are both running under the exact same context
Avatar of Clearly

ASKER

THey are both running under the exact same context
Isn't as easy as it sounds. This article is pretty good, just read through it:

http://www.execpc.com/~gopalan/dotnet/classic_com/com.net_interop.html
You have posted this question twice. You can delete the other question in this topic area.

CJ
Avatar of Clearly

ASKER

I've posted twice because 500 is the limit and I want to award 1000.
Aight. Checked the link?
Avatar of Clearly

ASKER

Let me be clear that I am not having a problem in general with .Net/COM interop. In fact, I've got many other interfaces working. The problem I am having is with this particular example which uses LPArray.
0xC0000005 as you have seen is an access violation, usually thrown when trying to access an invalid or NULL pointer.
Could ypu check if iMyInterface2 poiner is NULL after CoCreateInstance. There is a very chance that this interface never got created. I have tried to replicate your problem and I am getting the same problem. Only reason that COM interface was never created.


Have you tried using #import direcive with the TLB created from your managed .Net service component. I am sure that you must have doine it, but could you check if you REGASM the managed asembly so that gets registered with COM.

I have made some modification your code and after that i did not get any acces violation.
Code for managed service component..

using System;
using System.Runtime.InteropServices;

namespace ComSrv
{
     [GuidAttribute("B55AFAEB-BC7D-4ca9-8065-F9A45FE378B6")]
     [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
     public interface IMyInterface2
     {
          void doit(
               [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex=1, ArraySubType=UnmanagedType.U1 )]byte[] msg,
               [In]ulong msgLength );
     }

     /// <summary>
     /// Summary description for Class1.
     /// </summary>
     ///
     [GuidAttribute("5F850215-7AC2-48b8-A8CF-A008F733D661")]
     public class MyClass : IMyInterface2
     {
          public MyClass()
          {
               //
               // TODO: Add constructor logic here
               //
          }

          public void doit( byte[] msg, ulong msgLength )
          {
          }

     }
}
1.I compiled it...
2.used REGASM on this assembly to register with COM
3. Then i used TLBEXP to create TLB file
4. Copied TLB file to client folder.
5. Copied assembly to testclient folder

Code for client application...You will notice that i have commented out some lines from your code. And replaced #include directives with #import directive

// TestApp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
//#include "TestIdl_h.h"
//#include "TestIdl_i.c"
#import "MyNetService.tlb" raw_interfaces_only
using namespace MyNetService;


int _tmain(int argc, _TCHAR* argv[])
{
     HRESULT hr = S_OK;
     hr = ::CoInitialize( NULL );
     //CLSID rcid = { 0x5F850215, 0x7AC2, 0x48b8, { 0xA8, 0xCF, 0xA0, 0x08, 0xF7, 0x33, 0xD6, 0x61 } };

     {
          //CComPtr<IMyInterface2> iMyInterface2;
          //hr = iMyInterface2.CoCreateInstance( IID_IMyInterface2, NULL, CLSCTX_INPROC_SERVER
          //     );

          IMyInterface2Ptr ob(__uuidof(MyClass));
          UCHAR* pBytes = (UCHAR*)::CoTaskMemAlloc( 20 );
          ::ZeroMemory( pBytes, 20 );
          ob->doit( pBytes, 20 );
     }
     ::CoUninitialize();

     return 0;

}
Then I ran this test app.... I did not get any exceptions thrown at me.
But with your original code, I did get access violation when I called doIt method.

Naveen
Avatar of Clearly

ASKER

As I stated in my original post, I am attempting to load the .Net component into a legacy application. I do not have the luxury of modifying the legacy apllication. Using the #import would require changing my legacy apllication. I was able to reduce the interface problem to my legacy application in the example I posted. Thus, figuring out how to solve the problem in my example application should solve my legacy application problem. The solution I need is some change in the .Net component.
Avatar of Clearly

ASKER

BTW, the memory returned from CoTaskMemAlloc is non NULL and the CoCreateInstance returns S_OK.
Do you have luxury of debugging through the legacy application or not? ?Because that can make things little simple if you have because right now you are not even sure of the COM object got created or not.
Can you verify if the object was created at all? Access violation has to come from NULL pointer. Most likely its your COM object pointer.
If you could really verify that interface is getting created and is not NULL, it will really help because it will eliminate one cause of this trouble.
Avatar of Clearly

ASKER

Verified that the interface is created. Before the call to CoCreateInstance, the value of iMyInterface2 is 0x00000000. After the call to CoCreateInstance, the value of iMyInterface2 is 0x0111002c.
Avatar of Clearly

ASKER

BTW, I've actually got 19,000 additional points that I am happy to reward if in fact I am satisfied with the solution and the solution comes quickly (24 hours).
Avatar of Clearly

ASKER

Here is more detail of the error emssage:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
Avatar of Clearly

ASKER

Here is the ILDasm signature for the .Net IMyInterface2:

instance void  doit([in] int32[]  marshal( unsigned int8[ + 1]) msg, [in] unsigned int64 msgLength) cil managed
Avatar of Clearly

ASKER

Scratch that...the IL is:

instance void  doit([in] unsigned int8[]  marshal(unsigned int8[ + 1]) msg,[in] unsigned int64 msgLength) cil managed
Clearly,
Now I am also getting the same "Run time Check Failure" errors. I have done some reasearch on this. COuple of other people have run into this issue too. But the problem turned out to be wrong GUIDs used for CoCreateInstance. It seems that this is not the problem in your case. Could you check into this?

Naveen
Avatar of Clearly

ASKER

What should I check? The code is in the original post.

The COM Client instantiates:

CLSID rcid = { 0x5F850215, 0x7AC2, 0x48b8, { 0xA8, 0xCF, 0xA0, 0x08, 0xF7, 0x33, 0xD6, 0x61 } };

and the GuidAttribute set for my .Net component is:

[GuidAttribute("5F850215-7AC2-48b8-A8CF-A008F733D661")]
public class MyClass : IMyInterface2

What could be going wrong here?
something similar was known in vb6 where I had to recompile the client component to conform to the server component..
http://dotnet.oreilly.com/news/complus_0801.html
but in C..
I don't quite understand

>>[ComImport]
>>[GuidAttribute("B55AFAEB-BC7D-4ca9-8065-F9A45FE378B6")]
>>[InterfaceTypeAttribute  
>>ComInterfaceType.InterfaceIsIUnknown)]
>>public interface IMyInterface2
>>{
>> void doit(
>>  [In, MarshalAs( UnmanagedType.LPArray, >>SizeParamIndex=1, ArraySubType=UnmanagedType.U1 )]byte[] >>msg,
>>[In]ulong msgLength );
>>}
>>}

Since UnmanagedType.U1 is known type by .NET. Do you really need to pass msgLength?

ie [In, MarshalAs( UnmanagedType.LPArray, >>SizeParamIndex=0, ArraySubType=UnmanagedType.U1 )]byte[] >>msg);
Avatar of Clearly

ASKER

EDDYKT :
How does .Net know how long the byte array is? It is not a string and does not have a NULL terminator. It is literally a chunk of memory.
Avatar of Clearly

ASKER

The question has been answered by someone else outside of experts-exchange. I will not reward anyone points for an answer.
COuld you please share the answer? This may benefit others.
Avatar of Clearly

ASKER

The problem was with the ulong marshalled parameter. The correct signature is listed below.

void doit( [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex=1, ArraySubType=UnmanagedType.U1 )]byte[] msg, [In]uint msgLength );

The change was from :
    [In]ulong msgLength
    to
    [In]uint msgLength
Have you been helped here, or is more needed?  This question is still open today.
":0) Asta
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

-  PAQ and points removed


Please leave any comments here within the next seven days.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER !

Zlatin Zlatev
_____________
P.S. @Clearly, I do not suggest delete for the question because there are comments from experts that may be useful to other EE members with similar problem. You will loose your question points assigned in this question, unless you provide a starting point or a solution for the question within 7 days.

In that case my suggestion will be to redeem the points to you (PAQ and points not removed)
ASKER CERTIFIED SOLUTION
Avatar of modulo
modulo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial