VBA execute code in string - vba

I am trying to execute vba code that is inside a string WITHOUT writting the code in a temp file.
For exemple :
Dim code As String
code = "n = 0 : e_i_e = 0 : For e_i_e = 0 To 100 : n+=1 : Next"
I have tried Eval, Evaluate, Run, executeGlobal and adding a new module with
Set VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = "NewModule"
Set VBCodeMod = ThisWorkbook.VBProject.VBComponents("NewModule").CodeModule
With VBCodeMod
LineNum = .CountOfLines + 1
.InsertLines LineNum, _
"Sub MyNewProcedure()" & Chr(13) & _
code & Chr(13) & _
"End Sub"
End With
Application.Run "MyNewProcedure"
but all of these are returning errors ='(.
Thank you !

You cannot break in code that's been generated after you've compiled your project, so you need to make sure you build that dynamic module with valid, compilable code.
You know before you hit that F5 button that your code is going to look like this:
Sub MyNewProcedure()
n = 0 : e_i_e = 0 : For e_i_e = 0 To 100 : n+=1 : Next
End Sub
Why not just take that snippet and paste it somewhere and see what the VBE complains about?
Wow. See, this is why cramming half a dozen instructions on the same line of code is a bad idea - if it was one instruction per line you wouldn't be wondering which one is broken.
As was already mentioned, n += 1 is not VBA syntax (it's not specifically C# syntax either); incrementing a value in VBA needs to access the current value, so n = n + 1.
It's not clear where n and e_i_e are coming from. If both are locals, then your procedure accomplishes essentially nothing. If n is declared outside MyNewProcedure, then you should consider passing it as a ByRef parameter, or better, leaving it out completely and making a Function with the result of which the calling code assigns n to.
Sub MyNewProcedure(ByRef n As Long)
Dim i As Long
For i = 0 To 100
n = i
Next
End Sub
Which boils down to:
Function MyNewFunction() As Long
MyNewFunction = 100
End Function
Which makes me wonder what the heck you're trying to accomplish.
If there is a bug in your generated code, you're going to have to debug it in a string, because the VBE's debugger won't let you break on generated code - this means it's crucially important that you generate code in a readable and maintainable way. There's currently nowhere in your code where you have the actual full generated code in a clear string - it's concatenated inline inside the InsertLines call.
Consider:
Dim code As String
code = "'Option Explicit" & vbNewLine & _
"Public Sub MyNewProcedure()" & vbNewLine & _
" n = 0" & vbNewLine & _
" e_i_e = 0" & vbNewLine & _
" For e_i_e = 0 To 100" & vbNewLine & _
" n = n + 1 ' fixed from n += 1" & vbNewLine & _
" Next" & vbNewLine & _
"End Sub" & vbNewLine
'...
'Debug.Print code
.InsertLines LineNum, code
It's much easier to get the full code back while debugging, and much easier to review and fix as well. Note that there's a limit to how many line continuations you can chain though.

Your code is c# addition, it needs to be n=n+1

You can create a module and populate it with a sub from a string. In fact one of the way developers place their vba code into a repository is to do just that: extract the code from modules as strings and then read them back in from whatever version control software they're using.
Here's a full example to do what you're looking to do (assuming you want your sub in a new separate module):
Sub make_module_sub_and_run():
Dim strMacro As String
Dim myModule As VBComponent
Set myModule = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)
strMacro = "Public Sub exampleSub()" & vbCrLf & _
" n=0" & vbCrLf & _
" For e_i_e = 0 to 100:" & vbCrLf & _
" n=n+1" & vbCrLf & _
" Next" & vbCrLf & _
" MsgBox(""Hello World. Oh and n="" & n)" & vbCrLf & _
"End Sub"
myModule.CodeModule.AddFromString strMacro
Application.Run myModule.Name & ".exampleSub"
End Sub
Note that what should happen as you type "vbext_ct_StdModule" is that excel intellisense will note that this is missing and will ask whether you want to load it in - which you, of course, do.
Also note that I've deliberately prefixed the sub with the module name when running it - otherwise if you were to run it repeatedly you'll create new modules with a sub of the same name and excel wouldn't know which one to run.

