Migrating VBA from 32 to 64 bit office fails on "AccessibleObjectFromWindow" in oleacc - excel

I've just moved from office 32 bit to office 64 bit. I have a lot of Outlook macros and Outlook helpfully points out all your VBA code that needs changing, and I've been able to fix most of it. The bit I'm struggling with is the code that I had help writing on one of my last stackexchange posts:
Open attachment in excel window and copy to open workbook
The code is supposed to find an excel window with my spreadsheet in it so I can mess about with it in the remainder of the code. Just to recap, the below works in 32 bit:
Option Explicit
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function AccessibleObjectFromWindow& Lib "oleacc" _
(ByVal hwnd&, ByVal dwId&, riid As GUID, xlWB As Object)
Private Const OBJID_NATIVEOM = &HFFFFFFF0
Private Type GUID
lData1 As Long
iData2 As Integer
iData3 As Integer
aBData4(0 To 7) As Byte
End Type
Sub Sample()
Dim Ret
Dim oXLApp As Object, wb As Object
Dim sPath As String, sFileName As String, SFile As String, filewithoutExt As String
Dim IDispatch As GUID
sPath = "C:\Users\Chris\Desktop\"
sFileName = "Data.xlsx": filewithoutExt = "Data"
SFile = sPath & sFileName
Ret = IsWorkBookOpen(SFile)
'~~> If file is open
If Ret = True Then
Dim dsktpHwnd As Long, hwnd As Long, mWnd As Long, cWnd As Long
SetIDispatch IDispatch
dsktpHwnd = GetDesktopWindow
hwnd = FindWindowEx(dsktpHwnd, 0&, "XLMAIN", vbNullString)
mWnd = FindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
While mWnd <> 0 And cWnd = 0
cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", filewithoutExt)
hwnd = FindWindowEx(dsktpHwnd, hwnd, "XLMAIN", vbNullString)
mWnd = FindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
Wend
'~~> We got the handle of the Excel instance which has the file
If cWnd > 0 Then
'~~> Bind with the Instance
Call AccessibleObjectFromWindow(cWnd, OBJID_NATIVEOM, IDispatch, wb)
'~~> Work with the file
With wb.Application.Workbooks(sFileName)
'
'~~> Rest of the code
'
End With
End If
'~~> If file is not open
Else
On Error Resume Next
Set oXLApp = GetObject(, "Excel.Application")
'~~> If not found then create new instance
If Err.Number <> 0 Then
Set oXLApp = CreateObject("Excel.Application")
End If
Err.Clear
On Error GoTo 0
Set wb = oXLApp.Workbooks.Open(SFile)
'
'~~> Rest of the code
'
End If
End Sub
Private Sub SetIDispatch(ByRef ID As GUID)
With ID
.lData1 = &H20400
.iData2 = &H0
.iData3 = &H0
.aBData4(0) = &HC0
.aBData4(1) = &H0
.aBData4(2) = &H0
.aBData4(3) = &H0
.aBData4(4) = &H0
.aBData4(5) = &H0
.aBData4(6) = &H0
.aBData4(7) = &H46
End With
End Sub
'~~> Function to check if file is open
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
There seems to be one line failing in the below, which is only invoked when the excel file you're looking is open, and in the 32 bit version it sets 'wb' to the application that's got it open.
I've commented the broken line:
Option Explicit
Private Declare PtrSafe Function FindWindowEx Lib "user32" _
Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, _
ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Public Declare PtrSafe Function GetDesktopWindow Lib "user32" () As LongPtr
Private Declare Function AccessibleObjectFromWindow& Lib "oleacc" _
(ByVal hwnd&, ByVal dwId&, riid As GUID, xlWB As Object)
Private Const OBJID_NATIVEOM = &HFFFFFFF0
Private Type GUID
lData1 As LongPtr
iData2 As Integer
iData3 As Integer
aBData4(0 To 7) As Byte
End Type
Sub Sample()
Dim Ret
Dim oXLApp As Object, wb As Object
Dim sPath As String, sFileName As String, SFile As String, filewithoutExt As String
Dim IDispatch As GUID
sPath = "C:\Users\Chris\Desktop\"
sFileName = "Data.xlsx": filewithoutExt = "Data"
SFile = sPath & sFileName
Ret = IsWorkBookOpen(SFile)
'~~> If file is open
If Ret = True Then
Dim dsktpHwnd As LongPtr, hwnd As LongPtr, mWnd As LongPtr, cWnd As LongPtr
SetIDispatch IDispatch
dsktpHwnd = GetDesktopWindow
hwnd = FindWindowEx(dsktpHwnd, 0&, "XLMAIN", vbNullString)
mWnd = FindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
While mWnd <> 0 And cWnd = 0
cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", filewithoutExt)
hwnd = FindWindowEx(dsktpHwnd, hwnd, "XLMAIN", vbNullString)
mWnd = FindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
Wend
'~~> We got the handle of the Excel instance which has the file
If cWnd > 0 Then
'~~> Bind with the Instance
'!!!!!!!!!!this next line does nothing!!!!!!!
Call AccessibleObjectFromWindow(cWnd, OBJID_NATIVEOM, IDispatch, wb)
'~~> Work with the file
With wb.Application.Workbooks(sFileName)
'
'~~> Rest of the code
'
End With
End If
'~~> If file is not open
Else
On Error Resume Next
Set oXLApp = GetObject(, "Excel.Application")
'~~> If not found then create new instance
If Err.Number <> 0 Then
Set oXLApp = CreateObject("Excel.Application")
End If
Err.Clear
On Error GoTo 0
Set wb = oXLApp.Workbooks.Open(SFile)
'
'~~> Rest of the code
'
End If
End Sub
Private Sub SetIDispatch(ByRef ID As GUID)
With ID
.lData1 = &H20400
.iData2 = &H0
.iData3 = &H0
.aBData4(0) = &HC0
.aBData4(1) = &H0
.aBData4(2) = &H0
.aBData4(3) = &H0
.aBData4(4) = &H0
.aBData4(5) = &H0
.aBData4(6) = &H0
.aBData4(7) = &H46
End With
End Sub
'~~> Function to check if file is open
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function

