VBA monitor folder for new files - vba

So I'm trying to write a VBA program that will monitor a folder for new files and then do stuff with them. I've found some promising examples on using the WMI api:
Receive notification of file creation in VBA without polling
http://www.mrexcel.com/forum/excel-questions/211547-monitor-new-files-folder.html
https://blogs.technet.microsoft.com/heyscriptingguy/2004/10/11/how-can-i-automatically-run-a-script-any-time-a-file-is-added-to-a-folder/
But here's the thing: It seems like the tack everyone takes with these examples is to wire the VBA into an Excel spreadsheet as a macro. People treat Excel as a poor-man's programming environment. Fair enough. The problem is, I need this to run when the user is closed out out this magic excel file with the macro.
Something tells me I need to make a full windows application in visual studio with VB6.0 or C# and run the application in the background as some kind of a scheduled task. Is that the right path to take or is there something simple that I'm missing in these Excel/VBA tutorials?
(Apologies for the generality of the question. I know that the community appreciates specific questions.)

VBA and VBScript is similar. For WMI pretty much the same. Here are three scripts. You can also wire up WMI with event handlers so you can have multiple events rather than one as shown here.
VB6 is VBA that can be compiled into an exe. VB6 hosts the VBA language as does Office.
InstanceCreationEvent
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=""c:\\\\scripts""'")
Do
Set objLatestEvent = colMonitoredEvents.NextEvent
Wscript.Echo objLatestEvent.TargetInstance.PartComponent
Loop
InstanceModificationEvent
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=""c:\\\\scripts""'")
Do
Set objLatestEvent = colMonitoredEvents.NextEvent
Wscript.Echo objLatestEvent.TargetInstance.PartComponent
Loop
InstanceDeletionEvent
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceDeletionEvent WITHIN 10 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=""c:\\\\scripts""'")
Do
Set objLatestEvent = colMonitoredEvents.NextEvent
Wscript.Echo objLatestEvent.TargetInstance.PartComponent
Loop

I don't think Excel is a good solution for this kind of need. What about using VB.NET to do the work?
http://www.dreamincode.net/forums/topic/150149-using-filesystemwatcher-in-vbnet/
Yes, it's overkill, but if you get into it, you'll find all kinds of other really cool things that you can do with VB.NET. I love working with Excel, but I'm really a huge proponent of using the right tool for the job.

Ok- I found a solution for this. It's not pretty, but you can copy the blurb of code found at this tutorial:
https://msdn.microsoft.com/en-us/library/aa383665(v=VS.85).aspx
into an excel macro and it will work almost without modification, scheduling a task which will occur even after excel has closed down. The solution is to use VBA to grab an instance of the task scheduler
Set service = CreateObject("Schedule.Service")
call service.Connect()
And then perform all of the verbose configurations on that object to schedule the task. From there it's only a skip hop and a jump to write an other macro that will actually do the deed on the folder.

Related

Close MS Project using VSTO