Related

Find mailfolder in Outlook with Redemption

I try to find a folder in an Outlook account (I use Multiple accounts) using VBA and Redemption by using the FIND method but I cannot get it to work. On the Redemption webpage there is a reference made to an example and this may help but unfortunately the example isn't there.
Here's my code so far:
Public Function FindFolderRDO(strCrit As String) As String
If Not TempVars![appdebug] Then On Error GoTo Err_Proc
Dim objRdoSession As Redemption.RDOSession
Dim objRdoFolder As RDOFolder
Dim strFoundFolder As String
Dim objFoundFolder As RDOFolder
Dim strFolderName As String
Set objRdoSession = CreateObject("Redemption.RDOSession")
objRdoSession.Logon
objRdoSession.MAPIOBJECT = Outlook.Session.MAPIOBJECT
strFolderName = "\\[mailbox name]\[foldername]\[foldername]" 'actual names removed
Set objRdoFolder = objRdoSession.GetFolderFromPath(strFolderName)
Debug.Print objRdoFolder.Parent.Name 'Prints the folder name
Set objFoundFolder = objRdoFolder.Folders.Find("LIKE 'strCrit%' ") 'Does not work
Debug.Print objFoundFolder.Name
strFoundFOlder = objRdoFolder.Folders.Find("LIKE 'strCrit%' ") 'Does not work
Debug.Print strFoundFOlder
Exit_Proc:
On Error Resume Next
Set objRdoFolder = Nothing
Set objRdoSession = Nothing
Set objFoundFolder = Nothing
Exit Function
Err_Proc:
Select Case Err.Number
Case Else
MsgBox "Error: " & CStr(Err.Number) & vbCrLf & _
"Desc: " & Err.Description & vbCrLf & vbCrLf & _
"Source: " & Err.Source & vbCrLf & _
"Library: " & Application.CurrentProject.Name & vbCrLf & _
"Module: Mod_RDO" & vbCrLf & _
"Function: FindFolderRDO" & vbCrLf, _
vbCritical, "Error"
End Select
Resume Exit_Proc
End Function
Purpose of this function is to find a subfolder (can be up to 4 dimensions deep) having an unique case number of 6 numbers (for example "200332") on the first 6 positions. This function should provide NULL if not found or the full path and the name of the found folder.
I can create the full path with a seperate function (calling the parent folder until top level) but maybe there is a procedure in Redemption such as "fullpath" which I overlooked.
Eventually I want to use this function to delete, move or rename the mailbox folder.
My main question is how to use the "Find(Filter)" method. But any reply on the full path is welcome as well.
Thx! Art.
You are you trying to find a suborder with a name that starts with "strCrit"?
You are almost there:
Set objFoundFolder = objRdoFolder.Folders.Find("Name LIKE 'strCrit%' ")

How to create a comment box with time and date stamp in Access using VBA

