Как программно обновить ссылки OCX в проектах vb6?

Я периодически нарушаю двоичную совместимость, и мне нужно перекомпилировать все приложение vb6, состоящее из нескольких десятков ActiveX DLL и OCX в общей сложности. Я написал сценарий для автоматизации этого процесса, но столкнулся с проблемой.

Когда OCX перекомпилируется с совместимостью проектов, его версия увеличивается, и проекты, ссылающиеся на OCX, не будут перекомпилироваться, пока их ссылка не будет обновлена ​​до новой версии. Это проверяется автоматически, когда проект открывается в обычном режиме, и пользователю предлагается обновить ссылку, но мне нужно сделать это в сценарии.

Как это сделать?


person Craig Gidney    schedule 06.03.2009    source источник


Ответы (4)


Я предполагаю, что вам придется отредактировать файлы проекта (.vbp), файлы форм (.frm) и управляющие файлы (.ctl), которые ссылаются на библиотеки DLL и OCX, и увеличить номер версии библиотеки типов.

Вы можете найти последний номер версии библиотеки типов для элемента управления / DLL в реестре.

Это может быть проблемой в зависимости от того, сколько у вас файлов.

Хакерством было бы открыть основной проект с VB6 с помощью вашего скрипта и отправить ключи для подтверждения ссылок на обновления, а затем сохранить проект.

Удачи

person DJ.    schedule 06.03.2009
comment
Я использовал этот подход несколько раз, он работает нормально, но немного утомительно. Автоматическая замена всех файлов с помощью Notepad ++ - фантастическая экономия времени. - person StayOnTarget; 22.11.2018

Мой проект, поддерживаемый более десяти лет, состоит из иерархии двух дюжин ActiveX DLL и полдюжины элементов управления. Также скомпилирован с помощью скриптовой системы.

Я не рекомендую делать то, что делаете вы.

Мы делаем следующее

  1. Внесите наши изменения, включая дополнения и тестирование в IDE.
  2. Мы компилируем снизу иерархии вверх
  3. мы копируем новые файлы в каталог ревизии, например 601, затем 602 и т. д. и т. д.
  4. мы создаем setup.exe
  5. когда установка завершена, мы копируем каталог ревизии в наш каталог совместимости. Обратите внимание, что мы никогда не указываем на скомпилированный двоичный файл в каталоге проекта. Всегда в каталог совместимости, в котором есть все библиотеки DLL.

Причина, по которой это работает, заключается в том, что если вы посмотрите на источник IDL с помощью инструмента OLE View, вы обнаружите, что любой элемент управления или библиотеки, на которые есть ссылки, добавляются в интерфейс через #include. Если вы укажете двоичный файл в каталоге своего проекта, то включение будет выбрано из реестра, что может привести к большой странности и совместимости.

Однако, если указанная DLL присутствует в каталоге, в котором существует двоичный файл при использовании для двоичной совместимости, VB6 будет использовать это вместо того, что есть в реестре.

Теперь есть одна проблема, с которой вы будете сталкиваться нечасто. Считайте эту иерархию

  • MyUtilityDLL
  • MyObjectDLL
  • MyUIDLL
  • MyEXE

Если вы ДОБАВЛЯЕТЕ свойство или метод в класс в MyUtilityDLL, MyUIDLL может не компилироваться, давая ошибку двоичной несовместимости, если вам повезет, или странную ошибку, такую ​​как [inref]. В любом случае решение состоит в том, чтобы скомпилировать MyUtilityDLL, а затем немедленно скопировать MyUtilityDLL в каталог совместимости. Тогда остальная часть автоматической компиляции будет работать нормально.

Вы можете включить этот шаг в автоматизированную сборку.

Обратите внимание, что во многих случаях проекты будут нормально работать в среде IDE. Если вы сейчас осознаете это, вы можете выдергивать волосы.

person RS Conley    schedule 07.03.2009
comment
Думаю, я столкнулся с ошибкой, которую вы описываете в конце своего сообщения. Но почему наличие каталога совместимости устраняет его? Если вы укажете на каталог сборки, это все те же данные, на которые ссылаются, когда произошла ошибка. - person Craig Gidney; 07.03.2009
comment
Поскольку библиотеки DLL двоичной совместимости извлекают свои включения из библиотек DLL в каталоге вместо использования реестра. - person RS Conley; 09.03.2009
comment
Очень опасно выставлять публичные классы типов MyUIDLL из MyUtilityDLL. Просто используйте As Object для param type / retval и избавьтесь от проблем с [inref] и другими причудливыми ошибками. Мы используем EditTLB из PowerVB для проверки внешних зависимостей и разрешаем там только статические библиотеки (ADODB и т. Д.). Ничто из того, что мы создаем (и может сломаться), не может быть внешней библиотекой типов для любого другого нашего проекта. - person wqw; 21.04.2009
comment
Это может быть хорошей схемой для общего поддержания двоичной совместимости, но я не понимаю, как она отвечает на конкретный вопрос об изменении ссылок OCX, которые требуют особых соображений из-за того, как на них ссылаются в формах и т. Д. - person StayOnTarget; 27.11.2018

Мы делаем аналогичные вещи, то есть манипулируем ссылками на используемые OCX непосредственно в файлах VB6 .vbp, в нашем Инструмент обновления ссылок на проекты VB6 (загрузите здесь). Обычно он используется для обновления ссылок, когда используемые ActiveX меняют свои номера версий, идентификаторы CLSID и т. Д.

введите описание изображения здесь

Инструменты имеют открытый исходный код, поэтому каждый, кто интересуется этой проблемой, может позаимствовать наши фрагменты кода VB для реализации подобных задач.

