Capture Opened Workbook in New Instance of Excel - vba

I have a number of macros where I want it run some code, then prompt the user to export an Excel workbook from another program, then run more code once the export has been opened. The tricky part is that some programs export to a new instance of Excel, while other programs export to the current instance.
The current workflow is (code at bottom):
Call the central 'Capture' Module with the name of the export (some
programs export 'Book[x]' some do 'workbook[x]', etc.) and the
procedure you want to run once the export is found.
Capture Module gets a list of all existing workbook names from all
Excel instances and saves as a Module-level string.
Capture Module uses Application.OnTime so that every 3 seconds, it
scans the list of all workbooks across all Excel instances.
If it finds a workbook that is not in the previously saved list of
all existing workbook names, and that contains the name of the
export, it stores that workbook as a public module level variable,
and runs the saved procedure from Step 1, which can the reference
the store workbook.
This works very well in all circumstances, EXCEPT for one. If I already have Book1.xlsx open in my current instance of Excel, and the 3rd party program exports Book1.xlsx to a NEW instance of Excel, the program doesn't recognize this as the export, since Book1.xlsx is in the existing workbook names string array already.
My solution is to find some way of uniquely identifying each workbook that's better than 'Name' or 'Path'. I tried saving each workbook name in the existing workbook names string as [application.hwnd]![workbook name] but this was an unstable fix and frequently broke (I don't really understand how hwnd works so I can't say why).
Any ideas? Thanks!
Sample Procedures That Use MCaptureExport
Public Sub GrabFXAllExport()
Const sSOURCE As String = "GrabFXAllExport"
On Error GoTo ErrorHandler
If Not TAAA.MCaptureExport.bCaptureExport("FXALL", "TAAA.FXAllEmail.ProcessFXAllExport") Then Err.Raise glHANDLED_ERROR
ErrorExit:
Exit Sub
ErrorHandler:
If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Sub
Public Sub ProcessFXAllExport()
Const sSOURCE As String = "ProcessFXAllExport"
On Error GoTo ErrorHandler
If MCaptureExport.mwbCaptured Is Nothing Then
MsgBox "Exported Workbook Not Found. Please try again.", vbCritical, gsAPP_NAME
GoTo ErrorExit
End If
Dim wsSourceSheet As Worksheet
Set wsSourceSheet = MCaptureExport.mwbCaptured.Worksheets(1)
Set MCaptureExport.mwbCaptured = Nothing
[I now have the export and can work with it as a I please]
ErrorExit:
Exit Sub
ErrorHandler:
If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Sub
MCaptureExport Module
Option Explicit
Option Base 1
' Description: This module contains the central error
' handler and related constant declarations.
Private Const msMODULE As String = "MCaptureExport"
Private sExistingWorkbookList() As String
Public mwbCaptured As Workbook
Public msCaptureType As String
Private sReturnProcedure As String
Private bListening As Boolean
Public Function bCaptureExport(sCaptureType As String, sRunAfterCapture As String) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bCaptureExport()"
On Error GoTo ErrorHandler
bReturn = True
If Not bWorkbookNamesAsArray(sExistingWorkbookList, True, False) Then Err.Raise glHANDLED_ERROR
sReturnProcedure = sRunAfterCapture
bListening = True
msCaptureType = sCaptureType
TAAA.MCaptureExport.WaitForCapture sCaptureTypeToNameContains(msCaptureType)
MsgBox "Waiting for " & msCaptureType & " Export", vbInformation, gsAPP_NAME
ErrorExit:
bCaptureExport = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Sub WaitForCapture(sNameContains As String)
Const sSOURCE As String = "WaitForCapture"
On Error GoTo ErrorHandler
Dim wbCaptureCheck As Workbook
If Not bCaptureCheck(sNameContains, wbCaptureCheck) Then Err.Raise glHANDLED_ERROR
If wbCaptureCheck Is Nothing Then
If bListening Then _
Application.OnTime Now + TimeSerial(0, 0, 3), "'TAAA.MCaptureExport.WaitForCapture " & Chr(34) & sNameContains & Chr(34) & "'"
Else
Dim bSameApp As Boolean
If Not bWorkbooksInSameApp(ThisWorkbook, wbCaptureCheck, bSameApp) Then Err.Raise glHANDLED_ERROR
If Not bSameApp Then
Dim sTempFilePath As String
sTempFilePath = ThisWorkbook.Path & "\temp_" & Format(Now, "mmddyyhhmmss") & ".xls"
wbCaptureCheck.SaveCopyAs sTempFilePath
wbCaptureCheck.Close SaveChanges:=False
Set wbCaptureCheck = Application.Workbooks.Open(sTempFilePath)
End If
Set mwbCaptured = wbCaptureCheck
bListening = False
Application.Run sReturnProcedure
End If
ErrorExit:
Exit Sub
ErrorHandler:
If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Sub
Private Function sCaptureTypeToNameContains(sCaptureType As String) As String
sCaptureTypeToNameContains = "*"
On Error Resume Next
Select Case UCase(sCaptureType)
Case "SOTER": sCaptureTypeToNameContains = "workbook"
Case "THOR": sCaptureTypeToNameContains = "Book"
Case "FXALL": sCaptureTypeToNameContains = "search_results_export"
End Select
End Function
Private Function bCaptureCheck(sNameContains As String, wbResult As Workbook) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bCaptureCheck()"
On Error GoTo ErrorHandler
bReturn = True
Dim i As Long, wb As Workbook
Dim xlApps() As Application
If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR
For i = LBound(xlApps) To UBound(xlApps)
For Each wb In xlApps(i).Workbooks
If wb.Name Like "*" & sNameContains & "*" _
And Not bIsInArray(wb.Name, sExistingWorkbookList) Then
Set wbResult = wb
GoTo ErrorExit
End If
Next
Next
ErrorExit:
bCaptureCheck = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Utility Functions Used by MCaptureExport
Public Function bWorkbookNamesAsArray(sResult() As String, Optional bAllInstances As Boolean = True) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bWorkbookNamesAsArray()"
On Error GoTo ErrorHandler
bReturn = True
Dim i As Long, wb As Workbook
Dim xlApps() As Application
Dim ResultArray() As String
Dim Ndx As Integer, wbCount As Integer
If bAllInstances Then
If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR
Else
ReDim xlApps(0)
Set xlApps(0) = Application
End If
For i = LBound(xlApps) To UBound(xlApps)
For Each wb In xlApps(i).Workbooks
wbCount = wbCount + 1
Next
Next
ReDim ResultArray(1 To wbCount)
For i = LBound(xlApps) To UBound(xlApps)
For Each wb In xlApps(i).Workbooks
Ndx = Ndx + 1
ResultArray(Ndx) = wb.Name
Next
Next
sResult = ResultArray()
ErrorExit:
bWorkbookNamesAsArray = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Public Function bGetAllExcelInstances(xlApps() As Application) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bGetAllExcelInstances()"
On Error GoTo ErrorHandler
bReturn = True
Dim n As Long
Dim hWndMain As LongPtr
Dim app As Application
' Cater for 100 potential Excel instances, clearly could be better
ReDim xlApps(1 To 100)
hWndMain = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)
Do While hWndMain <> 0
If Not bGetExcelObjectFromHwnd(hWndMain, app) Then Err.Raise glHANDLED_ERROR
If Not (app Is Nothing) Then
If n = 0 Then
n = n + 1
Set xlApps(n) = app
ElseIf bCheckHwnds(xlApps, app.Hwnd) Then
n = n + 1
Set xlApps(n) = app
End If
End If
hWndMain = FindWindowEx(0&, hWndMain, "XLMAIN", vbNullString)
Loop
If n Then
ReDim Preserve xlApps(1 To n)
'GetAllExcelInstances = n
Else
Erase xlApps
End If
ErrorExit:
bGetAllExcelInstances = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bCheckHwnds(xlApps() As Application, Hwnd As LongPtr) As Boolean
On Error Resume Next
Dim i As Integer
For i = LBound(xlApps) To UBound(xlApps)
If Not xlApps(i) Is Nothing Then
If xlApps(i).Hwnd = Hwnd Then
bCheckHwnds = False
Exit Function
End If
End If
Next i
bCheckHwnds = True
End Function
Public Function bWorkbooksInSameApp(wb1 As Workbook, wb2 As Workbook, bSameApp As Boolean) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bWorkbooksInSameApp()"
On Error GoTo ErrorHandler
bReturn = True
bSameApp = wb1.Application.Hwnd = wb2.Application.Hwnd
ErrorExit:
bWorkbooksInSameApp = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bGetExcelObjectFromHwnd(ByVal hWndMain As LongPtr, aAppResult As Application) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bGetExcelObjectFromHwnd()"
On Error GoTo ErrorHandler
bReturn = True
Dim hWndDesk As LongPtr
Dim Hwnd As LongPtr
Dim strText As String
Dim lngRet As Long
Dim iid As UUID
Dim obj As Object
hWndDesk = FindWindowEx(hWndMain, 0&, "XLDESK", vbNullString)
If hWndDesk <> 0 Then
Hwnd = FindWindowEx(hWndDesk, 0, vbNullString, vbNullString)
Do While Hwnd <> 0
strText = String$(100, Chr$(0))
lngRet = CLng(GetClassName(Hwnd, strText, 100))
If Left$(strText, lngRet) = "EXCEL7" Then
Call IIDFromString(StrPtr(IID_IDispatch), iid)
If AccessibleObjectFromWindow(Hwnd, OBJID_NATIVEOM, iid, obj) = 0 Then 'S_OK
Set aAppResult = obj.Application
GoTo ErrorExit
End If
End If
Hwnd = FindWindowEx(hWndDesk, Hwnd, vbNullString, vbNullString)
Loop
End If
ErrorExit:
bGetExcelObjectFromHwnd = bReturn
Exit Function
ErrorHandler:
MsgBox Err.Number
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function