Completely new to VBA and need help with detailed instructions (dummy version for me).
I have a table with various columns and the following columns, specifically:
ReviewerComments
NewComment
I created a form with both of these fields and need to create an Append Comment button that moves the text from NewComment field and appends it to ReviewerComment field and time/date stamps the comments as they are added. I named this button cmdAppendComment.
I had seen someone else post something and I tried, but as I am completely new to this I know I messed it up. Any help is greatly appreciated.
This is what the VBA code looks like right now:
Private Sub cmdAppendComment_Click()
If (IsNull(NewComment.Value)) Then
MsgBox ("Please provide a comment before clicking" & _
"on the Append Comment button.")
Exit Sub
End If
If (IsNull(ReviewerComments.Value)) Then
ReviewerComments.Value = NewComment.Value & " ~ " & _
VBA.DateTime.Date & " ~ " & VBA.DateTime.Time
Else
ReviewerComments.Value = ReviewerComments.Value & _
vbNewLine & vbNewLine & _
NewComment.Value & " ~ " & _
VBA.DateTime.Date & " ~ " & VBA.DateTime.Time
End If
NewComment.Value = ""
End Sub
I have some suggestions for your code:
1. Do not check if a textbox is null but how many characters your textbox has. You should always do it that way because otherwise you tend to get errors.
If (len(Me.NewComment.Value & "") > 0) Then
MsgBox ("Please provide a comment before clicking" & _
"on the Append Comment button.")
Exit Sub
End If
Here you check the length of the string in your textbox. You need to append "" because otherwise you tend to get null-errors or something similar.
2. You forgot to reference the objects in your form correctly. You have your form and in that form you put your textboxes and also your VBA-code. Your can reference all your objects with "Me.[FormObjects]".
The compiler complains that "NewComment.Value" or "ReviewerComment.Value" is not initialized or in other words not dimensioned. With correct reference this should stop.
Private Sub cmdAppendComment_Click()
If (len(Me.NewComment.Value & "") > 0) Then
MsgBox ("Please provide a comment before clicking" & _
"on the Append Comment button.")
Exit Sub
End If
If (IsNull(Me.ReviewerComments.Value)) Then
Me.ReviewerComments.Value = Me.NewComment.Value & " ~ " & _
VBA.DateTime.Date & " ~ " & VBA.DateTime.Time
Else
Me.ReviewerComments.Value = Me.ReviewerComments.Value & _
vbNewLine & vbNewLine & _
Me.NewComment.Value & " ~ " & _
VBA.DateTime.Date & " ~ " & VBA.DateTime.Time
End If
Me.NewComment.Value = ""
End Sub

Build tools in Excel which do not need an end user installation

