Creating COM servers in .NET

F

Fredo

I've never used C# to create COM servers before and I'm not really sure what
I'm doing wrong. I read something about versioning problems when using
InterfaceIsDual. I thought I could fix this by supplying the DispIDs. I have
the following interface, for example:

[Guid("B7A755CC-809B-412f-BCFE-C47F0084DDFB")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
public interface IIdentity
{
[DispId(100)]
void Initialize(byte[] idData);
[DispId(101)]
IIdentity DistanceTo(IIdentity otherID);
[DispId(102)]
int CompareTo(IIdentity otherID);
[DispId(103)]
int RawDataLen
{
get;
}
[DispId(104)]
ulong[] RawData
{
get;
}
[DispId(105)]
int GetBit(int bit);
[DispId(106)]
void SetBit(int bit, int value);
}
}

But when I go into OLE/COM Object Viewer, I see the following for the
dispinterface:

dispinterface _Identity {
properties:
methods:
[id(00000000), propget,
custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
BSTR ToString();
[id(0x60020001)]
VARIANT_BOOL Equals([in] VARIANT obj);
[id(0x60020002)]
long GetHashCode();
[id(0x60020003)]
_Type* GetType();
[id(0x60020004)]
void Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x60020005)]
IIdentity* DistanceTo([in] IIdentity* otherID);
[id(0x60020006)]
long CompareTo([in] IIdentity* otherID);
[id(0x60020007), propget]
long RawDataLen();
[id(0x60020008), propget]
SAFEARRAY(uint64) RawData();
[id(0x60020009)]
long GetBit([in] long bit);
[id(0x6002000a)]
void SetBit(
[in] long bit,
[in] long value);
[id(0x6002000b), propget]
SAFEARRAY(unsigned char) ByteArray();
[id(0x6002000c), propget]
SAFEARRAY(uint64) _id();
[id(0x6002000c), propput]
void _id([in] SAFEARRAY(uint64) rhs);
};

Why isn't it using the IDs I'm supplying?

Thanks.
 
N

