VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END Attribute VB_Name = "API_Project" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False ' ================ Import / export code functionality ============= ' Shared module version: 20220407 ' Tested in: ' Depends on: ' Required reference: Scripting, VBIDE Option Explicit Private fso_ As Scripting.FileSystemObject Private proj_ As VBIDE.VBProject Private contents_ As Scripting.Dictionary Private sharedContents_ As Scripting.Dictionary Private sourceHome_ As String Private importsHome_ As String Private Sub Class_Initialize() Set fso_ = New Scripting.FileSystemObject End Sub Private Sub Class_Terminate() Call Detach End Sub Public Function Init(target As VBIDE.VBProject, Optional sImportsHome$ = "") As Boolean Init = target.Protection <> 1 If Not Init Then Call MsgBox("Project code is protected, cannot acccess", vbCritical) Exit Function End If Set proj_ = target importsHome_ = sImportsHome Set contents_ = New Scripting.Dictionary Set sharedContents_ = New Scripting.Dictionary End Function Public Function Detach() Set proj_ = Nothing importsHome_ = "" End Function Public Function SetInternals(ByRef oContents As Scripting.Dictionary, _ ByRef oSharedCont As Scripting.Dictionary) Set contents_ = oContents Set sharedContents_ = oSharedCont End Function Public Function ExportSrcTo(sPath$) sourceHome_ = sPath Call PrepareFolders(contents_) Call InternalExport(contents_) End Function Public Function ExportShared() sourceHome_ = importsHome_ Call PrepareFolders(sharedContents_) Call InternalExport(sharedContents_) End Function Public Function ImportSrcFrom(sPath$) sourceHome_ = sPath Call InternalImport(contents_) End Function Public Function ImportShared() sourceHome_ = importsHome_ Call InternalImport(sharedContents_) End Function Public Function RemoveAll() Dim nItem&: nItem = 1 Dim aComponent As VBIDE.VBComponent Do While nItem <= proj_.VBComponents.Count Set aComponent = proj_.VBComponents.Item(nItem) If aComponent.Type <> vbext_ct_Document Then If Not TryRemoving(aComponent) Then _ nItem = nItem + 1 Else nItem = nItem + 1 End If Loop End Function Public Function AddItem(sName$, sFolder$) Call contents_.Add(sName, sFolder) End Function Public Function AddSharedItem(sName$, sFolder$) Call sharedContents_.Add(sName, sFolder) End Function Public Function ReloadFrom(sFile$) As Boolean ReloadFrom = False If fso_.FileExists(sFile) Then _ ReloadFrom = TryImportFile(fso_.GetFile(sFile)) End Function ' ========= Private Function PrepareFolders(target As Scripting.Dictionary) Call ProjEnsureFolderExists(sourceHome_) Dim sKey As Variant For Each sKey In target Call ProjEnsureFolderExists(sourceHome_ & "\" & CStr(target(sKey))) Next sKey End Function Private Function InternalExport(target As Scripting.Dictionary) Dim fso As New Scripting.FileSystemObject Dim aComponent As VBComponent For Each aComponent In proj_.VBComponents Dim sName$: sName = ComponentName(aComponent) If Not target.Exists(sName) Then _ GoTo NEXT_COMPONENT Dim sPath$: sPath = sourceHome_ & "\" & target(sName) & "\" & sName If aComponent.Type = vbext_ct_MSForm Then _ If HasNoChanges(aComponent, sPath) Then _ GoTo NEXT_COMPONENT Call aComponent.Export(sPath) NEXT_COMPONENT: Next aComponent End Function Private Function InternalImport(tContents As Scripting.Dictionary) Dim sFileName As Variant Dim sPath$ For Each sFileName In tContents sPath = sourceHome_ & "\" & tContents(sFileName) & "\" & CStr(sFileName) If fso_.FileExists(sPath) Then _ Call TryImportFile(fso_.GetFile(sPath)) Next sFileName End Function Private Function TryImportFile(target As Scripting.File) As Boolean TryImportFile = False Dim sExt$: sExt = fso_.GetExtensionName(target.Name) Dim sName$: sName = fso_.GetBaseName(target.Name) Dim sComp As VBIDE.VBComponent: Set sComp = ComponentByName(sName) If Not sComp Is Nothing Then _ If Not TryRemoving(sComp) Then _ Exit Function Call proj_.VBComponents.Import(target.Path) If sExt = "frm" Then _ Call RemoveEmptyFirstLine(ComponentByName(sName).CodeModule) TryImportFile = True End Function Private Function ComponentName(target As VBComponent) As String ComponentName = target.Name Select Case target.Type Case vbext_ct_ClassModule: ComponentName = ComponentName & ".cls" Case vbext_ct_MSForm: ComponentName = ComponentName & ".frm" Case vbext_ct_StdModule: ComponentName = ComponentName & ".bas" Case Else: ComponentName = vbNullString End Select End Function Private Function RemoveEmptyFirstLine(target As CodeModule) Dim firstLineTxt$: firstLineTxt = target.Lines(1, 1) If firstLineTxt = vbNullString Then _ Call target.DeleteLines(1, 1) End Function Private Function TryRemoving(target As VBIDE.VBComponent) As Boolean Dim nCount& nCount = proj_.VBComponents.Count Call proj_.VBComponents.Remove(target) TryRemoving = nCount <> proj_.VBComponents.Count End Function Private Function ComponentByName(sName$) As VBIDE.VBComponent Dim aComponent As VBComponent For Each aComponent In proj_.VBComponents If aComponent.Name = sName Then Set ComponentByName = aComponent Exit Function End If Next aComponent End Function Private Function ProjEnsureFolderExists(sPath$) If fso_.FolderExists(sPath) Then _ Exit Function Dim sParent$: sParent = sPath Do While VBA.Right(sParent, 1) = "\" sParent = VBA.Left(sParent, VBA.Len(sParent) - 1) Loop sParent = VBA.Left(sParent, VBA.InStrRev(sParent, "\") - 1) Call ProjEnsureFolderExists(sParent) Call fso_.CreateFolder(sPath) End Function Private Function HasNoChanges(aForm As VBIDE.VBComponent, sPath$) As Boolean HasNoChanges = False Dim fso As New Scripting.FileSystemObject If Not fso.FileExists(sPath) Then _ Exit Function Dim tmpDir$: tmpDir = fso.GetParentFolderName(sPath) & "\tmpFrm" Dim tmpFile$: tmpFile = tmpDir & "\" & aForm.Name & ".frm" Call fso.CreateFolder(tmpDir) Call aForm.Export(tmpFile) Dim nFile1&: nFile1 = FreeFile Open sPath For Input As #nFile1 Dim cont1 As Variant: cont1 = Input(LOF(nFile1), nFile1) Close nFile1 Dim nFile2&: nFile2 = FreeFile Open tmpFile For Input As #nFile2 Dim cont2 As Variant: cont2 = Input(LOF(nFile2), nFile2) Close nFile2 HasNoChanges = cont1 = cont2 Call fso.DeleteFolder(tmpDir) End Function