Attribute VB_Name = "DevTools" '================ Developer Tools ============= ' Shared module version: 20220811 ' Depends on: ex_WinAPI ' Required reference: Scripting, VBIDE Option Private Module Option Explicit Private Const TEST_SUITE_MASK = "s_*" Private Const TEST_NAME_MASK = "t_*" Private Const COMPILATION_DELAY As Long = 250 ' ms Public Function CompileVBProject(target As VBIDE.VBProject) As Boolean Const CONTROL_ID_COMPILE = 578 Const MSGBOX_CLASS_ID As String = "#32770" Const MSGBOX_NAME As String = "Microsoft Visual Basic for Applications" Const WINDOW_MESSAGE_CLOSE As Long = &H10 target.VBE.MainWindow.Visible = True With target.VBComponents(1).CodeModule Call .CodePane.Show Call .InsertLines(1, "123") Call .DeleteLines(1, 1) End With Dim btnCompile As CommandBarControl: Set btnCompile = target.VBE.CommandBars.FindControl(ID:=CONTROL_ID_COMPILE) If Not btnCompile.Enabled Then CompileVBProject = False Exit Function End If Call btnCompile.Execute Call Sleep(COMPILATION_DELAY) Dim nHwnd&: nHwnd = FindWindow(MSGBOX_CLASS_ID, MSGBOX_NAME) CompileVBProject = nHwnd = 0 If Not CompileVBProject Then _ Call PostMessage(nHwnd, WINDOW_MESSAGE_CLOSE, 0&, 0&) End Function Public Function UpdateVersionStamp(target As VBIDE.VBProject, sVersion$) As Boolean UpdateVersionStamp = False On Error Resume Next Dim iModule As VBComponent: Set iModule = target.VBComponents("Main") If iModule Is Nothing Then _ Exit Function Dim iCode As CodeModule: Set iCode = iModule.CodeModule Dim nLineStart&: nLineStart = 1 Dim nLineEnd&: nLineEnd = iCode.CountOfLines Dim nColumnStart&: nColumnStart = 1 Dim nColumnEnd&: nColumnEnd = 255 If Not iCode.Find("Public Const PRODUCT_VERSION", nLineStart, nColumnStart, nLineEnd, nColumnEnd) Then _ Exit Function Call iCode.ReplaceLine(nLineEnd, "Public Const PRODUCT_VERSION = """ & sVersion & """") UpdateVersionStamp = True End Function Public Function PrepareSkeletonFor(target As Object) On Error Resume Next Call target.Application.Run("'" & target.Name & "'" & "!Dev_PrepareSkeleton") End Function Public Function ExtractProperties(sLines() As String, ByRef nCurrent&, ByRef oSink As Scripting.Dictionary) ExtractProperties = False Do While nCurrent <= UBound(sLines, 1) Dim sLine$: sLine = sLines(nCurrent) nCurrent = nCurrent + 1 If sLine Like "%%*" Then _ Exit Do Dim nSeparator&: nSeparator = VBA.InStr(1, sLine, "=") If nSeparator = 0 Then _ GoTo NEXT_LINE If VBA.Left(sLine, 1) = "#" Then _ GoTo NEXT_LINE Dim sKey$: sKey = VBA.Trim(VBA.Left(sLine, nSeparator - 1)) Dim sValue$: sValue = VBA.Trim(Right(sLine, VBA.Len(sLine) - nSeparator)) If oSink.Exists(sKey) Then _ Exit Function Call oSink.Add(sKey, sValue) NEXT_LINE: Loop ExtractProperties = True End Function Public Function Dev_CountPublicAPI(target As VBIDE.CodeModule) As Long Dim nLine& For nLine = 1 To target.CountOfLines Step 1 If InStr(1, target.Lines(nLine, 1), "Public") <> 0 Then _ Dev_CountPublicAPI = Dev_CountPublicAPI + 1 Next nLine End Function Public Function Dev_ScanFunctions(target As VBIDE.CodeModule) As Collection ' of CDS_InfoFunction Dim cResult As New Collection Dim currentFunc As CDS_InfoFunction Dim lastFunc As New CDS_InfoFunction Dim nLine& For nLine = 1 To target.CountOfLines Step 1 Set currentFunc = ExtractProcedure(target, nLine) If lastFunc.name_ <> currentFunc.name_ And currentFunc.name_ <> vbNullString Then currentFunc.isPublic_ = IsPublicFunc(target, currentFunc) Call cResult.Add(currentFunc) End If Set lastFunc = currentFunc Next nLine Set Dev_ScanFunctions = cResult End Function Public Function Dev_ScanTests(target As VBIDE.VBProject, ByRef cTestsDB As Scripting.Dictionary) As Long Dim nCount&: nCount = 0 Dim aComponent As VBIDE.VBComponent Dim iComponents As VBIDE.VBComponents: Set iComponents = target.VBComponents Dim tests As Collection For Each aComponent In iComponents If aComponent.Type <> vbext_ct_ClassModule Then _ GoTo NEXT_COMPONENT If Not aComponent.Name Like TEST_SUITE_MASK Then _ GoTo NEXT_COMPONENT Set tests = New Collection Dim funcs As Collection: Set funcs = Dev_ScanFunctions(aComponent.CodeModule) Dim aFunc As CDS_InfoFunction For Each aFunc In funcs If aFunc.name_ Like TEST_NAME_MASK And aFunc.isPublic_ Then _ Call tests.Add(aFunc.name_) Next aFunc If tests.Count > 0 Then Call cTestsDB.Add(aComponent.Name, tests) nCount = nCount + tests.Count End If NEXT_COMPONENT: Next aComponent Set iComponents = Nothing Set aComponent = Nothing Dev_ScanTests = nCount End Function Public Function Dev_RemoveDebugCode(target As VBIDE.VBProject) Dim selectedComps As New Collection Dim aComponent As VBIDE.VBComponent For Each aComponent In target.VBComponents If Dev_IsTestingModule(aComponent.Name) Then _ Call selectedComps.Add(aComponent) Next aComponent For Each aComponent In selectedComps Call target.VBComponents.Remove(aComponent) Next aComponent End Function Public Function Dev_IsTestingModule(sName$) As Boolean Dev_IsTestingModule = True If sName Like TEST_SUITE_MASK Then _ Exit Function If sName Like "DevHelper*" Then _ Exit Function If sName Like "DevTester*" Then _ Exit Function If sName Like "DevTesterUI*" Then _ Exit Function If sName Like "API_MockInteraction*" Then _ Exit Function Dev_IsTestingModule = False End Function ' ===== Private Function ExtractProcedure(target As VBIDE.CodeModule, nLine&) As CDS_InfoFunction Set ExtractProcedure = New CDS_InfoFunction ExtractProcedure.name_ = target.ProcOfLine(nLine, ExtractProcedure.type_) End Function Private Function IsPublicFunc(target As VBIDE.CodeModule, func As CDS_InfoFunction) As Boolean Dim nStart&: nStart = target.ProcBodyLine(func.name_, func.type_) Dim sTxt$: sTxt = target.Lines(nStart, 1) IsPublicFunc = InStr(1, sTxt, "Private") = 0 End Function