I have a VSTO on MS Project. I use VB.NET. What I need is when I press the button I created on the ribbon, it will perform some codes which will update the info of some task, however, I would need to close the MS Project automatically. I tried application.FileCloseEx(), but it only closes the file, the MS Project is still loaded. I need similar to clicking the x button of the window.
Thanks,
Gilbert
If your MS Project application object is represented by "appMSProject" then it's as simple as:
appMSProject.Quit
OR say in a macro running under Project:
Application.Quit
Here's how I do it in VBA from Excel or Access. As far as I can tell the objects & methods are the same in VB.NET. Bottom line is that I create an instance of the MS Project object which starts the app & opens a file, execute some work, close the file, then destroy the MS Project object by setting it to Nothing. That has the effect of closing the app. You can also use "appMSProject.Quit" followed by setting it to Nothing. Frankly the 2nd option looks more orderly & easier to understand in code. Anyway, here's a sample of the way I do it:
Dim appMSProject As MSProject.Application
Dim prjPrj As MSProject.Project
Dim strPrjFile As String
strPrjFile = "C:\where_is_my_file\file_name.mpp"
Set appMSProject = New MSProject.Application
appMSProject.FileOpenEx Name:=strPrjFile
Set prjPrj = appMSProject.ActiveProject
'''Do something in here with the prjPrj
'Close the file, in my case w/o saving
appMSProject.FileCloseEx pjDoNotSave
'Destroy the objects
Set prjPrj = Nothing
Set appMSProject = Nothing
FYI - In this example I'm doing background work so I don't show the app. I also use "early binding".
Here's an MSDN example that does show the app with more info on early -vs- late binding - https://msdn.microsoft.com/en-us/library/office/ff865152.aspx

Access not exiting

Recently, my Access .mdb database started intermittently not allowing Access (both Access 2003 and 2007) to quit. If I quit (whether by pressing the X button or from the menu, then it closes the database and appears to exit Access, as well, but then it suddenly reappears (without any database open). The only way for me to exit at that point is from the task manager.
There are two significant changes that I did recently that might be related. 1) I started using the WinSCP .Net assembly to access an ftp server, which I had to install and register for COM from the instructions here. 2) I started using ODBC, first as a linked table, and then from VBA ADO code (see this). I doubt that this second change is causing this problem because I've had the problem both when I was using the linked tables and when with ADO.
This doesn't happen every time I open the database, and I haven't noticed a pattern. What could be causing this strange problem?
Edit - I found the root of the problem. By breaking my ftp download code at various points and seeing whether it will exit, I narrowed it down to the following:
Dim PDFFolders As Recordset
Set PDFFolders = CurrentDb.OpenRecordset("PDFFolders")
Dim syncOptions As TransferOptions
Set syncOptions = New TransferOptions
syncOptions.filemask = "*/*.pdf"
On Error Resume Next 'In case it doesn't exist
Do While Not PDFFolders.EOF
sess.SynchronizeDirectories SynchronizationMode_Local, info!RTFFolder, _
info!BasePDFFolder & "/" & PDFFolders!Name, False, , , _
syncOptions
PDFFolders.MoveNext
Loop
PDFFolders.Close
Set syncOptions = Nothing
Set PDFFolders = Nothing
On Error GoTo 0
If it runs the sess.SynchronizeDirectories statement, then access won't exit, otherwise it does. Looks to me like a bug win WinSCP.
I can do other things like downloading files, creating directories, etc. with no problem, but when it gets to this statement, it doesn't exit access afterwards.
Sticky instances of Access usually result from hanging object references.
If Access hangs the way you described, I would suspect a nasty circular reference.
To investigate on circular references, you basically have two options:
Inspect your code on circular dependencies - That might become tedious and not so easy. But if you know your code base deeply, you might have suspects where to look first.
Add logging to your code - In case you cannot spot the problem via inspection alone, you can consider adding consequent logging of object creation/deletion (through Class_Initialize/Class_Terminate). For larger code bases using classes heavily, this is a good investment to start with.
If your problem is with classes where you cannot control the code (as is your case), you might consider using that external classes only through wrapper classes where you can log creation/deletion. Of course in tricky cases, termination of the wrapper class does not mean termination of the wrapped class.
BTW, I strongly recommend to make sure to set every object reference explicitly to Nothing ASAP:
Set MyObj = GetMyObject()
' Proceed with coding here later
' First write the following line
Set MyObj = Nothing
Special thoughts have to be given in the case of local error handling to make sure to set the reference to Nothing in either case.
A good way to ensure this is using a With-block instead of an explicit variable (if the usage pattern allows to):
With GetMyObject()
' Use the object's members here
End With
With this pattern you save declaring the local variable and can be sure that the object reference does not survive the current method.
I still think it's a bug in WinSCP, but I found a workaround. I noticed that it only happened if I took the information from a table in the database, and not if I put in a hard-coded string. So I just added & vbNullString, which concatenates a blank string, which changes the data type from a Field to a String, and now it doesn't happen anymore.

Agilent Power Supply Programming using GPIB

On looking at the examples provided in the documentation of the Power supply. The Programming has been done by adding two libraries AgilentRMLib and VisComLib in the C#. When
i try to add the AgilentRMLib by Selecting the Add Reference->Agilent VISA COM Resourse Manager 1.0, an error is shown at the reference.
I tried adding the agtRM.dll directly from the Program Files. Still the error persists. Has anyone faced this problem before? Any Solutions for this? Do you have any other method to program the Power Supply from PC using Agilent IO.
I was able to use the VisaComLib(GlobMgr.dll) instead to program the GPIB using C# programming language.
The pdf file link!
was used as reference.
If you don't mind using VBA, this code can help you accomplish what you are trying to do, make sure it has VISA COM 488.2 Formatted I/O in the References:
Public Sub TestVISA()
Dim Dev_IO As VisaComLib.FormattedIO488
Dim io_manager As VisaComLib.ResourceManager
'Start of Open GPIB port (or any VISA resource)
Set io_manager = New VisaComLib.ResourceManager
Set Dev_IO = New VisaComLib.FormattedIO488
Set Dev_IO.IO = io_manager.Open("GPIB0::x::INSTR") ' x is the GPIB address number of the Dev_IOument
Set io_manager = Nothing
Dev_IO.IO.Timeout = 10000 'set time out to 10 seconds, use this line to change timeout to any time out value per VISA spec
'End of Open GPIB port
'Send some SCPI command to the Dev_IOumnet
Dev_IO.WriteString ("*IDN?")
MsgBox ("Connected to: " & Dev_IO.ReadString)
'Close the port upon completion
Dev_IO.IO.Close
Set Dev_IO = Nothing 'release the object
End Sub
As you are using GPIB protocol, better use GPIB libraries and wrapper then code with native SCPI commands. That way your software will be more dependent to your applications and you can control almost everything. With VISA interface you have to worry about another layer but with this approach you can directly control your devices efficiently. I worked with VISA for couple of years but after that hardworking times now I can build my measurement systems with direct GPIB programming. You can find required libraries from NI's or Agilent's website.

Calling an External VBA from VBScript

I am using a program called mathtype to pull some equation objects out of a word document. I've written code in VBA that works perfectly using their API, but I have to translate it to a VBScript file. I have looked all over google, but have not found any solution on how (If it is even possible) to call a VBA library from VBScript.
VBScript can't see the MathTypeSDK Objects/Functions.
If not possible, how would I encase the macro I need to run in a globally available word file and call it from the VBScript?
Edit: Got it! Unfortunately the approaches below, while helpful, did not work for my situation. I found something closer: Embedding the macro in a global file and calling it through the Word Objects Run command.
objWord.Run "Normal.NewMacros.RunMain"
Here is an approach which might work for you. I tested this simple example.
Class "clsTest" in file "Tester.docm":
Public Sub Hello()
MsgBox "Hello"
End Sub
Class "Instancing" is marked "PublicNotCreatable".
Module in "Tester.docm":
Public Function GetClass() As clsTest
Set GetClass = New clsTest
End Function
In your vbscript:
Dim fPath, fName
fPath = "C:\Documents and Settings\twilliams\Desktop\"
fName = "Tester.docm"
Dim wdApp, o
Set wdApp = CreateObject("word.application")
wdApp.visible=true
wdapp.documents.open fPath & fName
Set o = wdApp.Run("GetClass")
o.Hello
Set o=nothing
Again - I only tested this simple example: you'll have to adapt it to your situation and try it out.
Word-VBA was not made to create reusable libraries, I suppose (for usage in external programs).
One way to reuse existing Word-VBA code is, however, run Word via WScript.Shell.Run using the /m<macroname> command line switch (see http://support.microsoft.com/kb/210565/en-us for details). This, has the restriction that evertime you need to call a specific macro, a Word process is started again, running that macro, and ends afterwards. Means, if you need just one call to your Word.VBA for a specfific task, this may be ok, but if you need a lot of interprocess communication between your VBScript and your VBA macro, you should look for a different solution.

How can I connect to Lotus thru ODBC using VBA?

I'm interested in setting up an Access db to run a report automatically. To save myself the trouble of going to each client computer and setting up the appropriate DSNs, I'd like to set up the ODBC connections in the VB script itself if possible.
I've googled and checked this site and found some good starter code, but not enough to make all the error messages go away. Can someone complete the code below?
Sub SetupODBC(Str_Server as string, Str_Db as string)
'Str_Server=Name of Server
'Str_db=Name of Database
Dim C as ADODB.Connection
Set C = new ADODB.Connection
C.ConnectionString = ??
C.Open
Debug.print C.State
Exit Sub
Welcome to the board. ConnectionStrings is indeed your friend, but the issue you are having is that you don't have the driver:) Lotus Notes is not a relational database it is a document oriented database. Historically there has not been a way to Access it like it is a relational database for that reason. However IBM eventually got around to writing a sort of translator in the form of NotesSQL. If you follow the link and get the driver you should be able to use ODBC. It is worth noting that Notes exposes itself to COM. So if push comes to shove you can automate the client.
This site is your friend: http://www.connectionstrings.com/access
I didn't follow your question correctly at first. I see you want to create a link from Access to Lotus to report on Lotus Notes data. Well there are a few ways to do so.
I frequently use a method of exposing Lotus Notes data as XML, then accessing that XML from the remote system. You can easily create a Notes Page with the XML start tag, root element, and then insert an embedded view in between the root element. That embedded view then needs to display as HTML and contain columns that resolve to xml tags. For instance, each row of the view would look similar to this:
<Person><FirstName>Ken</FirstName><LastName>Pespisa</LastName></Person>
and your column formulas would be:
"<Person><FirstName>" + FirstName + "</FirstName>"
for the first name column, and for the last name column it would be this:
"<LastName> + LastName + </LastName></Person>"
Note that this assumes that your Notes server has the HTTP service turned on and you can reach the database via a browser.
However as mentioned by other answers, you can use other methods such as NotesSQL and COM. It sounds like you are putting this solution on many workstations, though, and NotesSQL would require you to install the driver on each workstation. The COM method would work without requiring any extra work at the users' desks so I'd favor that solution in this case.
Looks like a great site for my needs, even if it hasn't been updated in a year. But still no cigar. Now, I'm getting "Data source name not found and default driver not specified"
(Obviously, ServerNameGoesHere and DatabaseNameGoesHere are subsitutions)
Sub dbX()
Dim C As adodb.Connection
Set C = New adodb.Connection
C.Open _
"Driver={Lotus NotesSQL 3.01 (32-bit) ODBC DRIVER (*.nsf)};" & _
" Server=ServerNameGoesHere;" & _
" Database=DatabaseNameGoesHere.nsf;"
C.Close
End Sub