Editing macro design using VBA (1 Viewer)

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
Editing macro design as part of a full database search using VBA

Hi

I have recently completed a full database SQL search project for selected text in all database items: query SQL / table fields / module code (standard / class / form & report) & macros. All of this is working fine.

Now I'm in the process of extending this to do find & replace in all the above items. For the sake of completeness, I'm currently trying to do this with macros which are causing some difficulties!

You can't edit macro design using VBA but you can export the design

So, the approach is
a) export the macro as a text file
b) replace selected text in the text file
c) reimport the text file overwriting the original macro
d) delete the text file

The code fails with error 2128 at step c) - import
This is because step b) completely alters the file contents

See attached screengrabs - original & modified text file

The code below is my latest attempt

Code:
Public Function ModifyMacroVBA(strMacroName, strFind, strReplace)

On Error GoTo Err_Handler

    'Const ForReading = 1
    'Const ForWriting = 2

    Dim objFSO As Object
    Dim objFile As Object
    'Dim strOldText As String
    'Dim strNewText As String
    
   ' Dim strBuf As String
    Dim strTemp As String
  '  Dim iFileNum As Integer
    Dim strFileName As String
  '  Dim filesys As Variant
    Dim tempfile As Object
    Dim file As Object
    Dim CurrentLine As String
    Dim NewLine As String
    'Dim strMacroName As String

'1. save macro to text file in same folder
    Path = Application.CurrentProject.Path
    strFileName = Path & "\" & strMacroName & ".txt"
  ' Debug.Print strMacroName, strFilename
   ' Application.SaveAsText acMacro, strMacroName, strFileName
    
    'Exit Function

'2. modify the text file
    '================================
    'THIS DOESN'T WORK for macros-'corrupts' the text
    
    'create a temp file then output the edited file contents
    strTemp = Path & "\" & strMacroName & ".tmp"
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.CreateTextFile(strTemp, True)
    
    'open the original file and for each line replace any matching text
    Set file = objFSO.OpenTextFile(strFileName)
    Do Until file.AtEndOfStream
        CurrentLine = file.ReadLine
        NewLine = Replace(CurrentLine, strFind, strReplace, , , vbTextCompare)
        'write to the new line containing replacements to the temp file
        objFile.WriteLine NewLine
    Loop
    file.Close
    
    objFile.Close
    
    'delete the original file and replace with the temporary file
    objFSO.DeleteFile strFileName, True
    objFSO.MoveFile strTemp, strFileName

'===================
   Previous attempt - also failed
   ' Set objFSO = CreateObject("Scripting.FileSystemObject")
   ' Set objFile = objFSO.OpenTextFile(strFilename, ForReading)

   ' strOldText = objFile.ReadAll

   ' objFile.Close

  '  strNewText = Replace(strOldText, strFind, strReplace)
   '' Debug.Print strNewText

   ' Set objFile = objFSO.OpenTextFile(strFilename, ForWriting)

   ' objFile.WriteLine strNewText

   ' objFile.Close  
'===================  
    

'3. import the text file overwriting the existing macro
    Application.LoadFromText acMacro, strMacroName, strFileName
   ' Application.LoadFromText acMacro, strMacroName, strTemp

'4. delete the text file
    Kill strFileName
    
Exit_Handler:
    Exit Function
    
Err_Handler:
    If Err <> 2128 Then
      'create error message & log
      FormattedMsgBox "Error " & Err.Number & " in ModifyMacroVBA procedure : " & _
          "@" & Err.Description & "      @", vbCritical, "Error modifying macro"
     ' strProc = Application.VBE.ActiveCodePane.CodeModule.ProcOfLine(Application.VBE.ActiveCodePane.TopLine, 0)
    '  PopulateErrorLog
    End If
    Resume Exit_Handler

End Function



Public Function TestModifyMacroVBA()

    ModifyMacroVBA "mcrOpenMainMenuBKP", "MainMenu", "AdminMenu"
    
End Function

Manual editing of the text file works fine but I need to do this using VBA as it forms part of looping through all database objects

If anyone has any experience in doing something similar & can advise, I would be very grateful

For info, the author of the excellent V-Tools addin was also unable to solve this though he used a very different approach
 

Attachments

  • text files before & after.PNG
    text files before & after.PNG
    82.3 KB · Views: 176
