ClassInterfaceType.AutoDual vs. ClassInterfaceType.AutoDispatch

  • Thread starter Thread starter Matthew Wieder
  • Start date Start date
M

Matthew Wieder

I have:
"public class B : A"
where A is an interface in a different assembly. When I precede the
class with [ClassInterface(ClassInterfaceType.AutoDual)] then there are
several methods that B implements that cannot be called from COM (once
the assembly is registered for COM, etc.). Those methods show up in the
VBA intellisense, but when I actually try to run code which calls them,
I get a "Compile Error: Function or interface marked as restricted, or
the function uses an Automation type not supported in Visual Basic."
Looking at the tlb, I see that many of the methods that cannot be called
either take or return a uint, which is transformed in the tlb as an
"unsigned long." I'm guessing that the VBA compiler is choking on that
type. Back in C#, if I change the ClassInterface to be AutoDispatch
(late binding) then everything works perfectly from VBA. However, our
requirements are that early-binding/intellisense must work from VBA, so
that won't do. I cannot change the interface A either. I was playing
with MarshalAs to see if I could tell it to use uint as something else,
but that either resulted in the method where I put the tag not showing
up at all in the tlb, or it showing up still as an unsigned long. How
can I solve this issue?
thanks!
 
Matthew,

Unsigned longs are not handled by automation. In that case, I would
recommend that you have the method/property return an int, and not a uint.

Also, instead of using AutoDual, I would have the class expose no
interface, rather, implementing the COM interface that is defined
separately.

Hope this helps.
 
Thanks for the response. The requirement is that the intellisense must
work, hence we need the AutoDual. Is there some way to resolve this
without changing the interface ("A")? i.e. can I have B Marshal the
return to that call as an int even though it really is a uint?
thanks!
 
Matthew,

I think that you misunderstand a little bit about what AutoDual does.
If you declare an interface, and then cast the class to that interface,
IntelliSense will work just fine. This is actually the preferred way to
program for COM. COM is an interface-based system. VB6 tries to obfuscate
this and feign OO functionality by creating an association between the
interface(s) that a class implements and the class itself, exposing the
members of the interface as members of the class. This is known as the
class interface.

For more information on the subject and why you shouldn't use class
interfaces, check out the section of the .NET framework titled "Introducing
the Class Interface", located at (watch for line wrap):

http://msdn.microsoft.com/library/d...guide/html/cpconintroducingclassinterface.asp

Also, for your problem with the uint, I believe that you could apply the
MarshalAs attribute to the members of the interface that expose uints,
setting the type to UnmanagedType.I4.
 
I've read the documentation. The reason we're not doing as you
suggested is that B, which implements A, also has some other methods
which are not in the interface A and hence casting it to A would cripple
what B does. I also previously tried using the MarshalAs to I4, but in
that case, whatever function I apply that to isn't included in the tlb
at all. The tlbexp is chocking on it for some reason.
thanks.
 
Matthew,

If VB still doesn't like it, then you will have to change the definition
so that it returns an int, not a UINT.
 
It's not that VBA doesn't like it, it's that tlbexp doesn't like it - it
leaves any function where I try to MarshalAs a uint as an I4 out of the tlb.
I'm messing around with implementing a CustomMarshaler which, I think,
would solve this problem (if I can have the uint marshaled as an int).
I have recently started a new thread asking for help with the
ICustomMarshaler if you can help.
thanks!
 
Matthew,

Instead of going that route, can you post the code for the interface, so
that we may compile it and run TLBEXP against it to see what it produces?
 
This should be what you need:
C# Code:
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ClassB : InterfaceA
{
public string MemberName(string dimensionName, uint memberIndex)
{...}
}

Produces from the tlb (using ITypeLib Viewer):
[id(0x60020008)]
BSTR MemberName(
[in] BSTR dimensionName,
[in] unsigned long memberIndex);

And

public string MemberName(string dimensionName,
[MarshalAs(UnmanagedType.I4)]uint memberIndex)
{...}

results in that method being totally absent from the tlb.
 
Matthew,

Running in verbose mode (with the /verbose switch), TLBEXP outputs:

Type library exporter warning processing
'ClassLibrary6.ClassB.MemberName(memberIndex), ClassLibrary6'. Warning: The
method or field has an invalid ELEMENT_TYPE/NATIVE_TYPE combination.

Is there no way that you can define the parameter as an int? You can
convert to an unsigned int in code.

Also, I don't believe the unsigned types are CLS compliant either, so
this is another reason to use an int.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Matthew Wieder said:
This should be what you need:
C# Code:
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ClassB : InterfaceA
{
public string MemberName(string dimensionName, uint memberIndex)
{...}
}

Produces from the tlb (using ITypeLib Viewer):
[id(0x60020008)]
BSTR MemberName(
[in] BSTR dimensionName,
[in] unsigned long memberIndex);

And

public string MemberName(string dimensionName,
[MarshalAs(UnmanagedType.I4)]uint memberIndex)
{...}

results in that method being totally absent from the tlb.



Matthew,

Instead of going that route, can you post the code for the interface, so
that we may compile it and run TLBEXP against it to see what it produces?
 
The issue is that classB implements interfaceA which is already public
and used in production. To change the functions to take an int would
mean to chnage the interface which would mean all clients would have to
change, etc. That's why I'm looking into the MarshalAs route. If you
have another suggestion, I'm open.
thanks.
 
Hi Matthew,

I can't think of a workaround from .NET, how about modifying the tlb file I
mentioned in reply to you another post "ICustomMarshaler"?


Best regards,

Ying-Shen Yu [MSFT]
Microsoft community Support
Get Secure! - www.microsoft.com/security

This posting is provided "AS IS" with no warranties and confers no rights.
This mail should not be replied directly, please remove the word "online"
before sending mail.
 
Back
Top