Comment mettre à jour par programmation les références OCX dans les projets vb6?
Question
Je casse périodiquement la compatibilité binaire et je dois recompiler une application vb6 complète composée de plusieurs dizaines de DLL ActiveX et OCX au total. J'ai écrit un script pour automatiser ce processus, mais j'ai rencontré un problème.
Lorsqu'un OCX est recompilé avec la compatibilité de projet, sa version est incrémentée et les projets référençant l'OCX ne seront pas recompilés tant que leur référence ne sera pas mise à jour vers la nouvelle version. Ceci est vérifié automatiquement lorsque le projet est ouvert normalement et l'utilisateur est invité à mettre à jour la référence, mais je dois le faire dans un script.
Comment puis-je le faire?
La solution
Je suppose que vous devez éditer les fichiers de projet (.vbp), les fichiers de formulaire (.frm) et les fichiers de contrôle (.ctl) qui référencent les DLL et les OCX et incrémentent le numéro de version de Typelib.
Vous trouverez le dernier numéro de version de la bibliothèque de types pour le contrôle / DLL dans le registre.
Cela peut être difficile en fonction du nombre de fichiers que vous avez.
Un hack consisterait à ouvrir le projet principal avec VB6 à l'aide de votre script et à envoyer les clés pour confirmer les références de mise à jour, puis enregistrer le projet.
Bonne chance
Autres conseils
Mon projet, géré pendant une décennie, consiste en une hiérarchie de deux douzaines de DLL ActiveX et d'une demi-douzaine de contrôles. Compilé avec un système de script également.
Je ne recommande pas de faire ce que vous faites.
Ce que nous faisons est le suivant
- Effectuer nos modifications, y compris les ajouts et tester dans l'EDI.
- Nous compilons à partir du bas de la hiérarchie vers le haut
- nous copions les fichiers récemment complétés dans un répertoire de révision par exemple 601, puis 602, etc., etc.
- nous créons le setup.exe
- lorsque la configuration est finalisée, nous copions sur le répertoire de révision dans notre directeur de compatibilité. Notez que nous ne pointez jamais vers le binaire compilé dans le répertoire du projet. Toujours à un répertoire de compatibilité qui a tout les DLL.
La raison pour laquelle cela fonctionne est que si vous examinez la source IDL à l'aide de l'outil Vue OLE, vous constaterez que tout contrôle référencé ou dll est ajouté à l'interface via un #include. Si vous pointez sur le fichier binaire de votre répertoire de projet, l'inclusion est extraite du registre, ce qui peut entraîner beaucoup d'étrangeté et de compatibilité.
Toutefois, si la DLL référencée est présente dans le répertoire qui contient le fichier binaire et est utilisée pour la compatibilité binaire, VB6 l'utilisera à la place de ce que le registre contient.
Maintenant, il y a un problème que vous rencontrez peu fréquemment. Considérez cette hiérarchie
- MyUtilityDLL
- MyObjectDLL
- MyUIDLL
- MyEXE
Si vous AJOUTEZ une propriété ou une méthode à une classe dans MyUtilityDLL, il se peut que MyUIDLL ne compile pas, ce qui donne une erreur d'incompatibilité binaire si vous êtes chanceux ou une erreur étrange telle que [inref]. Dans tous les cas, la solution consiste à compiler MyUtilityDLL, puis à copier immédiatement MyUtilityDLL dans le répertoire de compatibilité. Ensuite, le reste de la compilation automatisée fonctionnera correctement.
Vous pouvez inclure cette étape dans la construction automatisée.
Notez que dans de nombreux cas, les projets fonctionneront bien dans l'EDI. Si vous en êtes maintenant conscient, vous risquez de vous arracher les cheveux.
Nous procédons de la même manière, c’est-à-dire que nous manipulons les références aux OCX utilisés directement dans les fichiers VB6 .vbp, dans notre Outil de mise à jour des références de projet VB6 ( à télécharger ici ) . Généralement, il est utilisé pour mettre à jour les références lorsque l’ActiveX utilisé change de numéro de version, de CLSID, etc.
Les outils sont open-source afin que toutes les personnes intéressées par ce problème puissent emprunter nos extraits de code VB pour mettre en œuvre des tâches comme celles-ci.
Notre outil est écrit en Visual Basic 6 et utilise tlbinf32.dll (la DLL d'informations de bibliothèque de types) qui vous permet d'extraire par programme des informations à partir de bibliothèques de types.
Auto-réponse: j'ai écrit du code vb6 pour effectuer la mise à niveau par programme. Il n’a pas fait l’objet de tests approfondis. Il existe probablement quelques bugs ici et là dans certains cas, mais j’ai utilisé avec succès.
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