Nicholas Paldino [.NET/C# MVP]

Fredo,

I don't know if it is related, but I wouldn't use the ulong type for the
RawData property. There is no corresponding type in COM automation.
Perhaps this is interfering somehow with the generation of the type libaray.

Other than that, from the code, it should be applying the proper DISP id
to the method, so the declaration of the RawData property is the only thing
I can think of.

Do you really need an array of unsigned 64-bit values?
 
J

John Duval

I've never used C# to create COM servers before and I'm not really sure what
I'm doing wrong. I read something about versioning problems when using
InterfaceIsDual. I thought I could fix this by supplying the DispIDs. I have
the following interface, for example:

    [Guid("B7A755CC-809B-412f-BCFE-C47F0084DDFB")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
    public interface IIdentity
    {
        [DispId(100)]
        void Initialize(byte[] idData);
        [DispId(101)]
        IIdentity DistanceTo(IIdentity otherID);
        [DispId(102)]
        int CompareTo(IIdentity otherID);
        [DispId(103)]
        int RawDataLen
        {
            get;
        }
        [DispId(104)]
        ulong[] RawData
        {
            get;
        }
        [DispId(105)]
        int GetBit(int bit);
        [DispId(106)]
        void SetBit(int bit, int value);
 }

}

But when I go into OLE/COM Object Viewer, I see the following for the
dispinterface:

dispinterface _Identity {
    properties:
    methods:
        [id(00000000), propget,
          custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
        BSTR ToString();
        [id(0x60020001)]
        VARIANT_BOOL Equals([in] VARIANT obj);
        [id(0x60020002)]
        long GetHashCode();
        [id(0x60020003)]
        _Type* GetType();
        [id(0x60020004)]
        void Initialize([in] SAFEARRAY(unsigned char) idData);
        [id(0x60020005)]
        IIdentity* DistanceTo([in] IIdentity* otherID);
        [id(0x60020006)]
        long CompareTo([in] IIdentity* otherID);
        [id(0x60020007), propget]
        long RawDataLen();
        [id(0x60020008), propget]
        SAFEARRAY(uint64) RawData();
        [id(0x60020009)]
        long GetBit([in] long bit);
        [id(0x6002000a)]
        void SetBit(
                        [in] long bit,
                        [in] long value);
        [id(0x6002000b), propget]
        SAFEARRAY(unsigned char) ByteArray();
        [id(0x6002000c), propget]
        SAFEARRAY(uint64) _id();
        [id(0x6002000c), propput]
        void _id([in] SAFEARRAY(uint64) rhs);

};

Why isn't it using the IDs I'm supplying?

Thanks.

Hi Fredo,
Are you generating a TLB and then looking at the generated .TLB with
OLEVIEW? When I do that with your code, I get the correct DISPIDs
(100-106). If I comment out the DispId attributes, rebuild, and rerun
REGASM /TLB, I get the other DISPIDs (0x60020000, etc...). I don't
have my interop book handy, but I'd bet that the 0x6002+ range is used
as the default DISPID offset, and your TLB was generated before you
added the DispId attributes. If that's the case, then you just need
to re-run REGASM /TLB to pick up the new DISPIDs.

John
 
F

Fredo

Nicholas,

That was a mistake (just corrected it right before I read your post,
actually). I did intend uints and not ulongs. I'm doing some code conversion
from C++ and ulong was meant as a 32-bit uint. Thanks for noticing.

But that isn't the problem. See my reply to John for more info...

Pete

Nicholas Paldino said:
Fredo,

I don't know if it is related, but I wouldn't use the ulong type for
the RawData property. There is no corresponding type in COM automation.
Perhaps this is interfering somehow with the generation of the type
libaray.

Other than that, from the code, it should be applying the proper DISP
id to the method, so the declaration of the RawData property is the only
thing I can think of.

Do you really need an array of unsigned 64-bit values?


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

Fredo said:
I've never used C# to create COM servers before and I'm not really sure
what I'm doing wrong. I read something about versioning problems when
using InterfaceIsDual. I thought I could fix this by supplying the
DispIDs. I have the following interface, for example:

[Guid("B7A755CC-809B-412f-BCFE-C47F0084DDFB")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
public interface IIdentity
{
[DispId(100)]
void Initialize(byte[] idData);
[DispId(101)]
IIdentity DistanceTo(IIdentity otherID);
[DispId(102)]
int CompareTo(IIdentity otherID);
[DispId(103)]
int RawDataLen
{
get;
}
[DispId(104)]
ulong[] RawData
{
get;
}
[DispId(105)]
int GetBit(int bit);
[DispId(106)]
void SetBit(int bit, int value);
}
}

But when I go into OLE/COM Object Viewer, I see the following for the
dispinterface:

dispinterface _Identity {
properties:
methods:
[id(00000000), propget,
custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
BSTR ToString();
[id(0x60020001)]
VARIANT_BOOL Equals([in] VARIANT obj);
[id(0x60020002)]
long GetHashCode();
[id(0x60020003)]
_Type* GetType();
[id(0x60020004)]
void Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x60020005)]
IIdentity* DistanceTo([in] IIdentity* otherID);
[id(0x60020006)]
long CompareTo([in] IIdentity* otherID);
[id(0x60020007), propget]
long RawDataLen();
[id(0x60020008), propget]
SAFEARRAY(uint64) RawData();
[id(0x60020009)]
long GetBit([in] long bit);
[id(0x6002000a)]
void SetBit(
[in] long bit,
[in] long value);
[id(0x6002000b), propget]
SAFEARRAY(unsigned char) ByteArray();
[id(0x6002000c), propget]
SAFEARRAY(uint64) _id();
[id(0x6002000c), propput]
void _id([in] SAFEARRAY(uint64) rhs);
};

Why isn't it using the IDs I'm supplying?

Thanks.
 
F

Fredo

John,

I'm not generating anything by hand. The way I did it was to go into the
project properties and under Configuration Properties/Build, I set "Register
for COM Interop" = true. I then build. The registration is done as part of
the build. I then ran OleView and looked for my objects under All Objects
and copied and pasted what it had in there for the dispinterface.

Pete

Hi Fredo,
Are you generating a TLB and then looking at the generated .TLB with
OLEVIEW? When I do that with your code, I get the correct DISPIDs
(100-106). If I comment out the DispId attributes, rebuild, and rerun
REGASM /TLB, I get the other DISPIDs (0x60020000, etc...). I don't
have my interop book handy, but I'd bet that the 0x6002+ range is used
as the default DISPID offset, and your TLB was generated before you
added the DispId attributes. If that's the case, then you just need
to re-run REGASM /TLB to pick up the new DISPIDs.

John
 
N

Nicholas Paldino [.NET/C# MVP]

Fredo,

I would uncheck that, and also run REGASM to unregister the assembly
from the registry for COM.

Then, build your project.

Once you do that, I would then generate the type library using
TLBEXP.exe, and then view the type library in OLEVIEW and see if it is
correct.

If it is, I would then recheck the option and have it regenerate the TLB
and it should be generated correctly.
 
F

Fredo

Thanks Nicholas and John, for the information. I think maybe there's some
confusion on my side and maybe I'm not understanding what I need to do.

I was under the impression that to do a COM server, I need to create a C#
interface and then create a class that implements that interface. This is
what I have done.

Now, my C# interface is the one that has the DISPIDs defined and running
TLBExport appears to generate the proper interface. Here's what I've got:

interface IIdentity : IDispatch {
[id(0x00000064)]
HRESULT Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x00000065)]
HRESULT DistanceTo(
[in] IIdentity* otherID,
[out, retval] IIdentity** pRetVal);
[id(0x00000066)]
HRESULT CompareTo(
[in] IIdentity* otherID,
[out, retval] long* pRetVal);
[id(0x00000067), propget]
HRESULT RawDataLen([out, retval] long* pRetVal);
[id(0x00000068), propget]
HRESULT RawData([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x00000069), propget]
HRESULT ByteArray([out, retval] SAFEARRAY(unsigned char)* pRetVal);
[id(0x0000006e)]
HRESULT GetBit(
[in] long bit,
[out, retval] long* pRetVal);
[id(0x0000006f)]
HRESULT SetBit(
[in] long bit,
[in] long value);
};