I have a potential solution. However I want to leave the question open. This is a fairly complicated problem and I bet there are more elegant solutions than what I'm proposing.
So I updated the format of sExistingWorkbookList to [Application.hWnd]![Workbook.name]. I had tried this before but I think it's working this time.
Thoughts?
Updated Version of bWorkbookNamesAsArray
Added wb.Application.Hwnd & "!" & to ResultArray(Ndx) = wb.name
Public Function bWorkbookNamesAsArray(sResult() As String, Optional bAllInstances As Boolean = True) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bWorkbookNamesAsArray()"
On Error GoTo ErrorHandler
bReturn = True
Dim i As Long, wb As Workbook
Dim xlApps() As Application
Dim ResultArray() As String
Dim Ndx As Integer, wbCount As Integer
If bAllInstances Then
If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR
Else
ReDim xlApps(0)
Set xlApps(0) = Application
End If
For i = LBound(xlApps) To UBound(xlApps)
For Each wb In xlApps(i).Workbooks
wbCount = wbCount + 1
Next
Next
ReDim ResultArray(1 To wbCount)
For i = LBound(xlApps) To UBound(xlApps)
For Each wb In xlApps(i).Workbooks
Ndx = Ndx + 1
ResultArray(Ndx) = wb.Application.Hwnd & "!" & wb.Name
Next
Next
sResult = ResultArray()
ErrorExit:
bWorkbookNamesAsArray = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
New Utility Function
Public Function bGetWorkbookFromHwndAndName(ByVal sWorkbookReference As String, ByRef wbResult As Workbook)
Dim bReturn As Boolean
Const sSOURCE As String = "bGetWorkbookFromHwndAndName()"
On Error GoTo ErrorHandler
bReturn = True
Dim xlApp As Application
If Not bGetExcelObjectFromHwnd(CLng(Split(sWorkbookReference, "!")(0)), xlApp) Then Err.Raise glHANDLED_ERROR
Set wbResult = xlApp.Workbooks(Split(sWorkbookReference, "!")(1))
ErrorExit:
bGetWorkbookFromHwndAndName = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Updated MCaptureExport.bCaptureCheck()
Private Function bCaptureCheck(sNameContains As String, wbResult As Workbook) As Boolean
Dim bReturn As Boolean
Const sSOURCE As String = "bCaptureCheck()"
On Error GoTo ErrorHandler
bReturn = True
Dim i As Long, wb As Workbook, sFullWorkbookReference As String
Dim xlApps() As Application
If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR
For i = LBound(xlApps) To UBound(xlApps)
For Each wb In xlApps(i).Workbooks
sFullWorkbookReference = wb.Application.Hwnd & "!" & wb.Name
If wb.Name Like "*" & sNameContains & "*" _
And Not bIsInArray(sFullWorkbookReference, sExistingWorkbookList) Then
If Not bGetWorkbookFromHwndAndName(sFullWorkbookReference, wbResult) Then Err.Raise glHANDLED_ERROR
GoTo ErrorExit
End If
Next
Next
ErrorExit:
bCaptureCheck = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function