Last edited:

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:25
Joined
Feb 28, 2001
Messages
27,175
Macros are an odd lot because they aren't stored quite like other items. Most people avoid the use of macros in more mature versions of their product, which you can do by finding the option to convert macros to VBA. I'm not surprised that the V-Tools author bounced off of the problem. I can't do much with them either.

I only recommend use of macros in a production system for the case where you need to run code based on using Windows Task Scheduler to trigger a macro using a command-line option to active Access on a given DB with the /x:macro-name option. Even there, it is a tricky prospect. All other cases of macros give trouble if you have any errors because macros suck at error handling. As such, they threaten the stability of the product. Oh, there are things you can do. To me (WARNING: OPINION) they are more trouble than they are worth in a product that you want to put your name on.
 

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
Hi Doc Man

I totally agree with your comments about macros & haven't created one for years. In fact I had to create a sample macro in order to test my code!

For me, the only other useful purpose with macros is using an Autokeys macro for keyboard shortcuts
e.g. I use Ctl+Shift+X as a shortcut to run the following function to close all open VBE windows.
Very useful when so many windows are left open you can't find anything.
It also significantly speeds up loading the VBE editor if it doesn't need to load lots of unwanted 'legacy' windows

Code:
Function CloseAllVBEWindows() 'CR v5207

'closes all VBE windows except this one!
'requires library 'Microsoft Visual Basic for Applications Extensibility'

'CR 02/02/2016 - added error handling to fix issue in 64-bit Office

On Error GoTo Err_Handler

Dim vbWin As VBIDE.Window

For Each vbWin In Application.VBE.Windows
     If (vbWin.Type = vbext_wt_CodeWindow Or _
         vbWin.Type = vbext_wt_Designer) And _
         Not vbWin Is Application.VBE.ActiveWindow Then
             vbWin.Close
     End If
 Next
 
Exit_Handler:
    Exit Function

Err_Handler:
    If Err.Number = 424 Then Resume Next 'object required
    MsgBox "Error " & Err.Number & " in CloseAllVBEWindows procedure: " & Err.Description
    Resume Exit_Handler

End Function


In other words the macro part of the search tool is not for myself but for others who have requested it.
I also intend to upload the search tool to the sample databases section in case its of general use.

Context:
I produce open source databases for various schools. In one of them the administrator still likes using macros rather than VBA - god knows why.

He has requested adding macro find & replace to the search tool code
The search tool can already search all database items including macros for selected key words.
 

CJ_London

Super Moderator
Staff member
Local time
Today, 21:25
Joined
Feb 19, 2013
Messages
16,608
I suspect there are some hidden/none printing characters in the exported text file which are not copied across with readline/writeline. Particularly when you see words like 'schemas' and 'interface' split across lines - although I have no idea what a macro looks like under the skin.

Have you tried simply opening the text file, manually change a name and save as different file to see if it imports OK?

Suggest you may need to go to a hex editor to see what is really there
 

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
Hi

Yes it works perfectly if I edit the text file manually then import it back in.
However, in that case, users could of course just edit the macro direct within Access.

As I want the functionality as part of a full database search, I may have to use a hex editor but the effort needed may be 'overkill'
 

CJ_London

Super Moderator
Staff member
Local time
Today, 21:25
Joined
Feb 19, 2013
Messages
16,608

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
Hi CJ

Sorry about delay in replying.

my point was it indicates that something is lost using readline and writeline.

I believe there is binary read/write functionality

... or possibly something is added?
I'm looking into using binary read/write and see how it goes

The other thing to try is of course using InStr combined with Replace
I'm sure it can be done with a bit of tweaking

Many thanks for the web links you gave me as a starting point
 

static

Registered User.
Local time
Today, 21:25
Joined
Nov 2, 2015
Messages
823
I haven't read the post in detail.
I'm assuming if you import the unedited file it works?

Next thing I would try is to open the file and save it without changes.
If that doesn't work, open the re-saved file in a good text editor like Notepad++ and compare the two.
If you still can't see any difference pay close attention to new line characters.
Sometime things just expect cr or lf not crlf.
(not sure if what you are doing would affect new line characters or not...)

SaveAsText is standard text so it shouldn't be too difficult to find the error.

As an aside, are you sure a simple replace will work?

If your macro has an object name PersonID and Person (first + last name text or whatever) and you change Person to PersonName, a simple replace would also change PersonID to PersonNameID.

Normally you would step through each character to determine where each word starts and ends to ensure a proper match.

Maybe off topic. If so, sorry. I'll have a proper look later.

