Reputation: 4842
I have an Excel .xlam file that adds a button in the ribbon to do the following:
I save the source text this way because it can be password protected and I don't need to drag another file around everywhere that the .xlam file goes. The source text is saved in a separate module called "Source" that looks something like this (Thanks VBA for not having Heredocs):
'Source Module
Public Function GetSource() As String
Dim s As String
s = ""
s = s & "This is the first line of my source text" & vbCrLf
s = s & "This is a parameter {par1}" & vbCrLf
s = s & "This is another line" & vbCrLf
GetSource = s
End Function
The function works fine. My problem is if I want to update the source text, I now have to manually do that in the .xlam file. What I would like to do is build something like a Sub ImportSource()
in another module that will parse some file, rebuild the "Source" Module programatically, then replace that Module with my calculated source code. What I don't know is if/how to replace the source code of a module with some value in a string variable.
It's like metaprogramming at its very worst and philosophically I'm against doing this down to my very core. Practically, however, I would like to know if and how to do it.
Upvotes: 4
Views: 20301
Reputation: 17475
As @brettdj already pointed out with his link to cpearson.com/excel/vbe.aspx , you can programmatically change to code of a VBA module using the VBA Extensibility library! To use it, select the library in the VBA editor Tools->References. Note that you need to also change the options in your Trust center and select: Excel Options->Trust Center->Trust Center Settings->Macro Settings->Trust access to the VBA project object model
Then something like the following code should do the job:
Private mCodeMod As VBIDE.CodeModule Sub UpdateModule() Const cStrModuleName As String = "Source" Dim VBProj As VBIDE.VBProject Dim VBComp As VBIDE.VBComponent Set VBProj = Workbooks("___YourWorkbook__").VBProject 'Delete the module VBProj.VBComponents.Remove VBProj.VBComponents(cStrModuleName) 'Add module Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule) VBComp.Name = cStrModuleName Set mCodeMod = VBComp.CodeModule 'Add procedure header and start InsertLine "Public Function GetSource() As String" InsertLine "Dim s As String", 1 InsertLine "" 'Add text InsertText ThisWorkbook.Worksheets("Sourcetext") _ .Range("___YourRange___") 'Finalize procedure InsertLine "GetSource = s", 1 InsertLine "End Function" End Sub Private Sub InsertLine(strLine As String, _ Optional IndentationLevel As Integer = 0) mCodeMod.InsertLines _ mCodeMod.CountOfLines + 1, _ Space(IndentationLevel * 4) & strLine End Sub Private Sub InsertText(rngSource As Range) Dim rng As Range Dim strCell As String, strText As String Dim i As Integer Const cLineLength = 60 For Each rng In rngSource.Cells strCell = rng.Value For i = 0 To Len(strCell) \ cLineLength strText = Mid(strCell, i * cLineLength, cLineLength) strText = Replace(strText, """", """""") InsertLine "s = s & """ & strText & """", 1 Next i Next rng End Sub
Upvotes: 5
Reputation: 46365
I realize now that what you really want to do is store some values in your document in a way that is accessible to your VBA, but that is not readable to a user of the spreadsheet. Following Charles Williams's suggestion to store the value in a named range in a worksheet, and addressing your concern that you don't want the user to have access to the values, you would have to encrypt the string...
The "proper way" to do this is described in this article - but it's quite a bit of work.
A much shorter routine is found here. It just uses simple XOR encryption with a hard coded key - but it should be enough for "most purposes". The key would be "hidden" in your macro, and therefore not accessible to prying eyes (well, not easily).
Now you can use this function, let's call it encrypt(string)
, to convert your string to a value in the spreadsheet:
range("mySecretCell").value = encrypt("The lazy dog jumped over the fox")
and when you need to use it, you use
Public Function GetSource()
GetSource = decrypt(Range("mySecretCell").value)
End Function
If you use the XOR
version (second link), encrypt
and decrypt
would be the same function...
Does that meet your needs better?
Upvotes: 5
Reputation: 46365
You can "export" and "import" .bas files programmatically. To do what you are asking, that would have to be the approach. I don't believe it's possible to modify the code in memory. See this article
Upvotes: 1