Those clearly match my DISPIDs like I want.

Here's where I'm confused. I then have a class called Identity that
implements IIdentity. The class declaration begins with:


[Guid("C00C300D-AEAD-4ceb-BF80-3F8DBCF49C16")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Identity : IIdentity
{
}

And that's the extent of my COM attributes.

TLBExp is then generating this for the Identity class

interface _Identity : IDispatch {
[id(00000000), propget,
custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
HRESULT ToString([out, retval] BSTR* pRetVal);
[id(0x60020001)]
HRESULT Equals(
[in] VARIANT obj,
[out, retval] VARIANT_BOOL* pRetVal);
[id(0x60020002)]
HRESULT GetHashCode([out, retval] long* pRetVal);
[id(0x60020003)]
HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x60020004)]
HRESULT Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x60020005)]
HRESULT DistanceTo(
[in] IIdentity* otherID,
[out, retval] IIdentity** pRetVal);
[id(0x60020006)]
HRESULT CompareTo(
[in] IIdentity* otherID,
[out, retval] long* pRetVal);
[id(0x60020007), propget]
HRESULT RawDataLen([out, retval] long* pRetVal);
[id(0x60020008), propget]
HRESULT RawData([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x60020009), propget]
HRESULT ByteArray([out, retval] SAFEARRAY(unsigned char)* pRetVal);
[id(0x6002000a)]
HRESULT GetBit(
[in] long bit,
[out, retval] long* pRetVal);
[id(0x6002000b)]
HRESULT SetBit(
[in] long bit,
[in] long value);
[id(0x6002000c)]
HRESULT SetValue([in] unsigned long value);
[id(0x6002000d)]
HRESULT Add(
[in] IIdentity* value,
[out, retval] IIdentity** pRetVal);
[id(0x6002000e),
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, Add)]
HRESULT Add_2(
[in] unsigned long value,
[out, retval] IIdentity** pRetVal);
[id(0x6002000f)]
HRESULT ShiftLeft(
[in] unsigned long bits,
[out, retval] IIdentity** pRetVal);
[id(0x60020010), propget]
HRESULT _id([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x60020010), propput]
HRESULT _id([in] SAFEARRAY(unsigned long) pRetVal);
};

So I guess I have a few questions:

1: Why don't the IDs match up with the DISPIds from the IIdentity interface?

2: If it's creating a COM interface for Identity, do I really need the
IIdentity C# interface?