Is it possible to build tools/add-in's in Microsoft Excel which do not need an end user installation.
For example we use VBA to build excel based tools but the language is very basic. Are there any other technologies similar to VBA or VSTO ..?
Thanks,
Jayanth
No. (although Admins can do it for users) But all the user has to do is double click something (excel sheet, word document, batch file, vbsfile, jscript, exe (type iexpress in Start - Run).
It's called Visual Basic so yes it is Basic. But the language is simple to write and is anything but basic in capabilities. It can do anything that any other language can do. With COM it can do in 10 lines that takes a C programmer hundreds. Anything any other language can do so can VB/VBA, usually in far less lines.
Perhaps you should think about your questions.
Number 1 is a favourite of those wanting to write viruses, that's why it's impossible to install code without someone's permission. Depending on circumstances, it may be against the law.
Number 2, I suspect you really don't know how to use VBA. Remember there is VBA and Excel and they are separate. If you've been programming Excel you probably haven't learnt much VBA, more likely you've learnt Excel's object model.
Remember VB6 and VBA is same language (and vbscript is pasteable into both VB6/VBA) hosted differently. If looking for solutions look at VB6/VBA/VBScript. It's a cinch to write a basic spreadsheet in VBA/VB6/VBScript.
VB6/VBA are easy COM programs and their are thousands of objects to play with. Can call API calls so can do anything.
Here's a program that randomises lines in a text file. It is VBScript and adodb (the main windows database object). It creates a database in memory, adds a random number and the line of text, sorts it, writes the database out.
Sub Randomise
Randomize
Set rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append "RandomNumber", 4
.Fields.Append "Txt", 201, 5000
.Open
Do Until Inp.AtEndOfStream
.AddNew
.Fields("RandomNumber").value = Rnd() * 10000
.Fields("Txt").value = Inp.readline
.UpDate
Loop
.Sort = "RandomNumber"
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
End Sub
Or a script to speak lines of text.
Sub Speak
Set objVoice = CreateObject("SAPI.SpVoice")
Do Until Inp.AtEndOfStream
Line=Inp.readline
outp.writeline Line
objVoice.Speak Line
Loop
End Sub
Or perhaps you would like to add a macro language to your projects. This runs a vbs script specified on a command line against every line of of a file. This includes syntax checking etc,
Sub VBSCmd
RawScript = Arg(1)
'Remove ^ from quoting command line and replace : with vbcrlf so get line number if error
Script = Replace(RawScript, "^", "")
Script = Replace(Script, "'", chr(34))
Script = Replace(Script, ":", vbcrlf)
'Building the script with predefined statements and the user's code
Script = "Dim gU" & vbcrlf & "Dim gdU" & vbcrlf & "Set gdU = CreateObject(" & chr(34) & "Scripting.Dictionary" & chr(34) & ")" & vbcrlf & "Function UF(L, LC)" & vbcrlf & "Set greU = New RegExp" & vbcrlf & "On Error Resume Next" & vbcrlf & Script & vbcrlf & "End Function" & vbcrlf
'Testing the script for syntax errors
On Error Resume Next
set ScriptControl1 = wscript.createObject("MSScriptControl.ScriptControl",SC)
With ScriptControl1
.Language = "VBScript"
.UseSafeSubset = False
.AllowUI = True
.AddCode Script
End With
With ScriptControl1.Error
If .number <> 0 then
Outp.WriteBlankLines(1)
Outp.WriteLine "User function syntax error"
Outp.WriteLine "=========================="
Outp.WriteBlankLines(1)
Outp.Write NumberScript(Script)
Outp.WriteBlankLines(2)
Outp.WriteLine "Error " & .number & " " & .description
Outp.WriteLine "Line " & .line & " " & "Col " & .column
Exit Sub
End If
End With
ExecuteGlobal(Script)
'Remove the first line as the parameters are the first line
'Line=Inp.readline
Do Until Inp.AtEndOfStream
Line=Inp.readline
LineCount = Inp.Line
temp = UF(Line, LineCount)
If err.number <> 0 then
outp.writeline ""
outp.writeline ""
outp.writeline "User function runtime error"
outp.writeline "==========================="
Outp.WriteBlankLines(1)
Outp.Write NumberScript(Script)
Outp.WriteBlankLines(2)
Outp.WriteLine "Error " & err.number & " " & err.description
Outp.WriteLine "Source " & err.source
Outp.WriteLine "Line number and column not available for runtime errors"
wscript.quit
End If
outp.writeline temp
Loop
End Sub
And a change of pace from objects to API calls.
This creates a Richtext control that was released after VB6 so isn't built in.
Ret = LoadLibrary("c:\windows\system32\MSFTEDIT.dll")
If Ret = 0 Then MsgBox "Load Lib " & Err.LastDllError
Flags = WS_CHILD + WS_HSCROLL + WS_VSCROLL + WS_VISIBLE + ES_MULTILINE + ES_AUTOHSCROLL + ES_AUTOVSCROLL + ES_NOHIDESEL + ES_WANTRETURN
Dim barray() As Byte
barray = "RICHEDIT50W" & vbNullChar
gRtfHwnd = CreateWindowEx(WS_EX_ACCEPTFILES + WS_EX_CLIENTEDGE, barray(0), "", Flags, 0, 0, ScaleX(Me.ScaleWidth, vbTwips, vbPixels), ScaleY(Me.ScaleHeight, vbTwips, vbPixels), Me.hWnd, vbNull, App.hInstance, vbNull)
Ret = SendMessageByVal(gRtfHwnd, EM_SETTEXTMODE, TM_MULTILEVELUNDO + TM_PLAINTEXT + TM_MULTICODEPAGE, 0)
If GetTextMode(gRtfHwnd) <> 41 Then MsgBox "get Text mode = " & GetTextMode(gRtfHwnd)
Ret = SendMessageByVal(gRtfHwnd, EM_SETEDITSTYLE, SES_ALLOWBEEPS + SES_USECRLF, SES_ALLOWBEEPS + SES_USECRLF)
Ret = SendMessageByVal(gRtfHwnd, EM_SETLANGOPTIONS, IMF_None, IMF_None)
If GetTextMode(gRtfHwnd) <> 41 Then MsgBox "get Text mode (2) = " & GetTextMode(gRtfHwnd)
Ret = SendMessageByVal(gRtfHwnd, EM_SETTYPOGRAPHYOPTIONS, TO_None, TO_None)
'Below is the default anyway with CreateWin flags spec above
Ret = SendMessageByVal(gRtfHwnd, EM_SETOPTIONS, ECO_AUTOHSCROLL + ECO_AUTOVSCROLL + ECO_NOHIDESEL + ECO_WANTRETURN, ECOOP_OR)
SetFocusAPI gRtfHwnd
Me.Show
Dim ParaFormat As ITextPara
Dim FontFormat As ITextFont
Ret = SendMessageAny(gRtfHwnd, EM_GETOLEINTERFACE, 0, TomObj)

error 1004 when trying to set cell value to value of string

I've seen this question a few times but haven't found a solution that is applicable to my situation, so here it goes:
I have a fairly complicated formula that I want to insert into a cell (complicated as in it's a pain in the butt to follow). I have the components set up in variables, and running the sub give me the correct final variable name in the locals window, however when I try to set the cell to the formula I get a '1004: application-defined or object defined error'. the output should look like this:
Cell A1: =BDS("0","pg_segment","dir = h", "number_of_periods = -3")
However it returns nothing.
I have tried the following: setting the final variable (cmdstr0) to an integer-- this works. As a string ("asdf") also works. Altering the value directly (.value = "string") works. The only thing that doesn't work is when VBA builds the formula itself to insert into the string. Here's the code, and thank you:
sub populate_revenues()
'field = bloomberg field to look up
'direction = output direction (horizontal/vertical)
'geoverride = override to display only geo or product segments
'periods = numbe of periods to display
'cmdstr = the string to be output that will download the data
Dim field As String
Dim direction As String
Dim geoverride As String
Dim periods As String
Dim cmdstr3 As String
Dim cmdstr2 As String
Dim cmdstr1 As String
Dim cmdstr0 As Variant
Let cmdstr2 = 0
Let field = Worksheets("output").Cells(1, 3).Value
Let direction = "Dir = " & Worksheets("output").Cells(1, 5).Value
Let geoverride = " product_geo_override = " & Worksheets("output").Cells(1, 7).Value
Let periods = " number_of_periods = " & Worksheets("output").Cells(2, 3).Value
Let cmdstr1 = "=BDS(" & Chr(34)
Let cmdstr3 = Chr(34) & "," & Chr(34) & field & ", " & Chr(34) _
& direction & Chr(34) & ", " & Chr(34) & geoverride & Chr(34) & ", " & Chr(34) & periods & Chr(34) & ")"
Let cmdstr0 = cmdstr1 & cmdstr2 & cmdstr3
'Let cmdstr0 = 1
Let Worksheets("Sheet2").Cells(10, 1).Value = cmdstr0
End Sub
Also, can anyone please tell me if there's a faster way to format as code than hitting space-bar four times every line?
As you know, you must double-up on the double quotes. So if you want:
=COUNTIF(A1:A10,"apples")
you must use:
Sub demo()
Range("B9").Formula = "=COUNTIF(A1:A10,""apples"")"
End Sub
I, personally, have a lot of trouble with this. What I do to debug is to place an apostrophe at the start of the formula:
Sub demo()
Range("B9").Formula = "'=COUNTIF(A1:A10,""apples"")"
End Sub
This allows me to "see" the text of the formula and fix problems.
B.T.W
To create a code block, hi-light the code and use the paired braces {}

VBA - How do I send new line command (\n) or tab command (\t) to a textbox.textrange.text of a PowerPointS Shape

SlideNumber = 1
Set oPPTSlide = oPPTFile.Slides(SlideNumber)
For y = 1 To oPPTSlide.Shapes.Count
MsgBox oPPTSlide.Shapes(y).Name
Next
With oPPTSlide.Shapes("Title 1")
.TextFrame.TextRange.Text = _
"Operations Monthly Report\n" & _
"April " & _
"2014"
End With
This is the code I have now. The "\n" does cause the text-box I am editing to start a new line. Is it possible? The code, in its context, is working perfectly. The exact text is sent to the text-box though, not two lines of text.
There is no "\n" in Vba instead you should use VbNewLine or VbCrLf or Vblf
Replace this
SlideNumber = 1
Set oPPTSlide = oPPTFile.Slides(SlideNumber)
For y = 1 To oPPTSlide.Shapes.Count
MsgBox oPPTSlide.Shapes(y).Name
Next
With oPPTSlide.Shapes("Title 1")
.TextFrame.TextRange.Text = _
"Operations Monthly Report" & VbCrLf & _
"April " & _
"2014"
End With
I had the problem where the vbNewLine didn't work in the UserForm, but I fixed it by checking the textBox properties and making sure multi-line is true. Give that a try.