tags:

views:

491

answers:

3

I have a COM server with a method currently returning an integer:

[
    object,
    uuid("..."),
    dual,
    helpstring("IMyCOMServer Interface"),
    pointer_default(unique)
]
__interface IMyCOMServer : IDispatch
{
    [id(1), helpstring("method MyQuery")]
    HRESULT MyQuery([in] BSTR instr, [out,retval] int* outint);
};

This compiles fine, but I'd rather return an enum: (this code is actually above the interface definition)

typedef
[
    uuid("..."),
    v1_enum,
    helpstring("Enum")
]
enum {
    value_a,
    value_b,
    value_c
} MyEnum;

Which again compile fine of its own right, but as soon as I change the int* to MyEnum* in the interface and implementation, I get linker errors:

[id(1), helpstring("method MyQuery")]
HRESULT MyQuery([in] BSTR instr, [out,retval] MyEnum* outint);

error MIDL2025 : syntax error : expecting a type specification near "MyEnum"

Whichever way I do it, I can't get it to compile.


Thanks to Euro Micelli it turns out that the real problem is that my User Defined Type (the enum) wasn't making it into the generated .IDL file. Judging by forum queries online, this seems to be a common problem.

A blog article Star Tech: UDT (User Defined Types) and COM guided me down the right path. It seems that a workaround is needed when using attributed ATL.

In summary, I made the following changes:

Created udt.idl:

import "oaidl.idl";
import "ocidl.idl";

[
    uuid("..."),
    v1_enum,
    helpstring("Enum")
]
typedef enum MyEnum {
    value_a,
    value_b,
    value_c
} MyEnum_t;

[
    version(1.0),
    uuid(...),
    helpstring(...)
]
library MyLibrary
{
    enum MyEnum;
}

Added the following line prior to the module attribute in the main .cpp file so that the above IDL gets imported into the generated file:

[importidl("udt.idl")];
+1  A: 

This is very compiler-dependent, since enums don't have a fixed storage size.

There's also the compatibility angle - how would you represent that enum in, say, Visual Basic, or C#? The underlying storage is something like an integer, so that's what COM allows.

Ben Straub
This is not C or C++. Enums in COM interfaces have sizes defined by COM rules. VB and C# both support enums (and they are compatible with COM enums). That doesn't mean that every language has to: in VBScript, for example, an enum is silently mapped to Integer.
Euro Micelli
+1  A: 

You've almost got it, but the idl compiler has a little stricter syntax than the cl.exe. You need to have the initial enum name before the enum like this.

typedef
[uuid("..."), v1_enum, helpstring("Enum")]
enum tagMyEnum
{
    value_a,
    value_b,
    value_c
} MyEnum;

If you build and register your tlb then scripting languages should be able to access your enum in scripts and .NET.

nichow
Data sizes defined in idl files for COM classes are not compiler dependant, but standardized. If they weren't standard sizes, COM's interoperability would be eliminated and one of the major reasons for COM's existence with it.
nichow
I'd tried it with a tag before but removed it after reading suggestions to do so. I get a different error:Error 1 error MIDL2011 : unresolved type declaration : [ Parameter 'outenum' of Procedure 'MyQuery' ( Interface 'IMyCOMServer' ) ]This is with my function defined:HRESULT MyQuery([in] BSTR instr, [out,retval] enum tagMyEnum* outenum);
Mat
+1  A: 

(This is adapted from an actual IDL, so I know it works)

[uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX), v1_enum, helpstring("Enum")]
enum MyEnum {
    value_a,
    value_b,
    value_c
} ;

Plus, in your Library section you must include the enum as well, or the enum won't be exported to the type library:

library MyLib
{
    enum MyEnum;
...
};
Euro Micelli
Ahh... I'm not sure I have a library section!
Mat
Right. I have a library section in my generated .IDL file. This file doesn't contain the enum at all. How do I get it put the enum in the right place in the .IDL file?
Mat
I don't know for sure; is it generated by Visual Studio? I have done most of my COM/IDL "the hard way" (VS 6) so I'm not that familiar with the newer versions of the COM/IDL tooling. If it's generated-once, you can type it yourself. If it's generated by VS or other tools, then the tool should have been smart enough to know that it needed the library entry.
Euro Micelli