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.
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?
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.
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 .
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With