Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why aren't TypeLib enums exposed as enums in Visual Basic 6.0?

I have a VB6 project that references COMSVCSLib and one of the methods makes calls to COMSVCSLib's SharedPropertyGroupManager.CreatePropertyGroup passing LockMethod and Process as parameters.

Cleaned up VB6 code:

Dim groupName       As String
Dim spmMgr          As COMSVCSLib.SharedPropertyGroupManager
Dim spmGroup        As COMSVCSLib.SharedPropertyGroup

Dim bGroupExists    As Boolean

Set spmMgr = New COMSVCSLib.SharedPropertyGroupManager

With spmMgr
    Set spmGroup = .CreatePropertyGroup(groupName, LockMethod, Process, bGroupExists)

End With

Having not worked with VB6 for several years now, at first I thought LockMethod and Process were variables or constants defined somewhere else within the project.

After a little research on the Object Browser I found out that they were both being exposed as constants in COMSVCSLib.

Object Browser

But looking at their definition in OLE/COM Object Viewer, they seem to be defined as values of an enumeration:

typedef enum {
    LockSetGet = 0,
    LockMethod = 1
} __MIDL___MIDL_itf_autosvcs_0469_0002;

Why aren't IDL/TypeLib enums from COMSVCSLib being exposed as enums to Visual Basic 6.0?

like image 381
Alfred Myers Avatar asked Sep 21 '10 20:09

Alfred Myers


People also ask

What is enum in Visual Basic net?

In VB.NET, Enum is a keyword known as Enumeration. Enumeration is a user-defined data type used to define a related set of constants as a list using the keyword enum statement. It can be used with module, structure, class, and procedure. For example, month names can be grouped using Enumeration.

What is the purpose of the enum statement in VB net?

The Enum statement can declare the data type of an enumeration. Each member takes the enumeration's data type. You can specify Byte , Integer , Long , SByte , Short , UInteger , ULong , or UShort . If you do not specify datatype for the enumeration, each member takes the data type of its initializer .

What is enum variable?

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.


1 Answers

Disclaimer: I'm not an expert on IDL (Interface Definition Language, which is the language used to define COM types) or the Microsoft IDL compiler (MIDL), but I came to the conclusions below after playing around with the type library for scrrun.dll, which has a similar problem with enum's. Some of this information was gleaned from a quick look at this DevX article on IDL and VB6: IDL for VB Tutorial

VB6 expects the actual enum to have a name, not just a enum that is typedef'd to a name. The __MIDL___MIDL_itf_autosvcs_0469_0002 name is a placeholder, since the original typelib didn't define the enum name in the same typedef where the enum constants are defined.

When you view the type library in OLE Viewer, the enum probably looks like this:

typedef [public] __MIDL___MIDL_itf_autosvcs_0469_0002 LockModes;

typedef enum {
    LockSetGet = 0,
    LockMethod = 1
} __MIDL___MIDL_itf_autosvcs_0469_0002;

The first typedef creates the public name LockModes as an alias for the auto-generated MIDL___MIDL_itf_autosvcs_0469_0002 name that was given to the enum. When the original type libary was compiled, the midl compiler generated the long __MIDL name for the original enum and automatically created a typedef alias pointing back to it.

The original IDL probably defined the enum like this:

typedef enum {
     LockSetGet = 0,
     LockMethod = 1
} LockModes;

When the midl compiler processes an enum definition written this way, it auto-generates a name for the enum (since it is missing - it should appear after the enum keyword). This is what the __MIDL name is that you see when you view the type library in OLE Viewer. The midl compiler also automatically generates a second typedef that aliases the typedef name to the auto-generated enum name.

The problem is that VB6 can't understand enum's that are created this way. It expects everything to be in a single typedef (i.e. you give the enum a name, as well as naming the typedef):

typedef enum LocksMode {
    LockSetGet = 0,
    LockMethod = 1
} LocksMode;

IDL treats typedef's the same way that C or C++ does: you don't have to give the enum itself a name, because the typedef already has a name, but you can give the enum a name if you choose. In other words, the typedef and the enum are actually two separate entities. VB6 happens to recognize the typedef and the enum as being two distinct, but vaguely-related things, so in your case it sees a typedef named __MIDL___MIDL_itf_autosvcs_0469_0002, and it sees that this is an alias to an unnamed enum, and it also sees a typedef for LockModes, which is a public alias for the other typedef.

Since the first typedef is public, you will see an entry for LockModes in the Object Browser, and because it is an alias for an enum, you will see the enum constants in the Object Browser as well. However, the actual enum itself does not have a name (so it gets the funky auto-generated name assigned to it in the browser), and VB6 can't use the enum because the auto-generated name happens to be illegal in VB6 (names with double-underscores are automatically hidden in VB6).

To demonstrate that last point, if you type this in your VB6 code, Intellisense will work and it will compile, but obviously, it's not very ideal:

MsgBox COMSVCSLib.[__MIDL___MIDL_itf_autosvcs_0469_0002].LockMethod

The reason this code works is because you can put names that normally cause syntax errors (such as names that start with underscores) in brackets to allow VB6 to accept the normally-illegal name. In addition, prefixing the constants with the auto-generated name works with Intellisense because it is the actual name that VB6 associates with the enum (remember the other typedef is just an alias back to this "real", but auto-generated name, and VB6 apparently can't put all the pieces together to realize both names refer to the same enum).

Rather than typing out the ridiculously-long name like above, you can also access enum constants by prefixing them with the library name, for example, COMSVCSLib.LockMethod should work. It's less clear to me why this actually works, and I'm not sure what would happen if two different enum's define constants with the same name.

Lastly, you could fix this problem a different way by using the IDL from the OLE Viewer to create a custom IDL file, in which you replace the existing enum typedefs with a single typedef for each enum that simply gives both the enum and the typedef the same name (i.e. typedef enum LockModes { ... } LockModes;), but since the OLE Viewer doesn't necessarily generate valid IDL, you would probably have to tweak it even more to get it to actually compile. If you can get this to work, then you can reference your custom .tlb from your VB6 project (instead of the COMSVCSLib library), and the enum's will work like you would expect them to.

If you want to go that route, there are two other tools you need, which should already been installed on your development machine (but you may need to search for them):

  • midl.exe: This tool can generate a typelib file (*.tlb) from a .idl file. So you could copy the IDL from the OLE Viewer into Notepad, modify the enum definitions as described above, save it as a .idl file, and pass it to midl.exe to make a new typelib:

    midl my-custom-typelib.idl

  • regtlib.exe: This tool can register a .tlb file, which is required if you want to be able to add it as a reference to your VB6 project:

    regtlib.exe my-custom-typelib.tlb

However, creating a custom typelib for this is probably overkill, and as already mentioned, it might be hard to get a compilable IDL file based off the output from the OLE Viewer, since it displays reverse-engineered IDL for the type library, not the original IDL.

like image 136
Mike Spross Avatar answered Sep 29 '22 00:09

Mike Spross