Related

Save an Excel file which contains a string from Outlook2007

Im newbiee in VBA, so i need a little help.
My goal is make an Outlook rule, but i have a problem:
I want to save one excel (xlsx) file from my Outlook Inbox to my PC. But only the file which contains (in spreadsheet) a string. But it saves (or not saving anything) the last excel file.. (not checking for MYSTRING)
Using this code:
Option Explicit
Sub CheckAttachments(olItem As MailItem)
Const strPath As String = "C:\Users\PC2\Documents\Temp_attachs\"
Const strFindText As String = "Completed"
Dim strFilename As String
Dim olAttach As Attachment
Dim xlApp As Object
Dim xlWB As Object
Dim xlSheet As Object
Dim bXStarted As Boolean
Dim bFound As Boolean
If olItem.Attachments.Count > 0 Then
For Each olAttach In olItem.Attachments
If Right(LCase(olAttach.FileName), 4) = "xlsx" Then
strFilename = strPath & Format(olItem.ReceivedTime, "yyyymmdd-HHMMSS") & _
Chr(32) & olAttach.FileName
olAttach.SaveAsFile strFilename
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If Err <> 0 Then
Application.StatusBar = "Please wait while Excel source is opened ... "
Set xlApp = CreateObject("Excel.Application")
bXStarted = True
End If
On Error GoTo 0
'Open the workbook to read the data
Set xlWB = xlApp.Workbooks.Open(strFilename)
Set xlSheet = xlWB.Sheets("Sheet1")
If FindValue(strFindText, xlSheet) Then
MsgBox "Value found in " & strFilename
bFound = True
End If
xlWB.Close 0
If bXStarted Then xlApp.Quit
If Not bFound Then Kill strFilename
Exit For
End If
Next olAttach
End If
End Sub
Function FindValue(FindString As String, iSheet As Object) As Boolean
Dim Rng As Object
If Trim(FindString) <> "" Then
With iSheet.Range("A:J")
Set Rng = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=-4163, _
LookAt:=1, _
SearchOrder:=1, _
SearchDirection:=1, _
MatchCase:=False)
If Not Rng Is Nothing Then
FindValue = True
Else
FindValue = False
End If
End With
End If
End Function
Sub Test()
Dim olMsg As MailItem
On Error Resume Next
Set olMsg = ActiveExplorer.Selection.Item(1)
CheckAttachments olMsg
End Sub
I think I found your Problem:
You have used Exit For in your For Loop only. So only after scanning 1st file, loop is exited.
You need to remove the Exit For and then your code will work smoothly.
Option Explicit
Sub CheckAttachments(olItem As MailItem)
Const strPath As String = "C:\Users\PC2\Documents\Temp_attachs\"
Const strFindText As String = "Completed"
Dim strFilename As String
Dim olAttach As Attachment
Dim xlApp As Object
Dim xlWB As Object
Dim xlSheet As Object
Dim bXStarted As Boolean
Dim bFound As Boolean
If olItem.Attachments.Count > 0 Then
For Each olAttach In olItem.Attachments
If Right(LCase(olAttach.FileName), 4) = "xlsx" Then
strFilename = strPath & Format(olItem.ReceivedTime, "yyyymmdd-HHMMSS") & _
Chr(32) & olAttach.FileName
olAttach.SaveAsFile strFilename
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If Err <> 0 Then
Application.StatusBar = "Please wait while Excel source is opened ... "
Set xlApp = CreateObject("Excel.Application")
bXStarted = True
End If
On Error GoTo 0
'Open the workbook to read the data
Set xlWB = xlApp.Workbooks.Open(strFilename)
Set xlSheet = xlWB.Sheets("Sheet1")
If FindValue(strFindText, xlSheet) Then
MsgBox "Value found in " & strFilename
bFound = True
End If
xlWB.Close 0
If bXStarted Then xlApp.Quit
If Not bFound Then Kill strFilename
End If
Next olAttach
End If
End Sub
Function FindValue(FindString As String, iSheet As Object) As Boolean
Dim Rng As Object
If Trim(FindString) <> "" Then
With iSheet.Range("A:J")
Set Rng = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=-4163, _
LookAt:=1, _
SearchOrder:=1, _
SearchDirection:=1, _
MatchCase:=False)
If Not Rng Is Nothing Then
FindValue = True
Else
FindValue = False
End If
End With
End If
End Function
Sub Test()
Dim olMsg As MailItem
On Error Resume Next
Set olMsg = ActiveExplorer.Selection.Item(1)
CheckAttachments olMsg
End Sub