Sorry, I haven't done COM stuff in so long and I have no idea where my COM
books have disappeared to, so I'm working with a flakey memory...
 
N

Nicholas Paldino [.NET/C# MVP]

Fredo,

Remove the ClassInterface attribute from the class implementation.
Since you have (correctly, I might add, in terms of how to ^properly^ export
a COM type) defined an interface separately, there is no need to have the
class definition define the interface.

Remove that, and it should be just fine.

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

Fredo said:
Thanks Nicholas and John, for the information. I think maybe there's some
confusion on my side and maybe I'm not understanding what I need to do.

I was under the impression that to do a COM server, I need to create a C#
interface and then create a class that implements that interface. This is
what I have done.

Now, my C# interface is the one that has the DISPIDs defined and running
TLBExport appears to generate the proper interface. Here's what I've got:

interface IIdentity : IDispatch {
[id(0x00000064)]
HRESULT Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x00000065)]
HRESULT DistanceTo(
[in] IIdentity* otherID,
[out, retval] IIdentity** pRetVal);
[id(0x00000066)]
HRESULT CompareTo(
[in] IIdentity* otherID,
[out, retval] long* pRetVal);
[id(0x00000067), propget]
HRESULT RawDataLen([out, retval] long* pRetVal);
[id(0x00000068), propget]
HRESULT RawData([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x00000069), propget]
HRESULT ByteArray([out, retval] SAFEARRAY(unsigned char)* pRetVal);
[id(0x0000006e)]
HRESULT GetBit(
[in] long bit,
[out, retval] long* pRetVal);
[id(0x0000006f)]
HRESULT SetBit(
[in] long bit,
[in] long value);
};

Those clearly match my DISPIDs like I want.

Here's where I'm confused. I then have a class called Identity that
implements IIdentity. The class declaration begins with:


[Guid("C00C300D-AEAD-4ceb-BF80-3F8DBCF49C16")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Identity : IIdentity
{
}

And that's the extent of my COM attributes.

TLBExp is then generating this for the Identity class

interface _Identity : IDispatch {
[id(00000000), propget,
custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
HRESULT ToString([out, retval] BSTR* pRetVal);
[id(0x60020001)]
HRESULT Equals(
[in] VARIANT obj,
[out, retval] VARIANT_BOOL* pRetVal);
[id(0x60020002)]
HRESULT GetHashCode([out, retval] long* pRetVal);
[id(0x60020003)]
HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x60020004)]
HRESULT Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x60020005)]
HRESULT DistanceTo(
[in] IIdentity* otherID,
[out, retval] IIdentity** pRetVal);
[id(0x60020006)]
HRESULT CompareTo(
[in] IIdentity* otherID,
[out, retval] long* pRetVal);
[id(0x60020007), propget]
HRESULT RawDataLen([out, retval] long* pRetVal);
[id(0x60020008), propget]
HRESULT RawData([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x60020009), propget]
HRESULT ByteArray([out, retval] SAFEARRAY(unsigned char)* pRetVal);
[id(0x6002000a)]
HRESULT GetBit(
[in] long bit,
[out, retval] long* pRetVal);
[id(0x6002000b)]
HRESULT SetBit(
[in] long bit,
[in] long value);
[id(0x6002000c)]
HRESULT SetValue([in] unsigned long value);
[id(0x6002000d)]
HRESULT Add(
[in] IIdentity* value,
[out, retval] IIdentity** pRetVal);
[id(0x6002000e),
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, Add)]
HRESULT Add_2(
[in] unsigned long value,
[out, retval] IIdentity** pRetVal);
[id(0x6002000f)]
HRESULT ShiftLeft(
[in] unsigned long bits,
[out, retval] IIdentity** pRetVal);
[id(0x60020010), propget]
HRESULT _id([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x60020010), propput]
HRESULT _id([in] SAFEARRAY(unsigned long) pRetVal);
};

So I guess I have a few questions:

1: Why don't the IDs match up with the DISPIds from the IIdentity
interface?

2: If it's creating a COM interface for Identity, do I really need the
IIdentity C# interface?

Sorry, I haven't done COM stuff in so long and I have no idea where my COM
books have disappeared to, so I'm working with a flakey memory...
 
F

Fredo