You've declared AccessibleObjectFromWindow() using shorthand notation which uses 32bit data types, try changing to:
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hwnd As LongPtr, ByVal dwId As LongPtr, riid As GUID, xlWB As Object) As LongPtr

Finally found an answer here:
https://stackoverflow.com/a/28504836/317958
So I made two mistakes, first I was over-enthusiastic with changes:
Private Type GUID
lData1 As LongPtr
iData2 As Integer
iData3 As Integer
aBData4(0 To 7) As Byte
End Type
Should remain:
Private Type GUID
lData1 As Long
iData2 As Integer
iData3 As Integer
aBData4(0 To 7) As Byte
End Type
And the declaration should be:
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" (ByVal Hwnd As LongPtr, ByVal dwId As LongPtr, ByRef riid As GUID, ByRef ppvObject As Object) As LongPtr

Related

Importing Spreedsheet file from SAP GUI using Macro - VBA error 9 subscript out of range while using Workbooks.Close

I'm newbie into macros and VBA - but because specific of my work requires to learn about VBA and macros.
So I've recorded in SAP GUI code below.
File EXPORT.XLSX saves at destination and new EXPORT.XLSX opens itself - But I want to just save it at the destination - without opening it straight away (for that I have different code) - how to do it? Or how to close the EXPORT.XLSX - Workbooks("filenamewithpath").close doesn't work - I'm receiving Error Code 9 :/
Run-time error '9': Subscript out of range
Sub Pobierz_zle()
If Not IsObject(SApplication) Then
Set SapGuiAuto = GetObject("SAPGUI")
Set SApplication = SapGuiAuto.GetScriptingEngine
End If
If Not IsObject(Connection) Then
Set Connection = SApplication.Children(0)
End If
If Not IsObject(session) Then
Set session = Connection.Children(0)
End If
If IsObject(WScript) Then
WScript.ConnectObject session, "on"
WScript.ConnectObject SApplication, "on"
End If
session.findById("wnd[0]").maximize
session.findById("wnd[0]/tbar[0]/okcd").Text = "/n zle_stocklist"
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/ctxt[0]").Text = "3901"
session.findById("wnd[0]/usr/ctxt[0]").caretPosition = 4
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/ctxt[1]").Text = "3182"
session.findById("wnd[0]/usr/ctxt[1]").caretPosition = 4
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/ctxt[3]").Text = "3500"
session.findById("wnd[0]/usr/ctxt[3]").SetFocus
session.findById("wnd[0]/usr/ctxt[3]").caretPosition = 4
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/chk[0]").Selected = False
session.findById("wnd[0]/usr/chk[0]").SetFocus
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]").sendVKey 8
session.findById("wnd[0]/usr/cntlCONTAINER_1/shellcont/shell").pressToolbarContextButton "&MB_EXPORT"
session.findById("wnd[0]/usr/cntlCONTAINER_1/shellcont/shell").selectContextMenuItem "&XXL"
session.findById("wnd[1]/usr/cmb").SetFocus
session.findById("wnd[1]/tbar[0]/btn[0]").press
session.findById("wnd[1]/usr/ctxt[0]").Text = "T:\DEPT-TSCW\WL\Inwentaryzacja\TEMP\"
session.findById("wnd[1]/usr/ctxt[1]").Text = "EXPORT.XLSX"
session.findById("wnd[1]/usr/ctxt[1]").caretPosition = 11
session.findById("wnd[1]").sendVKey 0
Application.Wait (Now + TimeValue("0:00:10"))
ActiveWorkbook.Close (False)
End Sub
Based on this entry you could try the following
Add the following declarations to a module
Option Explicit
' https://stackoverflow.com/a/68004477/6600940
#If VBA7 Then
Private Declare PtrSafe Function FindWindowEx Lib "User32" Alias _
"FindWindowExA" (ByVal hWnd1 As LongPtr, _
ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As LongPtr
Private Declare PtrSafe Function IIDFromString Lib "ole32" _
(ByVal lpsz As LongPtr, ByRef lpiid As GUID) As LongPtr
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hWnd As LongPtr, ByVal dwId As LongPtr, ByRef riid As GUID, _
ByRef ppvObject As Object) As LongPtr
#Else
Private Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function IIDFromString Lib "ole32" _
(ByVal lpsz As Long, ByRef lpiid As GUID) As Long
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As GUID, _
ByRef ppvObject As Object) As Long
#End If
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Const S_OK As Long = &H0
Private Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0
As you have the name of the workbook you would like to close you can run the following code. It will counter-times look for that file and try to close it. It might be not the perfect solution but for me it works all the time. Whenever I donwload a file from SAP it will close the automatically opened file regardless wheter it was opened in the same instance or in another one
Sub runCloseWorkbook(wkbName As String, counter As Long)
Const MAX_TRIES As Long = 10
If closeWorkbook(wkbName) Then
Exit Sub
Else
counter = counter + 1
If counter <= MAX_TRIES Then
Application.OnTime Now + TimeValue("00:00:05"), "'runCloseWorkbook """ & wkbName & """,""" & counter & "'"
'Debug.Print "closeWorkbook", wkbName, counter
End If
End If
End Sub
For getting this to work you also need
Private Function closeWorkbook(wkbName As String) As Boolean
Dim hWinXL As Long
hWinXL = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)
Dim xlApp As Excel.Application
Dim wb As Excel.Workbook
Do While hWinXL > 0
If getXLApp(hWinXL, xlApp) Then
For Each wb In xlApp.Workbooks
If wb.Name = wkbName Then
wb.Close False
closeWorkbook = True
' xlApp.Quit
End If
Next
End If
hWinXL = FindWindowEx(0, hWinXL, "XLMAIN", vbNullString)
Loop
End Function
' Private Function getXLApp(hWinXL As Long, xlApp As Excel.Application) As Boolean
Function getXLApp(hWinXL As Long, xlApp As Object) As Boolean
Dim hWinDesk As Long, hWin7 As Long
Dim obj As Object
Dim iid As GUID
Call IIDFromString(StrPtr(IID_IDispatch), iid)
hWinDesk = FindWindowEx(hWinXL, 0&, "XLDESK", vbNullString)
hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)
If AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj) = S_OK Then
Set xlApp = obj.Application
getXLApp = True
End If
End Function
In your case you could call it like that
runcloseworkbook "T:\DEPT-TSCW\WL\Inwentaryzacja\TEMP\EXPORT.XLSX", 5