Best Method For Excel VBA to Get Commands From Central Server

I'm sure something like this has been asked before but I guess I'm not searching the right keywords because I couldn't find a good answer.
I have created an Excel Add-In used by my entire team. I keep the most recent version on the Network drive and whenever someone re-opens Excel, the add-in checks if there is a new version and updates itself automatically.
What I'd like to do is be able to send commands to the add-ins individually to execute. For instance, if I have an important update to push, rather than waiting for each user to re-open Excel, I'd like to be able to save the command on the Network drive in a text file (i.e. "USER: ALL; COMMAND: UPDATE") and each user's add-in would automatically pick-up that command and process it within a reasonable time frame.
My question is what's the best method for accomplishing this? I can think of two solutions off the top of my head, neither of which I like.
Potential Solution #1 - In 'Worksheet_Calculate' or some similar place, have it check for new commands and process any it finds. However that seems like overkill and would potentially be checking far too often.
Potential Solution #2 - Use an infinite chain of Application.OnTime calls so that every X seconds/minutes it's checking for new central commands and will process any it finds. However I find Application.OnTime to be funky and unreliable.
Any ideas? I feel like doing something with a Class is the way to go but I don't have much experience with those.
Thanks!
OK, I ended up going with Potential Solution #1.
Code in ThisWorkbook
Private Sub Workbook_SheetCalculate(ByVal Sh As Object)
If mdtLastCheck = 0 Or DateDiff("s", mdtLastCheck, Now) > miCHECK_FREQUENCY_SECONDS Then
mdtLastCheck = Now
CheckForCommandsAndRun
End If
End Sub
Code in MCentralCommands
Note the only reference in this module to other modules is to a couple of global variables like gsAPP_MASTER_PATH. This code uses the MErrorHandler system from this book: Professional Excel Development.
Option Explicit
' Description: This module contains
'
Private Const msModule As String = "MCentralCommands"
Private Const msCOMMANDS_FOLDER As String = "Commands\"
Private Const msCOMMAND_NAME_FORUSER As String = "CMD_USERNAME_*"
Private Const msCOMMAND_NAME_FORALL As String = "CMD_ALL_*"
Public Const miCHECK_FREQUENCY_SECONDS = 10
Public mdtLastCheck As Date
Sub CheckForCommandsAndRun()
' *********************************************
' Entry-Point Procedure Code Start
' *********************************************
Const sSource As String = "CheckForCommandsAndRun"
On Error GoTo ErrorHandler
' *********************************************
' *********************************************
Dim sCommands() As String
If Not bGetNewCommands(sCommands) Then Err.Raise glHANDLED_ERROR
If Not bProcessAllCommands(sCommands) Then Err.Raise glHANDLED_ERROR
' *********************************************
' Entry-Point Procedure Code Exits
' *********************************************
ErrorExit:
Exit Sub
ErrorHandler:
If bCentralErrorHandler(msModule, sSource, , True) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Sub
Private Function bGetNewCommands(sCommands() As String) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bGetNewCommands()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandPath As String, sUser As String
sCommandPath = gsAPP_MASTER_PATH & msCOMMANDS_FOLDER
sUser = UCase(Application.UserName)
Dim iCommandCount As Integer
Dim vFile As Variant
vFile = Dir(sCommandPath)
While (vFile <> "")
If vFile Like msCOMMAND_NAME_FORALL Or _
vFile Like Replace(msCOMMAND_NAME_FORUSER, "USERNAME", sUser) Then _
ReDim Preserve sCommands(0 To iCommandCount)
sCommands(iCommandCount) = vFile
iCommandCount = iCommandCount + 1
End If
vFile = Dir
Wend
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bGetNewCommands = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bProcessAllCommands(sCommands() As String) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bProcessAllCommands()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandPath As String, sUser As String
sCommandPath = gsAPP_MASTER_PATH & msCOMMANDS_FOLDER
sUser = UCase(Application.UserName)
Dim iCmd As Integer
For iCmd = LBound(sCommands) To UBound(sCommands)
If Not bProcessCommand(sCommands(iCmd)) Then Err.Raise glHANDLED_ERROR
Next
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bProcessAllCommands = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bProcessCommand(sCommand As String, Optional bDeleteIfUserCmd As Boolean = True) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bProcessCommand()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandPath As String, sUser As String
sCommandPath = gsAPP_MASTER_PATH & msCOMMANDS_FOLDER
sUser = UCase(Application.UserName)
Dim bHaveIRun As Boolean, bCommandSuccessful As Boolean
If Not bHaveIRunCommand(sCommand, bHaveIRun) Then Err.Raise glHANDLED_ERROR
If Not bHaveIRun Then
If Not bRunCommand(sCommand, bCommandSuccessful) Then Err.Raise glHANDLED_ERROR
If bCommandSuccessful Then
If Not bMarkCommandAsRan(sCommand) Then Err.Raise glHANDLED_ERROR
MLog.Log "Ran: " & sCommand
End If
End If
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bProcessCommand = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bRunCommand(sCommand As String, bCommandSuccessful As Boolean) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bRunCommand()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandName As String
sCommandName = Replace(Mid(sCommand, InStrRev(sCommand, "_") + 1), ".txt", "")
Select Case UCase(sCommandName)
Case "MSGBOX":
Dim sMsgBoxText As String
If Not bGetParameterFromCommand(sCommand, "Msg", sMsgBoxText) Then Err.Raise glHANDLED_ERROR
MsgBox sMsgBoxText
bCommandSuccessful = True
Case "UPDATE":
CheckForUpdates False
bCommandSuccessful = True
Case "OLFLDRS":
UpdateSavedOutlookFolderList
bCommandSuccessful = True
End Select
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bRunCommand = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bGetParameterFromCommand(sCommand As String, sParameterName As String, sParameterReturn As String) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bGetParameterFromCommand()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandPath As String, sUser As String
sCommandPath = gsAPP_MASTER_PATH & msCOMMANDS_FOLDER
sUser = UCase(Application.UserName)
Dim sFilePath As String, sParameterText() As String, sTextLine As String
Dim iLineCount As Integer
sFilePath = sCommandPath & sCommand
Dim bBegin As Boolean
Open sFilePath For Input As #1
Do Until EOF(1)
Line Input #1, sTextLine
If bBegin Then If Left(sTextLine, 1) = ":" Then bBegin = False
If sTextLine Like "*:Parameters:*" Then
bBegin = True
End If
If bBegin Then
ReDim Preserve sParameterText(0 To iLineCount)
sParameterText(iLineCount) = sTextLine
iLineCount = iLineCount + 1
End If
Loop
Close #1
Dim iParameterCounter As Integer
For iParameterCounter = LBound(sParameterText) To UBound(sParameterText)
If sParameterText(iParameterCounter) Like sParameterName & ": *" Then _
sParameterReturn = Mid(sParameterText(iParameterCounter), InStr(1, sParameterText(iParameterCounter), " ") + 1)
Next
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bGetParameterFromCommand = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bHaveIRunCommand(sCommand As String, bHaveIRun As Boolean) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bHaveIRunCommand()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandPath As String, sUser As String
sCommandPath = gsAPP_MASTER_PATH & msCOMMANDS_FOLDER
sUser = UCase(Application.UserName)
Dim sFile As String, sText As String, sTextLine As String
sFile = sCommandPath & sCommand
Dim bBegin As Boolean
Open sFile For Input As #1
Do Until EOF(1)
Line Input #1, sTextLine
If bBegin Then If Left(sTextLine, 1) = ":" Then bBegin = False
If sTextLine Like "*:Run By Users:*" Then bBegin = True
If bBegin Then
sText = sText & sTextLine
End If
Loop
Close #1
bHaveIRun = sText Like "*" & sUser & "*"
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bHaveIRunCommand = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function
Private Function bMarkCommandAsRan(sCommand As String) As Boolean
' *********************************************
' **** Function Code Start
' *********************************************
Dim bReturn As Boolean
Const sSource As String = "bMarkCommandAsRan()"
On Error GoTo ErrorHandler
bReturn = True
' *********************************************
' *********************************************
Dim sCommandPath As String, sUser As String
sCommandPath = gsAPP_MASTER_PATH & msCOMMANDS_FOLDER
sUser = UCase(Application.UserName)
Dim sFilePath As String, sRanText As String, sTextLine As String, bHaveIRun As Boolean
Dim sFullText() As String, iLineCount As Integer, iRunBy As Integer
sFilePath = sCommandPath & sCommand
Dim bBegin As Boolean
Open sFilePath For Input As #1
Do Until EOF(1)
Line Input #1, sTextLine
ReDim Preserve sFullText(0 To iLineCount)
sFullText(iLineCount) = sTextLine
iLineCount = iLineCount + 1
If bBegin Then If Left(sTextLine, 1) = ":" Then bBegin = False
If sTextLine Like "*:Run By Users:*" Then
bBegin = True
iRunBy = iLineCount - 1
End If
If bBegin Then
sRanText = sRanText & sTextLine
End If
Loop
Close #1
bHaveIRun = sRanText Like "*" & sUser & "*"
If Not bHaveIRun Then
Dim iCounter As Integer
Open sFilePath For Output As #1
For iLineCount = LBound(sFullText) To UBound(sFullText)
Print #1, sFullText(iLineCount)
If iLineCount = iRunBy Then _
Print #1, sUser
Next
Close #1
End If
' *********************************************
' Function Code Exits
' *********************************************
ErrorExit:
bMarkCommandAsRan = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msModule, sSource) Then
Stop
Resume
Else
Resume ErrorExit
End If
End Function

