Use VBA to retrieve and call VBA code from a text file - vba

I am working on a project to process some incoming Outlook messages using rule-triggered VBA code.
However, I don't want to have to manually update the code for each user's inbox any time the code needs to change. So my idea is to put a text file on a shared drive and have the VBA pull that text file down and treat it like code. Essentially, I want to use that text file like a little code library.
I was able to find this link that has gotten me very close to my goal. However, I'm having a few issues with it.
Here is the code I put together. It is attached to the click event of a Rectangle shape I inserted into an Excel file. Eventually, I'll move this over to Outlook, but I'm just doing a basic test with Excel VBA first.
Sub Rectangle1_Click()
On Error GoTo Err_Handler
Dim enviro As String
Dim myFile As String
'Pull code "library" from text file on user's desktop
'This will eventually be changed to reside on a shared drive
enviro = CStr(Environ("USERPROFILE"))
myFile = enviro & "\Desktop\hello_vba.txt"
'If the "Library" module already exists, delete it
For Each a In Modules
If a.Name = "Library" Then
a.Delete
Exit For
End If
Next
'Add a new module
Set m = Application.Modules.Add
'Rename it to "Library"
m.Name = "Library"
'Insert the text from the other file to this new module
m.InsertFile myFile
'Call the hello() subroutine from the retrieved text file
Library.Hello
Exit_Here:
'Cleanup code goes here
Exit Sub
Err_Handler:
MsgBox Err.Description
Resume Exit_Here
Exit Sub
And here is the content of the external text file named "hello_vba.txt":
Sub Hello()
MsgBox "Hello"
End Sub
The first time I run this, using the debugger I can see that it creates the new module and then gets to the line that says:
m.Name = "Library"
And then a window pops up in the debugger that says:
Can't enter break mode at this time
When I click continue on that message, I get an
Object Required
error message. If it run it again, then I get some more error messages but I do eventually get a successful "Hello" message box pop up.
I'm wondering if I may not have "Dim"ed the "a" or "m" variables properly or if there is a problem trying to pull in a text file and immediately treat it like code?
Any ideas?

To run your new code, try using Application.Run instead of Library.Hello
It would be written:
Application.Run("Hello")
Does that work?

Related

How to change default path in save on close prompt?

I'm trying to create a template that automatically changes folder suggested by the save prompt to a specified location. I've managed to get it partially working using the following code (modified from here):
Sub FileSave()
Dim UserSaveDialog As Dialog
Set UserSaveDialog = Dialogs(wdDialogFileSaveAs)
'save changes if doc has been saved previously
If ActiveDocument.Path <> "" Then
ActiveDocument.Save
Exit Sub
End If
With UserSaveDialog
.Name = "C:\Users\david\Downloads"
If .Display Then
UserSaveDialog.Execute
End If
End With
End Sub
Using this code, my macro correctly intercepts the default save behaviour (or Ctrl+S), however it doesn't intercept the save dialog when closing the file. I've tried basically copying this code to a new Sub called Document_BeforeSave, FileExit, FileCloseEx and FileCloseAllEx (yes, I am having difficulty with all the different objects and what they do :) all to no avail.
I'm not sure the same code will even work in this event, but I don't even get any indication that it has failed to work, so it seems I'm using the wrong event.
Turns out I somehow missed AutoClose (MS Docs), which does what I want.

Random File Selector?