Look for an open file before running code

I want to look for the needed reference file before running code.
My code will error out with some of the code running if the file is not found.
something like...
Sub TestByWorkbookName()
Dim wb As Workbook
For Each wb In Workbooks
If wb.Name = "file name" Then
'run code...
End If
Next
MsgBox "File not found"
End Sub
I want to run code if the workbook is open and end sub if not open.
Quick example for checking if any wb has been found:
for each wb in workbooks
if instr(wb.name, "file name")>0 then
check = 1
`do stuff
exit sub
else
check = 0
end if
next wb
if check = 0 then msgbox "File not found."
If I got the OP right he would like to check if a certain workbook is already open in Excel. And in case it is open some code can run then.
My suggestion would be to do it like that
Sub RunCode()
Dim wkbName As String
wkbName = "myWorkbook.xlsx" ' only the workbook name is needed, not the full path
If isWorkbookOpen(wkbName) Then ' code for this function is below
' Run the code you want to run in case the workbook with the name wkbName is open
Debug.Print wkbName & " is open"
Else
' Do not do anything in case the workbook with the name wkbName is not open
Debug.Print wkbName & " is not open"
End If
End Sub
The following code will be needed for the code above
Option Explicit
Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, _
ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Private Declare PtrSafe Function IIDFromString Lib "ole32" _
(ByVal lpsz As Long, ByRef lpiid As GUID) As Long
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hWnd As LongPtr, ByVal dwId As Long, ByRef riid As GUID, _
ByRef ppvObject As Object) As LongPtr
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Const S_OK As Long = &H0
Private Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0
Function getXLApp(hWinXL As LongPtr, xlApp As Excel.Application) As Boolean
Dim hWinDesk As LongPtr, hWin7 As LongPtr
Dim obj As Object
Dim iid As GUID
Call IIDFromString(StrPtr(IID_IDispatch), iid)
hWinDesk = FindWindowEx(hWinXL, 0&, "XLDESK", vbNullString)
hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)
If AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj) = S_OK Then
Set xlApp = obj.Application
getXLApp = True
End If
End Function
Function isWorkbookOpen(wkbName As String) As Boolean
Dim hWinXL As LongPtr
hWinXL = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)
Dim xlApp As Excel.Application
Dim wb As Excel.Workbook
Do While hWinXL > 0
If getXLApp(hWinXL, xlApp) Then
For Each wb In xlApp.Workbooks
If wb.Name = wkbName Then
isWorkbookOpen = True
End If
Next
End If
hWinXL = FindWindowEx(0, hWinXL, "XLMAIN", vbNullString)
Loop
End Function

How to activate workbook exported from SAP?

I have a macro to process data from SAP.
When the workbook from SAP appears I am not able to activate it via VBA code below.
Even Workbooks("export.xlsx").activate does not work.
session.findById("wnd[0]/tbar[0]/okcd").Text = "/nFBL3N"
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[0]/usr/btn%_SD_SAKNR_%_APP_%-VALU_PUSH").press
session.findById("wnd[1]/usr/tabsTAB_STRIP/tabpSIVA/ssubSCREEN_HEADER:SAPLALDB:3010/tblSAPLALDBSINGLE/ctxtRSCSEL_255-SLOW_I[1,0]").Text = "179811"
session.findById("wnd[1]/usr/tabsTAB_STRIP/tabpSIVA/ssubSCREEN_HEADER:SAPLALDB:3010/tblSAPLALDBSINGLE/ctxtRSCSEL_255-SLOW_I[1,1]").Text = "431311"
session.findById("wnd[1]/usr/tabsTAB_STRIP/tabpSIVA/ssubSCREEN_HEADER:SAPLALDB:3010/tblSAPLALDBSINGLE/ctxtRSCSEL_255-SLOW_I[1,1]").SetFocus
session.findById("wnd[1]/usr/tabsTAB_STRIP/tabpSIVA/ssubSCREEN_HEADER:SAPLALDB:3010/tblSAPLALDBSINGLE/ctxtRSCSEL_255-SLOW_I[1,1]").caretPosition = 6
session.findById("wnd[1]/tbar[0]/btn[0]").press
session.findById("wnd[1]/tbar[0]/btn[8]").press
session.findById("wnd[0]/usr/radX_AISEL").Select
session.findById("wnd[0]/usr/ctxtSO_BUDAT-LOW").Text = "01.03.2020"
session.findById("wnd[0]/usr/ctxtSO_BUDAT-HIGH").Text = "31.03.2020"
session.findById("wnd[0]/usr/ctxtSO_BUDAT-HIGH").SetFocus
session.findById("wnd[0]/usr/ctxtSO_BUDAT-HIGH").caretPosition = 10
session.findById("wnd[0]/tbar[1]/btn[16]").press
session.findById("wnd[0]/usr/ssub%_SUBSCREEN_%_SUB%_CONTAINER:SAPLSSEL:2001/ssubSUBSCREEN_CONTAINER2:SAPLSSEL:2000/ssubSUBSCREEN_CONTAINER:SAPLSSEL:1106/ctxt%%DYN009-LOW").Text = "L6"
session.findById("wnd[0]/usr/ssub%_SUBSCREEN_%_SUB%_CONTAINER:SAPLSSEL:2001/ssubSUBSCREEN_CONTAINER2:SAPLSSEL:2000/ssubSUBSCREEN_CONTAINER:SAPLSSEL:1106/ctxt%%DYN009-LOW").SetFocus
session.findById("wnd[0]/usr/ssub%_SUBSCREEN_%_SUB%_CONTAINER:SAPLSSEL:2001/ssubSUBSCREEN_CONTAINER2:SAPLSSEL:2000/ssubSUBSCREEN_CONTAINER:SAPLSSEL:1106/ctxt%%DYN009-LOW").caretPosition = 2
session.findById("wnd[0]/tbar[1]/btn[8]").press
session.findById("wnd[0]/mbar/menu[0]/menu[3]/menu[1]").Select
session.findById("wnd[1]/tbar[0]/btn[11]").press
Set session = Nothing
Set Connection = Nothing
Set SapGuiAuto = Nothing
Set WScript = Nothing
Set Application = Nothing
Dim wb As Workbook
Windows("export.XLSX").Activate
In order to check the existence of the workbook in all open Excel sessions, please use the next code, as I will explain
Firstly copy the next code on top of your module (in the declarations part):
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hWnd As LongPtr, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Private Declare PtrSafe Function FindWindowExA Lib "user32" ( _
ByVal hwndParent As LongPtr, ByVal hwndChildAfter As LongPtr, _
ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
#Else
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hwnd As Long, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Private Declare Function FindWindowExA Lib "user32" ( _
ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
ByVal lpszClass As String, ByVal lpszWindow As String) As Long
#End If
Then, use the next function to be called from your existing code (instead of Windows("export.XLSX").Activate):
Private Function GetExcelSes() As Collection
Dim g&(0 To 3), ppv As Object, hWnd, hWnd2, hWnd3
g(0) = &H20400: g(1) = &H0
g(2) = &HC0: g(3) = &H46000000
Dim AlreadyThere As Boolean, Xl As Application
Set GetExcelSes = New Collection
Do
hWnd = FindWindowExA(0, hWnd, "XLMAIN", vbNullString)
If hWnd = 0 Then Exit Do
hWnd2 = FindWindowExA(hWnd, 0, "XLDESK", vbNullString)
hWnd3 = FindWindowExA(hWnd2, 0, "EXCEL7", vbNullString)
If AccessibleObjectFromWindow(hWnd3, &HFFFFFFF0, g(0), ppv) = 0 Then
AlreadyThere = False
For Each Xl In GetExcelSes
If Xl Is ppv.Application Then
AlreadyThere = True
Exit For
End If
Next
If Not AlreadyThere Then
GetExcelSes.aDD ppv.Application
End If
End If
Loop
End Function
The above function can be called as in the following example (use it like it is instead of your last code line). The declarations are important:
Sub TestSaveAWbFromAllSess()
Dim Ex As Collection, El As Variant
Dim wb As Workbook, expWb As Workbook
Set Ex = GetExcelSes
For Each El In Ex
For Each wb In El.Workbooks
Debug.Print wb.Name 'just to see all open wb names, confirming that the function works...
If UCase(wb.Name) = "EXPORT.XLSX" Then
Set expWb = wb
'Do here whatever you need with the found workbook. For instance:
expWb.SaveCopyAs fileName:=ThisWorkbook.path & "\TestSAPExport.XLSX"
End If
Next
Next
End Sub

Close specific Excel file opened by user from Access-VBA

I see version of how to do this with
Dim ran as Excel.Applcation
but the version of Access I am using doesn't have Excel.Application as an option.
I have written the following code that runs, but does not close the file
Dim Path1 as String
Dim objXL As Object
Dim xlWB As Object
Path1 = "C:/....."
Set objXL = CreateObject("Excel.Application")
Set xlWB = objXL.Workbooks.Open(Path1)
xlWB.Close False
Set xlWB = Nothing
objXL.Quit
Set objXL = Nothing
You can use the following code to close all Excel files (already posted here):
Public Sub CloseAllExcel()
Dim obj As Object
On Error GoTo ExitSub
Dim i As Integer
'There shouldn't be more than 10000 running Excel applications
'Can use While True too, but small risk of infinite loop
For i = 0 To 10000
Set obj = GetObject(, "Excel.Application")
obj.Quit
Next i
ExitSub:
End Sub
But if we're going to close one specific one, we need some Win32 magic that I can't do, but hey, if you can't do something, you can find it on StackOverflow.
Most code by Florent B found here
First, declare our Win32 functions
#If VBA7 Then
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hwnd As LongPtr, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Private Declare PtrSafe Function FindWindowExA Lib "user32" ( _
ByVal hwndParent As LongPtr, ByVal hwndChildAfter As LongPtr, _
ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
#Else
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hwnd As Long, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Private Declare Function FindWindowExA Lib "user32" ( _
ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
ByVal lpszClass As String, ByVal lpszWindow As String) As Long
#End If
Then use them to get all running Excel application objects
Public Function GetExcelInstances() As Collection
Dim guid&(0 To 3), acc As Object, hwnd, hwnd2, hwnd3
guid(0) = &H20400
guid(1) = &H0
guid(2) = &HC0
guid(3) = &H46000000
Set GetExcelInstances = New Collection
Do
hwnd = FindWindowExA(0, hwnd, "XLMAIN", vbNullString)
If hwnd = 0 Then Exit Do
hwnd2 = FindWindowExA(hwnd, 0, "XLDESK", vbNullString)
hwnd3 = FindWindowExA(hwnd2, 0, "EXCEL7", vbNullString)
If AccessibleObjectFromWindow(hwnd3, &HFFFFFFF0, guid(0), acc) = 0 Then
GetExcelInstances.Add acc.Application
End If
Loop
End Function
And then use that collection so we can check which one has the workbook open, and close it
Public Sub closeWorkbook(workbookPath As String)
Dim excelInstances As Collection
Set excelInstances = GetExcelInstances
Dim excelApp As Object
Dim excelWorkbook As Object
For Each excelApp In excelInstances
For Each excelWorkbook In excelApp.Workbooks
If excelWorkbook.FullName = workbookPath Then
excelWorkbook.Close False
End If
Next excelWorkbook
If excelApp.Workbooks.Count = 0 Then
excelApp.Quit
End If
Next excelApp
End Sub
And then, implement that close function
Dim Path1 as String
Path1 = "C:/....."
closeWorkbook Path1

Open attachment in excel window and copy to open workbook

Using outlook VBA - I would like to open an attachment in a particular instance of excel, and then copy the sheets from that attachment into an open workbook.
I've used a couple of code snippets from (Saving Outlook attachment with date in the filename and Check to see if Excel is open (from another Office 2010 App) to save an attachment from an email and then find the excel window I need to open it in - both work in isolated outlook test macros.
Trouble is, I can't seem to link the two parts together into working code, at the end of all of it I have:
Option Explicit
Private Declare Function newFindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function AccessibleObjectFromWindow& Lib "oleacc" _
(ByVal hwnd&, ByVal dwId&, riid As newGUID, xlWB As Object)
Private Const newOBJID_NATIVEOM = &HFFFFFFF0
Private Type newGUID
lData1 As Long
iData2 As Integer
iData3 As Integer
aBData4(0 To 7) As Byte
End Type
Sub AttachmentToExcel()
Dim obj As Object
Dim msg As Outlook.MailItem
Dim objAtt As Object, iDispatch As newGUID
Dim sPath As String, sFileName As String, sFile As String, filewithoutExt As String
Dim attachFileName As String, DealID As String
Dim srcWorkbook As Object
sPath = "\\eu.insight.com\users\mklefass\Data\Desktop\"
sFileName = "Test Workbook.xlsx": filewithoutExt = "Test Workbook.xlsx"
sFile = sPath & sFileName
Set obj = GetCurrentItem
If TypeName(obj) = "MailItem" Then
Set msg = obj
DealID = FindDealID(msg.Subject)
For Each objAtt In msg.Attachments
If Right(objAtt.FileName, 4) = ".txt" Then
attachFileName = "C:\Users\mklefass\Desktop\tmp\" & objAtt.FileName & ".tsv"
objAtt.SaveAsFile attachFileName
Set objAtt = Nothing
End If
Next
' Find window that has our main workbook open
Dim dsktpHwnd As Long, hwnd As Long, mWnd As Long, cWnd As Long, wb As Object
newSetIDispatch iDispatch
dsktpHwnd = GetDesktopWindow
hwnd = newFindWindowEx(dsktpHwnd, 0&, "XLMAIN", vbNullString)
mWnd = newFindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
While mWnd <> 0 And cWnd = 0
cWnd = newFindWindowEx(mWnd, 0&, "EXCEL7", filewithoutExt)
hwnd = newFindWindowEx(dsktpHwnd, hwnd, "XLMAIN", vbNullString)
mWnd = newFindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
Wend
'~~> We got the handle of the Excel instance which has the file
If cWnd > 0 Then
'~~> Bind with the Instance
Debug.Print AccessibleObjectFromWindow(cWnd, newOBJID_NATIVEOM, iDispatch, wb)
'~~> Work with the file
Set srcWorkbook = wb.accParent.Application.Workbooks.Open(attachFileName)
'srcWorkbook.Worksheets(sheetNr).Copy after:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
srcWorkbook.Close
Set srcWorkbook = Nothing
End If
End If
End Sub
Private Sub newSetIDispatch(ByRef ID As newGUID)
With ID
.lData1 = &H20400
.iData2 = &H0
.iData3 = &H0
.aBData4(0) = &HC0
.aBData4(1) = &H0
.aBData4(2) = &H0
.aBData4(3) = &H0
.aBData4(4) = &H0
.aBData4(5) = &H0
.aBData4(6) = &H0
.aBData4(7) = &H46
End With
End Sub
SetIDispatch, Findwindowex, accessibleobjectfromwindow are all defined in Check to see if Excel is open (from another Office 2010 App) and are the same in my code.
The last line fails, with runtime error 438: Object doesn't support this property or method. This suggests to me that I'm probably barking up the wrong tree - I'm afraid though that I've no idea which tree to aim for!
Thanks in advance.
Two problems: AccessibleObjectFromWindow returns a Window object and the Open method is a member of Application.Workbooks; and the window title doesn't have the file extension.
So to solve the first issue:
Set srcWorkbook = wb.Application.Open(attachFileName)
needs to become:
Set srcWorkbook = wb.Parent.Application.Workbooks.Open(attachFileName)
And for the second in some installations of Excel:
cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", "Test Workbook.xlsx")
may need to become:
cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", "Test Workbook")
Note for future readers: This appears to depend on Windows and Excel versions, and whether or not you enable the "Hide known file extensions" in the windows explorer options.
Finally it seems that the window names need to be pointers (in 64-bit Office only):
Dim dsktpHwnd As Long, hwnd As Long, mWnd As Long, cWnd As Long, wb As Object
needs to become:
Dim dsktpHwnd As LongPtr, hwnd As LongPtr, mWnd As LongPtr, cWnd As LongPtr, wb As Object

Resources