Can I stop vba code from running if one of the source workbook is open?

I am using a VBA script where the first worksheets of all workbooks saved in a specific folder are consolidated in one workbook. What I want is, if any source workbook is open while running this script, then I should get a prompt that 'source workbook is open' and the script should not run.
VBA script of destination worksheet is as follows:
Private Sub CommandButton1_Click()
Dim directory As String, fileName As String, sheet As Worksheet, total As Integer
Dim WrdArray() As String
Application.ScreenUpdating = False
Application.DisplayAlerts = False
directory = "C:\test\"
fileName = Dir(directory & "*.xl??")
Application.EnableEvents = False
Do While fileName <> ""
Workbooks.Open (directory & fileName)
WrdArray() = Split(fileName, ".")
For Each sheet In Workbooks(fileName).Worksheets
Workbooks(fileName).ActiveSheet.Name = WrdArray(0)
total = Workbooks("import-sheets.xlsm").Worksheets.Count
Workbooks(fileName).Worksheets(sheet.Name).Copy After:=Workbooks("import-sheets.xlsm").Worksheets(total)
GoTo exitFor:
Next sheet
exitFor:
Workbooks(fileName).Close
fileName = Dir()
Loop
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
I appreciate your help in advance
Untested but it should work, source:
https://support.microsoft.com/en-us/kb/291295
Function IsFileOpen(filename As String)
Dim filenum As Integer, errnum As Integer
On Error Resume Next ' Turn error checking off.
filenum = FreeFile() ' Get a free file number.
' Attempt to open the file and lock it.
Open filename For Input Lock Read As #filenum
Close filenum ' Close the file.
errnum = Err ' Save the error number that occurred.
On Error GoTo 0 ' Turn error checking back on.
' Check to see which error occurred.
Select Case errnum
' No error occurred.
' File is NOT already open by another user.
Case 0
IsFileOpen = False
' Error number for "Permission Denied."
' File is already opened by another user.
Case 70
IsFileOpen = True
' Another error occurred.
Case Else
Error errnum
End Select
End Function
if you want to check if a workbook (an Excel file) is opened, try this function.
Public Function isWbOpened(ByVal wb As String) As Boolean
Dim workB As Workbook
isWbOpened = False
For Each workB In Workbooks
If workB.FullName = wb Or workB.Name = wb Then ''FullName : path + filename Name : filename only
isWbOpened = True
End If
Next workB
End Function
if the function return TRUE, then the Excel file is open, so skeep your script.
example:
if isWbOpened("theExcelFile.xlsx") then
msgbox "theExcelFile.xlsx is open"
end if
You can enumerate the files in a folder then test them to see if any is open before proceeding. Please note - the following code is assuming you are the one with them open, so if a shared file is open this may have to be adapted
Sub TestFolder()
Debug.Print XLFileIsOpen("C:\Test")
End Sub
Function XLFileIsOpen(sFolder As String) As Boolean
For Each Item In EnumerateFiles(sFolder)
If IsWorkBookOpen(CStr(Item)) = True Then XLFileIsOpen = True
Next Item
End Function
Function EnumerateFiles(sFolder As String) As Variant
Dim objFSO As Object: Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim objFolder As Object: Set objFolder = objFSO.GetFolder(sFolder)
Dim objFile As Object, V() As String
For Each objFile In objFolder.Files
If IsArrayAllocated(V) = False Then
ReDim V(0)
Else
ReDim Preserve V(UBound(V) + 1)
End If
V(UBound(V)) = objFile.Name
Next objFile
EnumerateFiles = V
End Function
Function IsArrayAllocated(Arr As Variant) As Boolean
On Error Resume Next
IsArrayAllocated = IsArray(Arr) And Not IsError(LBound(Arr, 1)) And LBound(Arr, 1) <= UBound(Arr, 1)
End Function
Function IsWorkBookOpen(sFile As String) As Boolean
On Error Resume Next
IsWorkBookOpen = Len(Application.Workbooks(sFile).Name) > 0
End Function