Sweet, thanks Nicholas. As always, I bow to the master...


Nicholas Paldino said:
Fredo,

Remove the ClassInterface attribute from the class implementation.
Since you have (correctly, I might add, in terms of how to ^properly^
export a COM type) defined an interface separately, there is no need to
have the class definition define the interface.

Remove that, and it should be just fine.

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

Fredo said:
Thanks Nicholas and John, for the information. I think maybe there's some
confusion on my side and maybe I'm not understanding what I need to do.

I was under the impression that to do a COM server, I need to create a C#
interface and then create a class that implements that interface. This is
what I have done.

Now, my C# interface is the one that has the DISPIDs defined and running
TLBExport appears to generate the proper interface. Here's what I've got:

interface IIdentity : IDispatch {
[id(0x00000064)]
HRESULT Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x00000065)]
HRESULT DistanceTo(
[in] IIdentity* otherID,
[out, retval] IIdentity** pRetVal);
[id(0x00000066)]
HRESULT CompareTo(
[in] IIdentity* otherID,
[out, retval] long* pRetVal);
[id(0x00000067), propget]
HRESULT RawDataLen([out, retval] long* pRetVal);
[id(0x00000068), propget]
HRESULT RawData([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x00000069), propget]
HRESULT ByteArray([out, retval] SAFEARRAY(unsigned char)*
pRetVal);
[id(0x0000006e)]
HRESULT GetBit(
[in] long bit,
[out, retval] long* pRetVal);
[id(0x0000006f)]
HRESULT SetBit(
[in] long bit,
[in] long value);
};

Those clearly match my DISPIDs like I want.

Here's where I'm confused. I then have a class called Identity that
implements IIdentity. The class declaration begins with:


[Guid("C00C300D-AEAD-4ceb-BF80-3F8DBCF49C16")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Identity : IIdentity
{
}

And that's the extent of my COM attributes.

TLBExp is then generating this for the Identity class

interface _Identity : IDispatch {
[id(00000000), propget,
custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
HRESULT ToString([out, retval] BSTR* pRetVal);
[id(0x60020001)]
HRESULT Equals(
[in] VARIANT obj,
[out, retval] VARIANT_BOOL* pRetVal);
[id(0x60020002)]
HRESULT GetHashCode([out, retval] long* pRetVal);
[id(0x60020003)]
HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x60020004)]
HRESULT Initialize([in] SAFEARRAY(unsigned char) idData);
[id(0x60020005)]
HRESULT DistanceTo(
[in] IIdentity* otherID,
[out, retval] IIdentity** pRetVal);
[id(0x60020006)]
HRESULT CompareTo(
[in] IIdentity* otherID,
[out, retval] long* pRetVal);
[id(0x60020007), propget]
HRESULT RawDataLen([out, retval] long* pRetVal);
[id(0x60020008), propget]
HRESULT RawData([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x60020009), propget]
HRESULT ByteArray([out, retval] SAFEARRAY(unsigned char)*
pRetVal);
[id(0x6002000a)]
HRESULT GetBit(
[in] long bit,
[out, retval] long* pRetVal);
[id(0x6002000b)]
HRESULT SetBit(
[in] long bit,
[in] long value);
[id(0x6002000c)]
HRESULT SetValue([in] unsigned long value);
[id(0x6002000d)]
HRESULT Add(
[in] IIdentity* value,
[out, retval] IIdentity** pRetVal);
[id(0x6002000e),
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, Add)]
HRESULT Add_2(
[in] unsigned long value,
[out, retval] IIdentity** pRetVal);
[id(0x6002000f)]
HRESULT ShiftLeft(
[in] unsigned long bits,
[out, retval] IIdentity** pRetVal);
[id(0x60020010), propget]
HRESULT _id([out, retval] SAFEARRAY(unsigned long)* pRetVal);
[id(0x60020010), propput]
HRESULT _id([in] SAFEARRAY(unsigned long) pRetVal);
};

So I guess I have a few questions:

1: Why don't the IDs match up with the DISPIds from the IIdentity
interface?

2: If it's creating a COM interface for Identity, do I really need the
IIdentity C# interface?

Sorry, I haven't done COM stuff in so long and I have no idea where my
COM books have disappeared to, so I'm working with a flakey memory...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top