.NET Forum / Languages / C# / March 2008
C# app with COM Interface for array of COM structs
|
|
Thread rating:  |
Sharon - 11 Mar 2008 17:04 GMT Hello Experts
I'm exporting COM interface from within my C# application (VS2005 .NET 2.0). One of my exported function GetROI() return type is a struct defined in a native COM object. The method signature is (viewed by the OLE View): [id(0x60020001)] HRESULT GetROI( [in] long partID, [in] BSTR roiName, [out, retval] SAFEARRAY(PhysicalLocation_t)* pRetVal);
In the native COM object, the struct is defined as (viewed by the OLE View): typedef struct tagPhysicalLocation_t { long ScanLine; long Pixel; long Value; } PhysicalLocation_t;
In the interop of the native COM object, the struct is defined as:
namespace USDALib { public struct PhysicalLocation_t { public int Pixel; public int ScanLine; public int Value; } }
This struct is also shown on my C# application by using the OLE View as:
typedef [uuid(7B61D653-82C0-33E9-94AA-913FE5290BC0), version(1.0), custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, USDALib.PhysicalLocation_t)] struct tagPhysicalLocation_t { long ScanLine; long Pixel; long Value; } PhysicalLocation_t;
But at the native unmanaged application that invoke this my COM method GetROI() is crushing with exception when trying to assign the return value to any kind of variable is end with exception.
It looks like it's impossible to transfer array of structs from my interface to the native application.
Can I do that and how can do I that?
-------- Thanks Sharon
Willy Denoyette [MVP] - 11 Mar 2008 17:21 GMT > Hello Experts > [quoted text clipped - 43 lines] > to > any kind of variable is end with exception. What do you mean with any kind of variable? The return type is an array (SAFEARRAY) of strucures, the structures being of type PhysicalLocation_t. This means that you have to declare a variable of type SAFEARRAY at the client side.
> It looks like it's impossible to transfer array of structs from my > interface > to the native application. Sure it's possible, but not all kind of native COM clients can handle arrays of user defined types (UDT's), so it's better not to expose such types to COM.
Willy.
Sharon - 11 Mar 2008 17:46 GMT My Client is VB Visual Studio 6 and I have a reference to COM Interface from C# application. The method signature is (viewed by the OLE View):
> [id(0x60020001)] > HRESULT GetROI( > [in] long partID, > [in] BSTR roiName, > [out, retval] SAFEARRAY(PhysicalLocation_t)* pRetVal); I'm trying to call GetROI and I'm getting exeption with "Element not found" message. The code in VB looks like that:
Dim PhLocationPoints() As PhysicalLocation_t Dim Iface as TheCOClass
Set Iface = new TheCOClass If Not Iface Is Nothing Then PhLocationPoints = Iface.GetROI(119, "ROI 3") end if
 Signature Thanks Sharon
Willy Denoyette [MVP] - 11 Mar 2008 19:23 GMT > My Client is VB Visual Studio 6 and I have a reference to COM Interface > from [quoted text clipped - 18 lines] > PhLocationPoints = Iface.GetROI(119, "ROI 3") > end if Hard to tell without seeing any C# code, are you sure you are returning a valid array when passed (119,"ROI 3")?
Willy.
Sharon - 12 Mar 2008 16:15 GMT Ok, Here is the relevant code.
The method at hand is USDALib.PhysicalLocation_t[] GetROI(int partID, string roiName);
The USDALib is the USDALib.dll interop for the COM object USDA.dll
The VB6 code is referencing the USDA.ll (the native COM object) that defines the PhysicalLocation_t and also referencing the 1000 Gates
/////////////////////////////////// /// File 1000GatesInterface.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { [ComVisible(true), Guid(GUIDs.IID_I1000Gates), InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface I1000Gates { // The USDALib is the USDALib.dll interop for the COM object USDA.dll USDALib.PhysicalLocation_t[] GetROI(int partID, string roiName); } }
/////////////////////////////////// /// File COMBridge.cs
using System; using System.Drawing; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { /// <summary>COMBridgeFor1000Gates is the bridge between the application /// interfaces and the actual implementation of the methods.</summary> [Guid(GUIDs.CLASS_1000Gates)] [ClassInterface(ClassInterfaceType.None)] [ProgId("MultiBScan.1000Gates")] [ComVisible(true)] public class COMBridgeFor1000Gates : ReferenceCountedObjectBase, I1000Gates { public static EvaluationForm m_evalForm;
[DllImport("msvcr70.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int _controlfp(int n, int mask);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process);
[DllImport("kernel32.dll", CharSet=CharSet.Ansi, ExactSpelling=true)] public static extern UIntPtr GetProcAddress(IntPtr hModule, string procName);
public COMBridgeFor1000Gates() { // README: according to Microsoft knowledge base! UIntPtr pIsWow64Process = UIntPtr.Zero; bool Is64 = false;
// Query for IsWow64Process function, if doesn't exist then not a 64bit system. pIsWow64Process = GetProcAddress(System.Diagnostics.Process.GetCurrentProcess().Handle,"IsWow64Process"); if( pIsWow64Process != UIntPtr.Zero ) { // Function exists, check it for values. IsWow64Process(System.Diagnostics.Process.GetCurrentProcess().Handle,out Is64); } // Perform if not a 64bit system. if( !Is64 ) { // this flag should be set in 32bit system in order to avoid an exception in the drawing. _controlfp(0x9001F, 0xFFFFF); } }
~COMBridgeFor1000Gates() { m_evalForm.Close(); }
#region I1000Gates Members
USDALib.PhysicalLocation_t[] I1000Gates.GetROI(int partID, string roiName) { return new USDALib.PhysicalLocation_t[0];
USDALib.PhysicalLocation_t[] jp = new USDALib.PhysicalLocation_t[10]; for( int i=0; i < 10; ++i ) { jp[i].Pixel = i; jp[i].ScanLine = i; jp[i].Value = i; } return jp; }
#endregion I1000Gates Members } #region Factory Class for COM object
class BridgeClassFactoryFor1000Gates : ClassFactoryBase { public override void virtual_CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject) { if( riid == Marshal.GenerateGuidForType(typeof(I1000Gates)) || riid == Program.IID_IDispatch || riid == Program.IID_IUnknown ) { COMBridgeFor1000Gates COMBridge_New = new COMBridgeFor1000Gates();
ppvObject = Marshal.GetComInterfaceForObject(COMBridge_New, typeof(I1000Gates)); EvaluationConfiguration.ExecutionMode = EvaluationConfiguration.RunMode.ActiveX; } else { throw new COMException("No interface", unchecked((int) 0x80004002)); } } }
#endregion Factory Class for COM object }
////////////////////////////////////////// //// File IClassFactory.cs
using System; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { ///<summary> /// Interface IClassFactory is here to provide a C# /// definition of the COM IClassFactory interface. ///</summary> [ComImport, // This interface originated from COM. InterfaceType(ComInterfaceType.InterfaceIsIUnknown),// Indicate that this interface is not IDispatch-based. Guid("00000001-0000-0000-C000-000000000046")]// This GUID is the actual GUID of IClassFactory. public interface IClassFactory { /// <summary>CreateInstance.</summary> /// <param name="pUnkOuter">Unknown outer.</param> /// <param name="riid">RI ID.</param> /// <param name="ppvObject">PPV Object.</param> void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
/// <param name="fLock">Lock flag.</param> void LockServer(bool fLock); } }
////////////////////////////////////////// //// File GUIDs.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { public class GUIDs { internal const string CLASS_1000Gates = "1B4363BF-33DF-49c7-B0D0-A201E631C100"; internal const string IID_I1000Gates = "BE8E6044-5B18-4d56-AA75-9E5120F27884"; } }
//////////////////////////////////////////////////////
 Signature Thanks Sharon
Sharon - 12 Mar 2008 16:17 GMT Ok, Here is the relevant code.
The method at hand is USDALib.PhysicalLocation_t[] GetROI(int partID, string roiName);
The USDALib is the USDALib.dll interop for the COM object USDA.dll
The VB6 code is referencing the USDA.ll (the native COM object) that defines the PhysicalLocation_t and also referencing the 1000 Gates
/////////////////////////////////// /// File 1000GatesInterface.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { [ComVisible(true), Guid(GUIDs.IID_I1000Gates), InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface I1000Gates { // The USDALib is the USDALib.dll interop for the COM object USDA.dll USDALib.PhysicalLocation_t[] GetROI(int partID, string roiName); } }
/////////////////////////////////// /// File COMBridge.cs
using System; using System.Drawing; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { /// <summary>COMBridgeFor1000Gates is the bridge between the application /// interfaces and the actual implementation of the methods.</summary> [Guid(GUIDs.CLASS_1000Gates)] [ClassInterface(ClassInterfaceType.None)] [ProgId("MultiBScan.1000Gates")] [ComVisible(true)] public class COMBridgeFor1000Gates : ReferenceCountedObjectBase, I1000Gates { public static EvaluationForm m_evalForm;
[DllImport("msvcr70.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int _controlfp(int n, int mask);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process);
[DllImport("kernel32.dll", CharSet=CharSet.Ansi, ExactSpelling=true)] public static extern UIntPtr GetProcAddress(IntPtr hModule, string procName);
public COMBridgeFor1000Gates() { // README: according to Microsoft knowledge base! UIntPtr pIsWow64Process = UIntPtr.Zero; bool Is64 = false;
// Query for IsWow64Process function, if doesn't exist then not a 64bit system. pIsWow64Process = GetProcAddress(System.Diagnostics.Process.GetCurrentProcess().Handle,"IsWow64Process"); if( pIsWow64Process != UIntPtr.Zero ) { // Function exists, check it for values. IsWow64Process(System.Diagnostics.Process.GetCurrentProcess().Handle,out Is64); } // Perform if not a 64bit system. if( !Is64 ) { // this flag should be set in 32bit system in order to avoid an exception in the drawing. _controlfp(0x9001F, 0xFFFFF); } }
~COMBridgeFor1000Gates() { m_evalForm.Close(); }
#region I1000Gates Members
USDALib.PhysicalLocation_t[] I1000Gates.GetROI(int partID, string roiName) { return new USDALib.PhysicalLocation_t[0];
USDALib.PhysicalLocation_t[] jp = new USDALib.PhysicalLocation_t[10]; for( int i=0; i < 10; ++i ) { jp[i].Pixel = i; jp[i].ScanLine = i; jp[i].Value = i; } return jp; }
#endregion I1000Gates Members } #region Factory Class for COM object
class BridgeClassFactoryFor1000Gates : ClassFactoryBase { public override void virtual_CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject) { if( riid == Marshal.GenerateGuidForType(typeof(I1000Gates)) || riid == Program.IID_IDispatch || riid == Program.IID_IUnknown ) { COMBridgeFor1000Gates COMBridge_New = new COMBridgeFor1000Gates();
ppvObject = Marshal.GetComInterfaceForObject(COMBridge_New, typeof(I1000Gates)); EvaluationConfiguration.ExecutionMode = EvaluationConfiguration.RunMode.ActiveX; } else { throw new COMException("No interface", unchecked((int) 0x80004002)); } } }
#endregion Factory Class for COM object }
////////////////////////////////////////// //// File IClassFactory.cs
using System; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { ///<summary> /// Interface IClassFactory is here to provide a C# /// definition of the COM IClassFactory interface. ///</summary> [ComImport, // This interface originated from COM. InterfaceType(ComInterfaceType.InterfaceIsIUnknown),// Indicate that this interface is not IDispatch-based. Guid("00000001-0000-0000-C000-000000000046")]// This GUID is the actual GUID of IClassFactory. public interface IClassFactory { /// <summary>CreateInstance.</summary> /// <param name="pUnkOuter">Unknown outer.</param> /// <param name="riid">RI ID.</param> /// <param name="ppvObject">PPV Object.</param> void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
/// <param name="fLock">Lock flag.</param> void LockServer(bool fLock); } }
////////////////////////////////////////// //// File GUIDs.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { public class GUIDs { internal const string CLASS_1000Gates = "1B4363BF-33DF-49c7-B0D0-A201E631C100"; internal const string IID_I1000Gates = "BE8E6044-5B18-4d56-AA75-9E5120F27884"; } }
//////////////////////////////////////////////////////
 Signature Thanks Sharon -- Thanks Sharon
> > My Client is VB Visual Studio 6 and I have a reference to COM Interface > > from [quoted text clipped - 23 lines] > > Willy. Willy Denoyette [MVP] - 12 Mar 2008 23:38 GMT Are you sure this is the real code? This should give you a compiler warning "Unreachable code..." ... USDALib.PhysicalLocation_t[] I1000Gates.GetROI(int partID, string roiName) { return new USDALib.PhysicalLocation_t[0]; <----- return here?????? USDALib.PhysicalLocation_t[] jp = new USDALib.PhysicalLocation_t[10];
...
and throw an "index out of bounds" runtime error when accessing the array elements.
Willy.
> Ok, Here is the relevant code. > [quoted text clipped - 220 lines] >> >> Willy. Sharon - 13 Mar 2008 15:39 GMT Oopppss, sorry, I didn't clean the original code enought. Here is is again fixed.
The method at hand is USDALib.PhysicalLocation_t[] GetROI(int partID, string roiName);
The USDALib is the USDALib.dll interop for the COM object USDA.dll
The VB6 code is referencing the USDA.ll (the native COM object) that defines the PhysicalLocation_t and also referencing the 1000 Gates
/////////////////////////////////// /// File 1000GatesInterface.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { [ComVisible(true), Guid(GUIDs.IID_I1000Gates), InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface I1000Gates { // The USDALib is the USDALib.dll interop for the native (unmanaged) COM object USDA.dll USDALib.PhysicalLocation_t[] GetROI(int partID, string roiName); } }
/////////////////////////////////// /// File COMBridge.cs
using System; using System.Drawing; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { /// <summary>COMBridgeFor1000Gates is the bridge between the application /// interfaces and the actual implementation of the methods.</summary> [Guid(GUIDs.CLASS_1000Gates)] [ClassInterface(ClassInterfaceType.None)] [ProgId("MultiBScan.1000Gates")] [ComVisible(true)] public class COMBridgeFor1000Gates : ReferenceCountedObjectBase, I1000Gates { [DllImport("msvcr70.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int _controlfp(int n, int mask);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process);
[DllImport("kernel32.dll", CharSet=CharSet.Ansi, ExactSpelling=true)] public static extern UIntPtr GetProcAddress(IntPtr hModule, string procName);
public COMBridgeFor1000Gates() { // README: according to Microsoft knowledge base! UIntPtr pIsWow64Process = UIntPtr.Zero; bool Is64 = false;
// Query for IsWow64Process function, if doesn't exist then not a 64bit system. pIsWow64Process = GetProcAddress(System.Diagnostics.Process.GetCurrentProcess().Handle,"IsWow64Process"); if( pIsWow64Process != UIntPtr.Zero ) { // Function exists, check it for values. IsWow64Process(System.Diagnostics.Process.GetCurrentProcess().Handle,out Is64); } // Perform if not a 64bit system. if( !Is64 ) { // this flag should be set in 32bit system in order to avoid an exception in the drawing. _controlfp(0x9001F, 0xFFFFF); } }
~COMBridgeFor1000Gates() {}
#region I1000Gates Members
USDALib.PhysicalLocation_t[] I1000Gates.GetROI(int partID, string roiName) { USDALib.PhysicalLocation_t[] jp = new USDALib.PhysicalLocation_t[10]; for( int i=0; i < 10; ++i ) { jp[i].Pixel = i; jp[i].ScanLine = i; jp[i].Value = i; } return jp; }
#endregion I1000Gates Members } #region Factory Class for COM object
class BridgeClassFactoryFor1000Gates : ClassFactoryBase { public override void virtual_CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject) { if( riid == Marshal.GenerateGuidForType(typeof(I1000Gates)) || riid == Program.IID_IDispatch || riid == Program.IID_IUnknown ) { COMBridgeFor1000Gates COMBridge_New = new COMBridgeFor1000Gates();
ppvObject = Marshal.GetComInterfaceForObject(COMBridge_New, typeof(I1000Gates)); EvaluationConfiguration.ExecutionMode = EvaluationConfiguration.RunMode.ActiveX; } else { throw new COMException("No interface", unchecked((int) 0x80004002)); } } }
#endregion Factory Class for COM object }
////////////////////////////////////////// //// File IClassFactory.cs
using System; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { ///<summary> /// Interface IClassFactory is here to provide a C# /// definition of the COM IClassFactory interface. ///</summary> [ComImport, // This interface originated from COM. InterfaceType(ComInterfaceType.InterfaceIsIUnknown),// Indicate that this interface is not IDispatch-based. Guid("00000001-0000-0000-C000-000000000046")]// This GUID is the actual GUID of IClassFactory. public interface IClassFactory { /// <summary>CreateInstance.</summary> /// <param name="pUnkOuter">Unknown outer.</param> /// <param name="riid">RI ID.</param> /// <param name="ppvObject">PPV Object.</param> void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
/// <param name="fLock">Lock flag.</param> void LockServer(bool fLock); } }
////////////////////////////////////////// //// File GUIDs.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices;
namespace MultiBScanDisplay { public class GUIDs { internal const string CLASS_1000Gates = "1B4363BF-33DF-49c7-B0D0-A201E631C100"; internal const string IID_I1000Gates = "BE8E6044-5B18-4d56-AA75-9E5120F27884"; } }
//////////////////////////////////////////////////////
Why the BV6 client is getting an exception? If I define a struct of my own in the C# application exposoed to COM, then it works perfectly. But I can not do that beacuse of design architecture issue.
Any idea?
 Signature Thanks Sharon
Willy Denoyette [MVP] - 13 Mar 2008 19:12 GMT > Oopppss, sorry, I didn't clean the original code enought. > Here is is again fixed. [quoted text clipped - 194 lines] > > Any idea? This should work, my guess is that your C# code throws an exception. In order to isolate the issue, you should build a minimal repro sample, something like this will do....
1) a VB6 AX class project "vbudt"
Type MyStruct i1 As Long i2 As Long End Type
Compile above and use tlbimp to create an interop assembly. tlbimp vbudt.dll /out:vbudt.dll
2) A C# library // File: minimal.cs // Compile and register .... // csc /r:vbudt.dll /t:library minimal.cs // regasm /tlb /codebase minimal.dll
using System; using System.Runtime.InteropServices; using vbudt; // import vbudt metadata
namespace ComIntSample1 { [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [Guid("ea361991-f123-11dc-95a4-001d9233a61f")] public interface IMyClass { MyStruct[] UDTArray { get; } }
[ComDefaultInterface(typeof(IMyClass))] [ClassInterface(ClassInterfaceType.AutoDual)] [Guid("ea361992-f123-11dc-95a4-001d9233a61f")] public class MyClass : IMyClass { public MyStruct[] UDTArray { get { MyStruct[] myArr = new MyStruct[2]; myArr[0].i1 = 1; myArr[0].i2 = 2; myArr[1].i1 = 3; myArr[1].i2 = 4; return myArr; } } } }
3) A VB6 client with references to 1 and 2
... Dim obj As New MyClass Dim ar() As MyStruct ar = obj.UDTArray For n = LBound(ar) To UBound(ar) Debug.Print (ar(n).i1) Debug.Print (ar(n).i2) Next n
Willy.
Sharon - 16 Mar 2008 16:56 GMT Thanks Willy, Your sample is working nicely.
After registering your .NET tlb – In the OLE View under Structs is empty.
But in my application the OLE View shows the struct I’m returning with my function. This struct is defined in the COM object DLL (native/Unmanaged).
This is the only difference I have notices between your sample and my application.
I have tried to exclude this struct when importing the .NET interface to the VB client - still it does not work.
In any case; the VB client is getting the exception “Element not found”.
I have encircled the C# function in try-catch to see whether any exception is thrown – but none was caught.
What can be the problem?
------- Thanks Sharon
Sharon - 16 Mar 2008 17:21 GMT One more difference I have noticed between your sample and my application:
In your so simple sample, the executable (yes, I made is exe) path is registered while in my executable is not, and I should register is manually.
How come?
--------- Thanks Sharon
Willy Denoyette [MVP] - 16 Mar 2008 17:57 GMT > One more difference I have noticed between your sample and my application: > [quoted text clipped - 3 lines] > > How come? I'm not entirely clear on what you mean by this, but I guess you are talking about the path of the .NET assembly registered for COM interop, you need to register the assembly with the "/codebase" option, this is not done when registering for COM interop from VS. If you don't register using regasm /codebase, you'll have to sign and register your assembly in the GAC.
Willy.
Willy Denoyette [MVP] - 16 Mar 2008 17:50 GMT > Thanks Willy, > Your sample is working nicely. > > After registering your .NET tlb – In the OLE View under Structs is empty. The .NET tlb does not contain a definition of the structure as this one is defined in the native COM DLL, so, the .NET typelib should not contain a definition either. The .NET library imports the typelib from the native COM dll (see the importlib directives in oleview output), and this is how COM gets at the structure definition.
> But in my application the OLE View shows the struct I’m returning with my > function. This struct is defined in the COM object DLL (native/Unmanaged). Not sure what you are talking about, "my" .NET tlb does contain this:
[id(0x60020004), propget] HRESULT UDTArray([out, retval] SAFEARRAY(MyStruct)* pRetVal);
note that I'm using a property that returns a structure array. The MyStruct being defined in the COM dll and imported via the "importlib" directive.
> This is the only difference I have notices between your sample and my > application. [quoted text clipped - 13 lines] > Thanks > Sharon Sharon - 16 Mar 2008 21:32 GMT Thanks again Willy,
>> In your so simple sample, the executable (yes, I made is exe) path is >> registered while in my executable is not, and I should register is >> manually.
> I'm not entirely clear on what you mean by this, but I guess you are talking > about the path of the .NET assembly registered for COM interop, you need to > register the assembly with the "/codebase" option, this is not done when > registering for COM interop from VS. > If you don't register using regasm /codebase, you'll have to sign and > register your assembly in the GAC. Your guess is correct. and after trying to register it with /codebase the executable does get register as well. (Because of the codebase usage warning I didn't use it earlier).
> The .NET tlb does not contain a definition of the structure as this one is > defined in the native COM DLL, so, the .NET typelib should not contain a > definition either. > The .NET library imports the typelib from the native COM dll (see the > importlib directives in oleview output), and this is how COM gets at the > structure definition. I agree that my tlb should not contain a definition of the structure. But the OLE View shows like it does. Yet, in your sample the structure does not shown on the OLE View for the tlb. Why? How?
> Not sure what you are talking about, "my" .NET tlb does contain this: I mean that for my C# application that produce the tlb by the regasm, the OLE View shows the struct (PhysicalLocation_t) returned by the COM viewable function. But this struct (PhysicalLocation_t) is defined in the COM object DLL (native/Unmanaged). I'm guessing that it (PhysicalLocation_t) should not be seen using the OLE View like it's defined in the tlb. It's shown for my TLB using the OLE View under the Structs folder like that:
typedef [uuid(B6EBEAA1-31B1-3EFF-A395-2871DC2BC11F), version(1.0), custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, USDALib.PhysicalLocation_t) ] struct tagPhysicalLocation_t { long ScanLine; long Pixel; long Value; } PhysicalLocation_t;
When the USDALib is the interop for the USDA.dll native COM object that contains the PhysicalLocation_t defenition
And the method for my tlb looks like that:
[id(0x60020001)] SAFEARRAY(PhysicalLocation_t) GetROI( [in] long partID, [in] BSTR roiName);
> note that I'm using a property that returns a structure array. The MyStruct > being defined in the COM dll and imported via the "importlib" directive. I'm not sure what do you mean. I have added the COM object DLL as a reference COM DLL, and the VS as generated an interop for it which I'm using in my project. How can I do that otherwise like you are referring by "importlib" directive?
 Signature Thanks Sharon
Willy Denoyette [MVP] - 17 Mar 2008 00:06 GMT > Thanks again Willy, > [quoted text clipped - 25 lines] > I agree that my tlb should not contain a definition of the structure. But > the OLE View shows like it does. It should not. This structure is defined in the native code DLL.
> Yet, in your sample the structure does not shown on the OLE View for the > tlb. > Why? How? Because the structure is not defined in the C# code.
>> Not sure what you are talking about, "my" .NET tlb does contain this: > [quoted text clipped - 40 lines] > How can I do that otherwise like you are referring by "importlib" > directive? When you set a reference to your native COM DLL in your C# project, then the typelib generated by regasm should contain a reference to this DLL's typelib in the form of a "importlib" directive.
Suppose you have a C# module named "csharp.dll" and a native COM dll named "myCOM.dll", then you should find the following in the tlb produced by "regasm /tlb /codebase csharp.dll"
library csharp { // TLib : // TLib : : {GUID of imported typelib} importlib("myCOM.dll"); // other imports here after...
...
importlib is the directive that instructs COM to include typelib info from "myCOM" in the current typelib. Note that I'm building the sample from the command line, it's possible that VS embeds the referenced typelib in the current typelib when registering the managed assembly for COM interop. Anyways, this is not the source of your problem.
Willy.
Sharon - 17 Mar 2008 09:21 GMT I have found something I can not explain.
I took your sample that works, and added a reference to the COM dll named USDA.dll which is the COM object I'm using in my real project. Then I added to methods to the C# code to be exposed to COM, so now it looks like that:
using System; using System.Runtime.InteropServices; using MyTest; // import vbudt metadata using USDALib;
namespace ComIntSample1 { [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [Guid("ea361991-f123-11dc-95a4-001d9233a61f")] [ComVisible(true)] public interface INetClass { MyType_t[] UDTArray { get; }
PhysicalLocation_t SinglePhysicalLocation { get; }
PhysicalLocation_t[] PhysicalLocationArray { get; } }
[ComDefaultInterface(typeof(INetClass))] [ClassInterface(ClassInterfaceType.AutoDual)] [Guid("ea361992-f123-11dc-95a4-001d9233a61f")] [ComVisible(true)] public class NetClass : INetClass { public MyType_t[] UDTArray { get { MyType_t[] myArr = new MyType_t[2]; myArr[0].Param1 = 1; myArr[0].Param2 = 2; myArr[0].Param3 = 3; myArr[1].Param1 = 4; myArr[1].Param2 = 5; myArr[1].Param3 = 6; return myArr; } }
public PhysicalLocation_t SinglePhysicalLocation { get { PhysicalLocation_t myArr; myArr.ScanLine = 1; myArr.Pixel = 2; myArr.Value = 3; return myArr; } }
public PhysicalLocation_t[] PhysicalLocationArray { get { PhysicalLocation_t[] myArr = new PhysicalLocation_t[2]; myArr[0].ScanLine = 1; myArr[0].Pixel = 2; myArr[0].Value = 3; myArr[1].ScanLine = 4; myArr[1].Pixel = 5; myArr[1].Value = 6; return myArr; } } } }
And now the PhysicalLocation_t struct which is defined in the USDA.dll (the native COM dll), is shown on the OLE View as if it is defined in the tlb generated from my C# code. Why???
And in the VB6 client I have added some code, so now it looks like that:
Private Sub Command1_Click()
Dim theClass As netClass Dim theStruct() As MyType_t Set theClass = New netClass theStruct = theClass.UDTArray ' OK! End Sub
Private Sub Command2_Click() Dim pl As PhysicalLocation_t Dim plArr() As PhysicalLocation_t Dim netClass As INetClass Set netClass = New netClass pl = netClass.SinglePhysicalLocation ' FAIL !!! plArr = netClass.PhysicalLocationArray ' FAIL !!! End Sub
The call to netClass.SinglePhysicalLocation and to netClass.PhysicalLocationArray fail with error: "Run-time error '-2147319765 (8002802b) Method 'SinglePhysicalLocation/PhysicalLocationArray' of object 'INetClass' failed."
It looks like the problem lies in the USDA.dll native com object or at its interop. What can be the problem?
 Signature Thanks Sharon
Willy Denoyette [MVP] - 17 Mar 2008 11:45 GMT >I have found something I can not explain. > [quoted text clipped - 114 lines] > interop. > What can be the problem? I don't get it, your typelib should *not* contain the definition of the structure defined in another typelib. What importlib's do you see in your C# module's typelib?
I mean stuff like this: ... importlib("stdole2.tlb");
Also, what tool has been used to build USDA.DLL? My guess is C++ and ATL, what version of C++ and ATL and what version of Windows are you running?
The 8002802b HRESULT is an indication that you may have a versioning issue, I would suggest you to un-register and re-register all components used in this project (native COM and the C# assembly). Also make sure that these are only registered once, you can check this with oleview. Willy.
Sharon - 17 Mar 2008 13:21 GMT > I don't get it, your typelib should *not* contain the definition of the > structure defined in another typelib. [quoted text clipped - 3 lines] > .... > importlib("stdole2.tlb"); I don't see any importlib, not in the sample that works, not in sample that doesn't work and not in the read project. In all cases I simply added the native COM dll as a reference and the VS has added an interop for me automatically.
> Also, what tool has been used to build USDA.DLL? My guess is C++ and ATL, Your guess is correct, it's C++ and ATL, it was build using the VC 6 with the latest service pack.
> what version of C++ and ATL and what version of Windows are you running? The version of C++ and ATL is the version used in the VC 6 with the latest service pack. The USDA.dll was built with VC 6 on Windws2000. The VB 6 client was also built on Windows2000. The C# application (that export the COM interface) was build with VS2005+SP1 on WinXP SP2.
> The 8002802b HRESULT is an indication that you may have a versioning issue, > I would suggest you to un-register and re-register all components used in > this project (native COM and the C# assembly). > Also make sure that these are only registered once, you can check this with > oleview. I also suspected that, so I made sure of that, still the same.
Any idea will be highly appreciated.
 Signature Thanks Sharon
Willy Denoyette [MVP] - 17 Mar 2008 13:54 GMT >> I don't get it, your typelib should *not* contain the definition of the >> structure defined in another typelib. [quoted text clipped - 10 lines] > has > added an interop for me automatically. You must be looking in the wrong typelib! The typelib you need to look at is the resultant typelib from the regasm.exe command. regasm.exe /tlb /codebase csharp.dlll, generates a typelib (.tlb) file, this is the typelib I'm talking about and that's also the typelib you need to reference in your VB6 client project.
Willy.
Sharon - 18 Mar 2008 09:22 GMT Actually I'm not looking directly to the tlb file. In the VB client I'm simply marking name that matches the tlb file (which is the same name as the C# exe file but with the tlb extension instead of the exe) in the References list.
Also when using the oleview to look for the C# COM exposure - I see that the struct like it's defined in there in spite the fact that this struct is defined in the native COM DLL.
My guess that something is wrong in the native COM dll USDA.dll that whenever I'm referencing it in the C# project its struct get like defined in the C# tlb. It does not happen in the activeX test we made.
Maybe there a way to generate the interop (using the TlbImp.exe) for the USDA.dll so its struct will nor get defined in the C# tlb?
 Signature Thanks Sharon
Willy Denoyette [MVP] - 18 Mar 2008 11:15 GMT > Actually I'm not looking directly to the tlb file. > In the VB client I'm simply marking name that matches the tlb file (which [quoted text clipped - 15 lines] > Maybe there a way to generate the interop (using the TlbImp.exe) for the > USDA.dll so its struct will nor get defined in the C# tlb? I keep saying that using oleview on your C# tlb file should include importlib directives. these directives are needed to unclude the typelib definitions from other typelibs needed.
So oleview on your C# tlb should look like this:
// TLib : // TLib : : {8F0A80A1-1351-42CA-9B18-CDB349102B29} importlib("USDA.tlb"); // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D} importlib("mscorlib.tlb"); // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole2.tlb");
here, stdole2.tlb is imported because it's needed by COM itself, this tlb is imported in every COM typelib, mscorlib.tlb import is needed for managed/native COM interop, and finally your native COM tlb (or dll) must be imported to get at the definition of the structure.
If you don't see these importlib's, then or, there is something wrong, or, you are using an old and buggy OLEVIEW. I would suggest you try using oleview from VS2008 (see common7\tools\bin) or from the latest platform sdk.
Willy.
Sharon - 18 Mar 2008 12:57 GMT I'm using the oleview version 2.1.0.60 of the VS2005.
I do see the importlib directives, but the importlib for the usda.dll is missing. Here is the importlib directive at the sample project that does work for the MyTest.dll native COM dll (the activX test we made) but does not work for the USDA.dll native com dll (the problematic dll):
// TLib : // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D} importlib("mscorlib.tlb"); // TLib : : {EA3AA151-7E8A-4BD8-B96F-54EAC73BE40A} importlib("MyTest.dll"); // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole2.tlb");
I guess this is the reason, why the definition of the struct is at the C# tlb. Is there a way to prevent that?
P.S.: I do think you can use the sample project. You don't need Win200, you can use the native COM DLL's and to reference them from within the VS2005. This way you will be able to see everything that should and does not work.
----- Thanks Sharon
Sharon G. - 18 Mar 2008 15:18 GMT Due ro the suspicion we had about the USDA.dll (the native COM dll). I have compiled it on the VS2005 on WinXP+SP2, and the results are the same.
PECULIAR...
------- Thaks Sharon
Willy Denoyette [MVP] - 18 Mar 2008 18:21 GMT Due ro the suspicion we had about the USDA.dll (the native COM dll). I have compiled it on the VS2005 on WinXP+SP2, and the results are the same.
PECULIAR...
------- Thaks Sharon
I would't directly assume something is wrong with this USDA.dll, IMO there is something wrong with the way you are importing the USDA typelib.
Make sure that: - the USDA.dll is registered (regsvr32.exe) and check it's apartment requirements, it should be "apartment" or "both". - no older versions of both, the native component and the csharp assembly (or exe) remain registered, before you try this in sequence.
1. tlbimp usda.tlb /out:usda.interop.dll 2. set a reference to usda.interop.dll when compiling the C# code, for instance using csc /r:usda.interop.dll xxxxxxx.cs form the command-line. 3. run regasm /tlb /codebase xxxxxxx.dll 4. in VB6, reset the references to xxxxxx.tlb and usda.tlb and rebuild build the VB6 client Run your client and inspect the xxxxx.tlb using oleview.
Willy.
Sharon - 20 Mar 2008 10:09 GMT I did all that but still the same.
In the registry I found the USDA is registered as:
[HKEY_CLASSES_ROOT\CLSID\{45C5732A-9C24-11D3-9EEE-0090278B204C}] @="USDAObject Class"
[HKEY_CLASSES_ROOT\CLSID\{45C5732A-9C24-11D3-9EEE-0090278B204C}\InprocServer32] @="D:\\Sharon's\\Rubbish\\COM-.NET Struct\\COM Objects\\USDA.dll" "ThreadingModel"="Single"
[HKEY_CLASSES_ROOT\CLSID\{45C5732A-9C24-11D3-9EEE-0090278B204C}\ProgID] @="USDA.USDAObject.1"
[HKEY_CLASSES_ROOT\CLSID\{45C5732A-9C24-11D3-9EEE-0090278B204C}\Programmable]
[HKEY_CLASSES_ROOT\CLSID\{45C5732A-9C24-11D3-9EEE-0090278B204C}\TypeLib] @="{45C5731B-9C24-11D3-9EEE-0090278B204C}"
[HKEY_CLASSES_ROOT\CLSID\{45C5732A-9C24-11D3-9EEE-0090278B204C}\VersionIndependentProgID] @="USDA.USDAObject"
So I have changed the ThreadingModel to be Apartment, and tried it all again as you said, still it didn't make any difference.
> I would't directly assume something is wrong with this USDA.dll, IMO there is > something wrong with the way you are importing the USDA typelib. I used the VS2005 to import the USDA.dll COM object. Also tried importing it with: TlbImp.exe" "USDA.dll" /verbose /sysarray /out:usda.interop.dll
> Make sure that: > - the USDA.dll is registered (regsvr32.exe) and check it's apartment requirements, > it should be "apartment" or "both". > - no older versions of both, the native component and the csharp assembly (or exe) > remain registered, before you try this in sequence. Was Single, so I changed its registry value to Apartment.
> 1. tlbimp usda.tlb /out:usda.interop.dll > 2. set a reference to usda.interop.dll when compiling the C# code, for instance using > csc /r:usda.interop.dll xxxxxxx.cs > form the command-line. I have used the VS2005 rebuild feature.
> 3. run regasm /tlb /codebase xxxxxxx.dll It's an exe so I did: regasm.exe COMdotNET.exe /tlb /codebase COMdotNET.tlb
> 4. in VB6, reset the references to xxxxxx.tlb and usda.tlb and rebuild build the VB6 client > Run your client and inspect the xxxxx.tlb using oleview. Checked it the oleview - still the struct is defined in the COMdotNET although it's originally defined in the USDA.dll native COM.
 Signature Thanks Sharon
Willy Denoyette [MVP] - 20 Mar 2008 13:05 GMT >I did all that but still the same. > [quoted text clipped - 39 lines] > > Was Single, so I changed its registry value to Apartment. You are fooling COM into believing it's an apartment object, but you are fooling yourself, you need to adapt the ATL project settings.
>> 1. tlbimp usda.tlb /out:usda.interop.dll >> 2. set a reference to usda.interop.dll when compiling the C# code, for [quoted text clipped - 15 lines] > Checked it the oleview - still the struct is defined in the COMdotNET > although it's originally defined in the USDA.dll native COM. Sorry, but I can't help you further with this, as I said before, I can't use the USDA.dll from the project you sent by mail. This dll depends on an older atl and C runtime version, that means I can't register the DLL (regsvr32.exe). Also, don't waste your time with the tlb stuff, your problem is due to something else.
Willy.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|