runtime error 91 object variable or With block variable not set vb6 in test machine however working properly in development machine

I have activex dll which has code for message queue, read and write from remote as well as local machine.
i did some changes for reading messages from remote queue and tested that dll with sample form application.
which works fine
however when i copied this dll to my test machine (in actual application)
i am facing "runtime error 91 object variable or With block variable not set"
error
if i revert my changes its working fine
code after changes
Private Function RetrieveMessage(ByVal MQReceive As msmq.MSMQQueue, _
MQTransaction As msmq.MSMQTransaction, _
ByVal Wait As Boolean, MessageLabel As String, MessageBody As String) As Boolean
'Dim MQReceive As MSMQ.MSMQQueue
Dim MQMsgRec As MSMQMessage
Dim MQDispenser As msmq.MSMQCoordinatedTransactionDispenser
On Error GoTo ErrorHandler
RetrieveMessage = False
If Not m_bQueueExists Then
Err.Raise vbObjectError + 1, "MicrosoftMQImpl", "Queue " & m_sQueueName & _
" does not exist"
Else
Set MQDispenser = New msmq.MSMQCoordinatedTransactionDispenser
'Begin Transaction
Set MQTransaction = MQDispenser.BeginTransaction
Dim bMessageFound As Boolean
bMessageFound = False
Set MQMsgRec = MQReceive.Receive(ReceiveTimeout:=IIf(Wait = True, DISPATCH_MESSAGE_INTERVAL, 0))
'Set MQMsgRec = MQReceive.Receive(Transaction:=MQTransaction, _
' ReceiveTimeout:=IIf(Wait = True, DISPATCH_MESSAGE_INTERVAL, 0))
If MQMsgRec Is Nothing Then
bMessageFound = False
Else
bMessageFound = True
MessageBody = CStr(MQMsgRec.Body)
MessageLabel = CStr(MQMsgRec.Label)
End If
Set MQDispenser = Nothing
Set MQMsgRec = Nothing
RetrieveMessage = bMessageFound
MQTransaction.Commit
Set MQTransaction = Nothing
End If
Exit Function
ErrorHandler:
If Not (MQTransaction Is Nothing) Then
MQTransaction.Abort
Set MQTransaction = Nothing
End If
LogNTEvent "PHLMessaging:MicrosoftMQImpl:RetrieveMessage", Err.Description, eNTLog_Error
Err.Raise Err.Number, Err.Source, Err.Description
End Function
code before changes
Private Function RetrieveMessage(ByVal MQReceive As msmq.MSMQQueue, _
MQTransaction As msmq.MSMQTransaction, _
ByVal Wait As Boolean, MessageLabel As String, MessageBody As String) As Boolean
'Dim MQReceive As MSMQ.MSMQQueue
Dim MQMsgRec As MSMQMessage
Dim MQDispenser As msmq.MSMQTransactionDispenser
On Error GoTo ErrorHandler
RetrieveMessage = False
If Not m_bQueueExists Then
Err.Raise vbObjectError + 1, "MicrosoftMQImpl", "Queue " & m_sQueueName & _
" does not exist"
Else
Set MQDispenser = New msmq.MSMQTransactionDispenser
'Begin Transaction
Set MQTransaction = MQDispenser.BeginTransaction
Dim bMessageFound As Boolean
bMessageFound = False
Set MQMsgRec = MQReceive.Receive(Transaction:=MQTransaction, _
ReceiveTimeout:=IIf(Wait = True, DISPATCH_MESSAGE_INTERVAL, 0))
If MQMsgRec Is Nothing Then
bMessageFound = False
Else
bMessageFound = True
MessageBody = CStr(MQMsgRec.Body)
MessageLabel = CStr(MQMsgRec.Label)
End If
Set MQDispenser = Nothing
Set MQMsgRec = Nothing
RetrieveMessage = bMessageFound
End If
Exit Function
ErrorHandler:
If Not (MQTransaction Is Nothing) Then
MQTransaction.Abort
Set MQTransaction = Nothing
End If
LogNTEvent "PHLMessaging:MicrosoftMQImpl:RetrieveMessage", Err.Description, eNTLog_Error
Err.Raise Err.Number, Err.Source, Err.Description
End Function
Thanks in advance
i somehow managed to resolve this error
just commented following code in changes
MQTransaction.Commit
Set MQTransaction = Nothing
still dont know why :-|