Наш инструмент написан на Visual Basic 6 и использует tlbinf32.dll (информационная библиотека TypeLib), которая позволяет программно извлекать информацию из библиотек типов.

person TecMan    schedule 27.01.2014

Самостоятельный ответ: я написал код vb6 для программного обновления. Он не тестировался всесторонне, вероятно, здесь и там есть несколько ошибок для крайних случаев, но я успешно его использовал.

Option Explicit

Const HKEY_LOCAL_MACHINE As Long = &H80000002
Const KEY_ENUMERATE_SUB_KEYS As Long = 8
Private Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As Long) As Long
Private Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" (ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpName As String, lpcbName As Long, lpReserved As Long, ByVal lpClass As String, lpcbClass As Long, lpftLastWriteTime As Any) As Long
Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long

'''Returns the expected major version of a GUID if it exists, and otherwise returns the highest registered major version.
Public Function GetOcxMajorVersion(ByVal guid As String, Optional ByVal expected_version As Long) As Long
    Const BUFFER_SIZE As Long = 255
    Dim reg_key As Long
    Dim ret As Long
    Dim enum_index As Long
    Dim max_version As Long: max_version = -1

    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\Classes\TypeLib\{" & guid & "}", 0, KEY_ENUMERATE_SUB_KEYS, reg_key)
    If ret <> 0 Then Err.Raise ret, , "Failed to open registry key."
    Do
        'Store next subkey name in buffer
        Dim buffer As String: buffer = Space(BUFFER_SIZE)
        Dim cur_buffer_size As Long: cur_buffer_size = BUFFER_SIZE
        ret = RegEnumKeyEx(reg_key, enum_index, buffer, cur_buffer_size, ByVal 0&, vbNullString, ByVal 0&, ByVal 0&)
        If ret <> 0 Then Exit Do
        buffer = Left(buffer, cur_buffer_size)

        'Keep most likely version
        buffer = Split(buffer, ".")(0)
        If Not buffer Like "*[!0-9A-B]*" And Len(buffer) < 4 Then
            Dim v As Long: v = CLng("&H" & buffer) 'convert from hex
            If v = expected_version Then
                max_version = v
                Exit Do
            ElseIf max_version < v Then
                max_version = v
            End If
        End If

        enum_index = enum_index + 1
    Loop
    RegCloseKey reg_key

    If max_version = -1 Then Err.Raise -1, , "Failed to enumerate any viable subkeys."
    GetOcxMajorVersion = max_version
End Function

Public Function RemoveFilename(ByVal path As String) As String
    Dim folders() As String: folders = Split(Replace(path, "/", "\"), "\")
    RemoveFilename = Left(path, Len(path) - Len(folders(UBound(folders))))
End Function

'''Changes any invalid OCX references to newer registered version
Public Sub UpdateFileOCXReferences(ByVal path As String)
    Dim file_data As String
    Dim changes_made As Boolean

    'Read
    Dim fn As Long: fn = FreeFile
    Open path For Input As fn
        While Not EOF(fn)
            Dim line As String
            Line Input #fn, line

            'check for ocx reference line
            If LCase(line) Like "object*=*{*-*-*-*-*}[#]*#.#*[#]#*;*.ocx*" Then
                'get guid
                Dim guid_start As Long: guid_start = InStr(line, "{") + 1
                Dim guid_end As Long: guid_end = InStr(line, "}")
                Dim guid As String: guid = Mid(line, guid_start, guid_end - guid_start)

                'get reference major version
                Dim version_start As Long: version_start = InStr(line, "#") + 1
                Dim version_end As Long: version_end = InStr(version_start + 1, line, ".")
                Dim version_text As String: version_text = Mid(line, version_start, version_end - version_start)

                'play it safe
                If Len(guid) <> 32 + 4 Then Err.Raise -1, , "GUID has unexpected length."
                If Len(version_text) > 4 Then Err.Raise -1, , "Major version is larger than expected."
                If guid Like "*[!0-9A-F-]*" Then Err.Raise -1, , "GUID has unexpected characters."
                If version_text Like "*[!0-9]*" Then Err.Raise -1, , "Major version isn't an integer."

                'get registry major version
                Dim ref_version As Long: ref_version = CLng(version_text)
                Dim reg_version As Long: reg_version = GetOcxMajorVersion(guid, ref_version)

                'change line if necessary
                If reg_version < ref_version Then
                    Err.Raise -1, , "Registered version precedes referenced version."
                ElseIf reg_version > ref_version Then
                    line = Left(line, version_start - 1) & CStr(reg_version) & Mid(line, version_end)
                    changes_made = True
                End If
            End If

            file_data = file_data & line & vbNewLine
        Wend
    Close fn

    'Write
    If changes_made Then
        Kill path
        Open path For Binary As fn
            Put fn, , file_data
        Close fn
    End If
End Sub

'''Changes any invalid in included files to newer registered version
Public Sub UpdateSubFileOCXReferences(ByVal path As String)
    Dim folder As String: folder = RemoveFilename(path)
    Dim fn As Long: fn = FreeFile
    Open path For Input As fn
        While Not EOF(fn)
            Dim line As String
            Line Input #fn, line

            If LCase(line) Like "form=*.frm" _
                            Or LCase(line) Like "usercontrol=*.ctl" Then
                Dim file As String: file = folder & Mid(line, InStr(line, "=") + 1)
                If Dir(file) <> "" Then
                    UpdateFileOCXReferences file
                End If
            End If
        Wend
    Close fn
End Sub
person Craig Gidney    schedule 07.03.2009