tags:

views:

491

answers:

2

I am trying to register a type library programatically from VBA code, using two variants of a technique found using Google (Subs RegisterTypeLibrary and RegisterTypeLibrary2 below).

The code below crashes with an access violation on the call to LoadTypeLib / LoadTypeLibEx. What am I doing wrong? In case it's relevant, the type library is a TLB file generated from a .NET assembly using tlbexp.

Private Enum RegKind
    RegKind_Default = 0
    RegKind_Register = 1
    RegKind_None = 2
End Enum

Private Declare Function LoadTypeLibEx Lib "oleaut32.dll" ( _
    pFileName As Byte, ByVal RegKind As RegKind, pptlib As Object) As Long
Private Declare Function LoadTypeLib Lib "oleaut32.dll" ( _
    pFileName As Byte, pptlib As Object) As Long
Private Declare Function RegisterTypeLib Lib "oleaut32.dll" ( _
    ByVal ptlib As Object, szFullPath As Byte, _
    szHelpFile As Byte) As Long

Private Sub RegisterTypeLibrary(FileName As String)

    Dim abNullTerminatedFileName() As Byte
    Dim objTypeLib As Object
    Dim lHResult As Long

    abNullTerminatedFileName = FileName & vbNullChar
    lHResult = LoadTypeLib(abNullTerminatedFileName(0), objTypeLib)
    If lHResult <> 0 Then
        Err.Raise lHResult, "LoadTypeLib", "Error registering type library " & FileName
    End If
    lHResult = RegisterTypeLib(objTypeLib, abNullTerminatedFileName(0), 0)
    If lHResult <> 0 Then
        Err.Raise lHResult, "RegisterTypeLib", "Error registering type library " & FileName
    End If
    Exit Sub

End Sub
Private Sub RegisterTypeLibrary2(FileName As String)
    Dim abNullTerminatedFileName() As Byte
    Dim objTypeLib As Object
    Dim lHResult As Long

    abNullTerminatedFileName = FileName & vbNullChar
    lHResult = LoadTypeLibEx(abNullTerminatedFileName(0), ByVal RegKind_Register, objTypeLib)
    If lHResult <> 0 Then
        Err.Raise lHResult, "LoadTypeLibEx", "Error registering type library " & FileName
    End If
End Sub

EDIT

I suspect it is something specific about my type library. I've found a solution which I've posted as an answer below.

+1  A: 

I suspect your type library (TLB) has errors because the code you provided works when I tested against a third-party TLB.

I am assuming you are going to use your .NET Assembly from VBA. Therefore, I suggest you make sure you can reference your TLB from VBA without errors.

Note, that all objects exposed by your .NET library must have public constructors that accept no arguments. This may be causing the problem.

AMissico
Thanks for the response. Yes I can register the TLB from within the VBA IDE without any errors. If there is something wrong with my .NET typelib, I could understand getting an error such as 0x80029c4a - Error loading type library, but not an access violation.
Joe
How about registering another TLB? If your code works then I believe the problem would be security that may be resolved with a Code Access Security Policy. (I don't have time to create TLB from .NET Assembly in order to test your code, nor do I have one at the moment.)
AMissico
Does it crash as you, the developer, or you, the user? I only tested as me, the developer.
AMissico
It crashes when I attempt to run the code above, passing the name of my type library as an argument. However executing regtlb.exe with my type library works, so it looks like my type library is OK. And Dependency walker shows that regtlb itself calls LoadTypeLibEx. Hence I think there must be something wrong with the VBA code above.
Joe
A: 

I've found a solution, using the code below. Basically, the third parameter to LoadTypeLibEx (ITypeLib** in C/C++) is declared as stdole.IUnknown instead of as Object.

To do so, I needed to add a reference to stdole32.tlb to the VBA project.

I suspect there is something about my type library that means it can't be declared as a VB (late-bound) Object.

I could also have declared the third parameter as Long, but I'm not sure that wouldn't lead to problems with reference counting.

Private Enum RegKind
    RegKind_Default = 0
    RegKind_Register = 1
    RegKind_None = 2
End Enum

Private Declare Function LoadTypeLibEx Lib "oleaut32.dll" ( _
    pFileName As Byte, ByVal RegKind As RegKind, pptlib As stdole.IUnknown) As Long

Public Sub RegisterTypeLibrary(FileName As String)
    Dim abNullTerminatedFileName() As Byte
    Dim objTypeLib As stdole.IUnknown
    Dim lHResult As Long

    abNullTerminatedFileName = FileName & vbNullChar
    lHResult = LoadTypeLibEx(abNullTerminatedFileName(0), ByVal RegKind_Register, objTypeLib)
    If lHResult <> 0 Then
        Err.Raise lHResult, "LoadTypeLibEx", "Error registering type library " & FileName
    End If
End Sub
Joe
As a note to, "needed to add a reference to stdole32.tlb to the VBA project," there should already have been a reference listed as "OLE Automation".
AMissico
When I used the Object versioin of RegisterTypeLibrary, I could not stop execution with a breakpoint within the function. The VBA editor crashed and Excel "recovered". I did not try to debug when I ran your code the first time, because the code worked. If you were trying to debug, it could explain why we had difference experiences running the code. Note, using the stdole.IUnknown version of RegisterTypeLibrary, I was able to set a breakpoint, stop execution, and otherwise debug normally.
AMissico
"there should already have been a reference listed as "OLE Automation" - my Office had a reference to stdole2.tlb listed as "OLE Automation". This didn't contain a definition of IUnknown so I removed it and replaced it by stdole32.tlb which does define IUnknown.
Joe
" If you were trying to debug," - yes I was running under the debugger. I tried the original code again outside the debugger and it works.
Joe
"OLE Automation" is stdole2.tlb and IUnknown is a hidden member of. To see, open the "Object Browser", right-click anywhere within the browser, and select "Show Hidden Members". Hidden members do not show with IntelliSense, but you can still type them in and use them.
AMissico
"OLE Automation" is stdole2.tlb - that's useful to know, thanks. I've changed it back from stdole32.tlb to stdole2.tlb.
Joe