Last modification date of open workbook

Vba newbie. Need a function to output the last modification date of an open workbook. Here is what I have so far but I am getting a message that my formula contains an error when I invoke the function:
Function LastWBModDate(wbname)
ActivateWB (wbname)
LastWBModDate = Format(FileDateTime(ActiveWorkbook.FullName), "m/d/yy h:n ampm")
End Function
Public Function ActivateWB(wbname As String)
If IsWBOpen(wbname) Then
Workbooks(wbname).Activate
Else
MsgBox "Workbook : " & wbname & " is not open " & vbNewLine
End If
End Function
Public Function IsWBOpen(wbname As String) As Boolean
On Error Resume Next
If Workbooks(wbname) Is Nothing Then
IsWBOpen = False
Else
IsWBOpen = True
End If
End Function
Thanks!
Function LastWBModDate(wbname As String)
Dim rv, wb As Workbook
rv = "workbook?" 'default return value
On Error Resume Next
Set wb = Workbooks(wbname)
On Error GoTo 0
If Not wb Is Nothing Then
rv = Format(FileDateTime(wb.FullName), "m/d/yy h:n ampm")
End If
LastWBModDate = rv
End Function
Try below code :
You may also refer this link
Put below code on ThisWorkbook code section
Private Sub Workbook_Open()
LastWBModDate
End Sub
Put this code in any Standard Module
Function LastWBModDate() As String
Dim FSO As Object
Dim File As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Set File = FSO.GetFile(ThisWorkbook.FullName)
LastWBModDate = Format(File.DateLastModified, "m/d/yy h:n ampm")
Msgbox LastWBModDate
Set FSO = Nothing
End Function