It's been years since I've used Visual Basic. I downgraded from 2017 to 2010 (The version I was using while I was in school). I figured VB would be the best way to attempt a solution. (Although I'm sure there are other languages that would do it as well.)
I'm looking to get back into programming. Let me get to the problem.
My friend has an ever growing amount of text documents in a folder, and he wants a program to choose one at random, and open it.
I thought I'd put a TextBox with a Button that would let him open the folder where he stores his files. Then this program would read the number of text files in that folder, and randomly generate a number between one and that number, select, and open the document with its default program (if it's text, notepad; if it's DocX then word.)
I've been sitting at a blinking cursor for 45 minutes. I've gone on YouTube for help with this project.
Any advice, or help you guys can give me? Does this need to be simplified?
That sounds like a reasonable strategy to me.
It might be worth displaying some sort of progress to the user, say by putting the name of current file name being read into the status bar, in case there's a long delay reading the file names due to the large number of files in the folder, and/or a slow-running network drive. If you do this, remember to put a DoEvents into your loop to allow screen updates to display.
There's a separate thread on how to open files in their native handler here.
Hope this helps - good luck!
Option Explicit
Public oFSO As Object
Public arrFiles()
Public lngFiles As Long
Sub Main()
Dim sPath As String
sPath = InputBox("Enter folder path", "Folder path")
' clear starting point
lngFiles = 0
Erase arrFiles
Set oFSO = CreateObject("Scripting.FileSystemObject")
Call recurse(sPath)
Randomize
Dim lngRandomFileNumber As Long
lngRandomFileNumber = CLng(lngFiles * Rnd) + 1
MsgBox "This is random file, that will be opened: " & arrFiles(lngRandomFileNumber)
Call CreateObject("Shell.Application").Open(arrFiles(lngRandomFileNumber))
End Sub
Sub recurse(sPath As String)
Dim oFolder As Object
Dim oSubFolder As Object
Dim oFile As Object
Set oFolder = oFSO.GetFolder(sPath)
'Collect file information
For Each oFile In oFolder.Files
lngFiles = lngFiles + 1
ReDim Preserve arrFiles(lngFiles + 1)
arrFiles(lngFiles) = sPath & "\" & oFile.Name
Next oFile
'looking for all subfolders
For Each oSubFolder In oFolder.SubFolders
'recursive call
Call recurse(oSubFolder.path)
Next oSubFolder
End Sub
You can paste this code in any VBA supporting application (MS Access, MS Excel, MS Word), call VBA editor (Shift + F11) and paste this code. After that press F5 and select Main() function. You'll see prompt to enter folder path, and after that you would get random file path.
I think it should be understandable in practice to see what program do
Updated: #Belladonna mentioned it clearly, to open file in default program.
NB: This code is passes through subfolders also, if you want to exclude subfolders, you should comment the recursive call block in recurce function

How to save Outlook mails as .msg file with categories and other details?

It is easy to save emails in Outlook VBA with MailItem.SaveAs
But I don't see any option to save additional details like i.e. the Author and Categories.
The 3rd party program MessageSave allows to save mails with Categories and Author in .msg format. In Windows Explorer the columns Author and Categories show the same information like in Outlook.
Does anybody know how to save messages using Outlook VBA including these additional information?
I bought MessageSave and it's a good program but they don't allow their save function to be used in VBA. The only workaround is to let MessageSave save messages when they "arrive" in a specific folder. If necessary I can use this function but this is just a workaround.
Here is a sample how the emails saved with MessageSave are shown in Windows Explorer:
here is a process i followed: (win7 64)
web search "windows vba set extended file property"
first hit: StackOverfow 16989882
web search: "DSOFile.OleDocumentProperties"
hit microsoft: The Dsofile.dll files lets you edit Office document properties when you do not have Office installed
https://support.microsoft.com/en-us/help/224351/the-dsofile.dll-files-lets-you-edit-office-document-properties-when-yo
that is not a typo ... it ends in "when-yo"
download: DsoFileSetup_KB224351_x86.exe
open DsoFileSetup_KB224351_x86.exe using 7-zip program (from 7-zip.org)
copy dsofile.dll from DsoFileSetup_KB224351_x86.exe (using 7-zip) into a folder desktop (named "testFiles" in this example) (this could be anywhere ... maybe windows system32 or syswow64 ... i only tried on desktop )
open command prompt window as administrator
navigate to folder that contains dsofile.dll
execute following: regsvr32 dsofile.dll
should receive success confirmation
start outlook ... vba editor ... tools ... references
and find "DSO OLE Document Properties Reader 2.1" and check the box on left
back to vba editor ... create new module
paste in the following: (this is just a minimal test script)
Sub extendedProperties()
Dim objFile As OleDocumentProperties
Set objFile = CreateObject("DSOFile.OleDocumentProperties")
objFile.Open ("C:\Users\js\Desktop\testFiles\myMessage.msg") ' adjust to match your system
objFile.SummaryProperties.Subject = "My Subject"
objFile.Save
Set objFile = Nothing
End Sub
copy (drag&drop) an email "myMessage" from outlook to folder (on desktop in this example)
right-click on folder column header ... click on more ... find "subject" ...
click checkbox
ran script
subject column should contain "My Subject" next to myMessage.msg (or whatever your message is named)
there may be a simpler way ... maybe windows PowerShell has a command that could be called from vba
here is a more usable script
it has no error checking
no check for duplicate message names
no check for illegal filenames (except for ":" character)
just select a bunch of emails in any outlook folder and run this
' make sure you have a reference to "DSO OLE Document Properties Reader"
Sub extendedProperties()
Dim msg As mailItem
Dim objFile As OleDocumentProperties
' Set objFile = CreateObject("DSOFile.OleDocumentProperties")
Set objFile = New OleDocumentProperties
Dim fileName As String
Dim subjectText As String
' !!!!!!!! select a bunch of messages before running this !!!!!!!!
For Each msg In ActiveExplorer.Selection
subjectText = Replace(msg.Subject, ":", "_") ' get rid of illegal file name character (there are others)
' adjust the destination folder for your liking
fileName = "C:\Users\js\Desktop\testFiles\" & subjectText & ".msg"
Debug.Print fileName
msg.SaveAs fileName
objFile.Open fileName
objFile.SummaryProperties.Subject = "My Subject"
'objFile.Save
objFile.Close True ' save and close !!!!! duplicate filenames get overwritten !!!!!
' stop ' uncomment this line and the code will stop. press F5 to run, F8 to single-step
Next msg
Set msg = Nothing
Set objFile = Nothing
End Sub

Access autocad object properties without opening it by VBA

I have been using folder browser for VBA, I could paste the code of it, but bottom line is that I get returned file name as a string.
Is there any way to access drawing properties (i.e number of layouts) without open?
Public Sub TestFileDialog()
dwgname = FileBrowseOpen("C:", "*", ".dwg", 1) 'dwgname is typeof string
End Sub
Its only the first step (use of FileBrowseOpen function is shown, but also i can use FolderBrowse and collect all .dwg inside of folder),actually i had in mind to batch export all layouts of selected .dwgs to currenty open one. Is there any chance for that?
To effectively read a .dwg file you'll need to open AutoCAD, otherwise the information is not accessible. Some properties may be, such as author, but not number of layouts...
But you can use AutoCAD Console (accoreconsole.exe) to run a headless AutoCAD and use APIs to read any information you need. This is really fast for reading lot's of files and the user will not see it running (but it needs to be installed anyway).
http://aucache.autodesk.com/au2012/sessionsFiles/3338/3323/handout_3338_CP3338-Handout.pdf
you could stay in VBA and use ObjectDBX
it leads to a very similar approach as accoreconsole.exe on in .NET does, i.e you won't see any drawing open in UI since it works on the database itself
It requires adding library reference (Tools->References) to "AutoCAD/ObjectDBX Common XX.Y Type Library", where "XX.Y" is "19.0" for AutoCAD 2014
a minimal functioning code is
Sub main()
Dim myAxDbDoc As AxDbDocument
Dim FullFileName As String
FullFileName = "C:\..\mydrawing.dwg" '<== put here the full name of the file to be opened
Set myAxDbDoc = AxDb_SetDrawing(FullFileName)
MsgBox myAxDbDoc.Layers.Count
End Sub
Function AxDb_SetDrawing(FullFileName As String) As AxDbDocument
Dim DBXDoc As AxDbDocument
Set DBXDoc = Application.GetInterfaceObject("ObjectDBX.AxDbDocument.19") '<== place correct AutoCAD version numeber ("19" works for AutoCAD 2014)
On Error Resume Next
DBXDoc.Open FullFileName
If Err <> 0 Then
MsgBox "Couldn't open" & vbCrLf & vbCrLf & FullFileName, vbOKOnly + vbCritical, "AxDB_SetDrawing"
Else
Set AxDb_SetDrawing = DBXDoc
End If
On Error GoTo 0
End Function
Still, you must have one AutoCAD session running from which make this sub run! But you should have it since talked about "currently open" drawing

Save and open vbs script programmatically

I have searched a lot on this method but no way
I want my VB Program as once opened it gives user some messages using vbs script file
so once program is opened the vbs file saved in temp and be ran to say the message to user
i have used this code with the vbs file imported to Resources
but unfortunately it works only with one line script not script with many lines
Dim Variable As String = Environ("temp") & "Message.vbs"
If Not System.IO.File.Exists(Variable) Then
System.IO.File.WriteAllBytes(Variable, My.Resources.Message)
End If
Process.Start(Variable)
i have used this second code as well but it gives error in compilation because of Save
Dim Variable As String = Environ("temp") & "\Message.vbs"
IO.File.Delete(Variable)
My.Resources.Access.Save(Variable )
Shell("explorer" & Variable )
Please Help me with this code, i have spend a long time to get solution but nothing
Thanks in advance
I made some minor changes to your first example and created a simple vbs file. The first change is because a VB script file is a just a text file I added the resource as a text file and changed the WriteAllBytes statement to WriteAllText. Second, because WriteAllText, (and WriteAllBytes also), overwrite the file if it already exists I eliminated the If statement. You may still want that, in case you really do not want to overwrite the file. Finally, I added a backslash to the beginning of the filename to create the file in the temp folder. Otherwise you get drive:\temppath\tempMessage.vbs". This executed my simple script file just fine.
.NET code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Variable As String = Environ("temp") & "\Message.vbs"
System.IO.File.WriteAllText(Variable, My.Resources.hello)
Process.Start(Variable)
End Sub
My VBScript file:
MsgBox "Hello World", 0,"Messagebox #1"
MsgBox "My name is Fred", 0,"Messagebox #2"
MsgBox "I have to go now", 0,"Messagebox #3"