If you could post a database with a simple macro and your code that would help.
 

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
Hi Static

Thanks for having a quick look.
Some comments

1. It works fine if I reimport the text file unedited or if I edit the text file manually
2. I've tried editing the file using VBA using fso / line input & get.
All failed in different ways.
3. I've also tried using InStr to find the position of the required text string
4. Obviously I need to check for issues like Person / PersonID but that's relatively easy to solve
5. The problem as I see it is the exported macro text file 'looks like' standard text but isn't in reality.
Hence the 'gobbledegook' that appears after editing
Not yet tried Notepad++ or a hex editor - that's next on the list

I'm intending to upload the full search tool to the sample databases section soon.
The search part is completely done.
I had hoped to upload after completing the replace part for all database items.
However maybe I'll wrap it up as it is now so others can use it

I'll let you know when I've uploaded it
However, it may be difficult to reduce to 2MB (even when zipped)
 

static

Registered User.
Local time
Today, 21:25
Joined
Nov 2, 2015
Messages
823
Works Ok here. Make sure you save as utf8 as well.

For this I created 2 forms form1 and form2 and macro1 opens one of them.
The replace toggles between the 2 forms.

Code:
Private Sub Command0_Click()

    Const p As String = "C:\macroedit\test.txt"
    Const mac As String = "macro1"

    Application.SaveAsText acMacro, mac, p
    
    s = CreateObject("scripting.filesystemobject").opentextfile(p, 1, False, -1).readall()
    
    If InStr(s, "form1") Then
        oldform = "form1"
        newform = "form2"
    Else
        oldform = "form2"
        newform = "form1"
    End If
    
    s = Replace(s, oldform, newform)
    
    CreateObject("scripting.filesystemobject").opentextfile(p, 2, False, -1).write s
    DoCmd.DeleteObject acMacro, mac
    RefreshDatabaseWindow
    
    Application.LoadFromText acMacro, mac, p
    RefreshDatabaseWindow
    
    DoCmd.RunMacro mac
End Sub
 

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
Hi Static

I really appreciate your help on this
I had tried that line of code you posted but got error 424 - object required
That's why I was trying other ideas from the link

I've modified your code to run from a module as follows but still get the same error on the line shown in red

Code:
Public Function TestStaticCode2()

On Error GoTo Err_Handler

    Const P As String = "C:\Programs\MendipDataSystems\SDA\mcrOpenMainMenuBKP.txt"
    Const mac As String = "mcrOpenMainMenuBKP"

    'Dim S As Object
    Dim oldform As String
    Dim newform As String
    Application.SaveAsText acMacro, mac, P
    
    Dim objFSO As Object
    Dim objFile As Object
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    [COLOR="Red"]Set objFile = objFSO.opentextfile(P, 1, False, -1).readall()[/COLOR] 'Error 424 in this line
       
    'Set S = CreateObject("scripting.filesystemobject").opentextfile(P, 1, False, -1).readall()
    
    If InStr(objFile, "MainMenu") Then
        oldform = "MainMenu"
        newform = "AdminMenu"
    Else
        oldform = "AdminMenu"
        newform = "MainMenu"
    End If
    
    objFile = Replace(objFile, oldform, newform)
    
    CreateObject("scripting.filesystemobject").opentextfile(P, 2, False, -1).Write objFile
    DoCmd.DeleteObject acMacro, mac
    RefreshDatabaseWindow
    
    Application.LoadFromText acMacro, mac, P
    RefreshDatabaseWindow
    
    DoCmd.RunMacro mac
    
Err_Handler:
    MsgBox "Error " & Err.Number & " " & Err.Description
  '  On Error Resume Next
  '  Set objFile = Nothing
  '  Set objFSO = Nothing
  '  On Error GoTo 0
    
End Function

the missing object must be related to
Code:
(P, 1, False, -1).readall()
- need to check out this in full though it might be obvious to you

As you will realise, I use late binding for scripting - I've got too many references in use already!
 

static

Registered User.
Local time
Today, 21:25
Joined
Nov 2, 2015
Messages
823
readall() is a function that returns all of the the text from the file so you assign that to a string variable.

If you want to assign the opened file to an object using SET, drop readall().
 

isladogs

MVP / VIP
Local time
Today, 21:25
Joined
Jan 14, 2017
Messages
18,217
It works for me too now.
Thanks++++ & reputation points added.

You are an absolute star!
 

Users who are viewing this thread

Top Bottom