WScript Command - Run Minimized? (MSAccess/VBA) - ms-access

I am performing a quick PING against the user-selected server IP to confirm it is reachable.
The following code does exactly what I need, except I would like to avoid the quick flash of the Command Shell window.
What do I need to modify to minimize that pesky CMD window?
SystemReachable (myIP)
If InStr(myStatus, "Reply") > 0 Then
' IP is Confirmed Reachable
Else
' IP is Not Reachable
End If
''''''''''''''''''''''
Function SystemReachable(ByVal strIP As String)
Dim oShell, oExec As Variant
Dim strText, strCmd As String
strText = ""
strCmd = "ping -n 1 -w 1000 " & strIP
Set oShell = CreateObject("WScript.Shell")
Set oExec = oShell.Exec(strCmd)
Do While Not oExec.StdOut.AtEndOfStream
strText = oExec.StdOut.ReadLine()
If InStr(strText, "Reply") > 0 Then
myStatus = strText
Exit Do
Else
myStatus = ""
End If
Loop
End Function

This question may be a little old but I figure that this answer may still be able to help.
(Tested with Excel VBA, have not been able to test with Access)
The WshShell.Exec Method enables the use of .StdIn, .StdOut, and .StdErr functions to write to and read from the consol window.
The WshShell.Run Method does not allow this functionality so for some purposes using Exec is required.
While it's true that there is no built in function to start the Exec method minimized or hidden you can use API's to quickly find the Exec window hwnd and minize/hide it.
My below script takes the ProcessID from the Exec object to find the window's Hwnd. With the Hwnd you can then set the window's show state.
From my testing with Excel 2007 VBA, in most cases I never even see the window... In some cases it might be visible for a few milliseconds but would only appear a quick flicker or blink... Note: I had better results using SW_MINIMIZE than I did with SW_HIDE, but you can play around with it.
I added the TestRoutine Sub to show an example of how to use the 'HideWindow' function.
The 'HideWindow' function uses the 'GetHwndFromProcess' function to get the window hwnd from the ProcessID.
Place the below into a Module...
Option Explicit
' ShowWindow() Commands
Public Const SW_HIDE = 0
Public Const SW_MINIMIZE = 6
'GetWindow Constants
Public Const GW_CHILD = 5
Public Const GW_HWNDFIRST = 0
Public Const GW_HWNDLAST = 1
Public Const GW_HWNDNEXT = 2
Public Const GW_HWNDPREV = 3
Public Const GW_OWNER = 4
' API Functions
Public Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Public Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function GetDesktopWindow Lib "user32" () As Long
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Sub TestRoutine()
Dim objShell As Object
Dim oExec As Object
Dim strResults As String
Set objShell = CreateObject("WScript.Shell")
Set oExec = objShell.Exec("CMD /K")
Call HideWindow(oExec.ProcessID)
With oExec
.StdIn.WriteLine "Ping 127.0.0.1"
.StdIn.WriteLine "ipconfig /all"
.StdIn.WriteLine "exit"
Do Until .StdOut.AtEndOfStream
strResults = strResults & vbCrLf & .StdOut.ReadLine
DoEvents
Loop
End With
Set oExec = Nothing
Debug.Print strResults
End Sub
Function HideWindow(iProcessID)
Dim lngWinHwnd As Long
Do
lngWinHwnd = GetHwndFromProcess(CLng(iProcessID))
DoEvents
Loop While lngWinHwnd = 0
HideWindow = ShowWindow(lngWinHwnd, SW_MINIMIZE)
End Function
Function GetHwndFromProcess(p_lngProcessId As Long) As Long
Dim lngDesktop As Long
Dim lngChild As Long
Dim lngChildProcessID As Long
On Error Resume Next
lngDesktop = GetDesktopWindow()
lngChild = GetWindow(lngDesktop, GW_CHILD)
Do While lngChild <> 0
Call GetWindowThreadProcessId(lngChild, lngChildProcessID)
If lngChildProcessID = p_lngProcessId Then
GetHwndFromProcess = lngChild
Exit Do
End If
lngChild = GetWindow(lngChild, GW_HWNDNEXT)
Loop
On Error GoTo 0
End Function
ShowWindow function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx
GetWindow function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515%28v=vs.85%29.aspx
GetDesktopWindow function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633504%28v=vs.85%29.aspx
GetWindowThreadProcessId function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633522%28v=vs.85%29.aspx
If you need more information on how the API's work, a quick google search will provide you with a ton of information.
I hope that this can help... Thank You.

Found a very workable and silent approach:
Dim strCommand as string
Dim strPing As String
strCommand = "%ComSpec% /C %SystemRoot%\system32\ping.exe -n 1 -w 500 " & myIP & " | " & "%SystemRoot%\system32\find.exe /i " & Chr(34) & "TTL=" & Chr(34)
strPing = fShellRun(strCommand)
If strPing = "" Then
MsgBox "Not Connected"
Else
MsgBox "Connected!"
End If
'''''''''''''''''''''''''''
Function fShellRun(sCommandStringToExecute)
' This function will accept a string as a DOS command to execute.
' It will then execute the command in a shell, and capture the output into a file.
' That file is then read in and its contents are returned as the value the function returns.
' "myIP" is a user-selected global variable
Dim oShellObject, oFileSystemObject, sShellRndTmpFile
Dim oShellOutputFileToRead, iErr
Set oShellObject = CreateObject("Wscript.Shell")
Set oFileSystemObject = CreateObject("Scripting.FileSystemObject")
sShellRndTmpFile = oShellObject.ExpandEnvironmentStrings("%temp%") & oFileSystemObject.GetTempName
On Error Resume Next
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 0, True
iErr = Err.Number
On Error GoTo 0
If iErr <> 0 Then
fShellRun = ""
Exit Function
End If
On Error GoTo err_skip
fShellRun = oFileSystemObject.OpenTextFile(sShellRndTmpFile, 1).ReadAll
oFileSystemObject.DeleteFile sShellRndTmpFile, True
Exit Function
err_skip:
fShellRun = ""
oFileSystemObject.DeleteFile sShellRndTmpFile, True
End Function

the run method of wscript already contains argumewnts to run minimized. So without all that effort shown above simply use
old code
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 0, True
new code
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 7, True
see Microsoft help for using the run method in wscript.
regards
Ytracks

Related

How can I display Access report in ac.preview view when rest of Access screen has been hidden?

I have a database and when it opens code runs which hides everything in Access apart from a form.
This looks great for the user but I believe this is preventing me from displaying Reports in acViewPreview view.
If I prevent the On Load code from running then I am able to view reports in acViewPreview view.
I do not fully understand the code that hides everything on the form On Load event (I copied it years ago and it worked but I cannot recall from where so cannot credit the actual creator).
The code that hides Access:
Private Sub Form_Load()
Call fSetAccessWindow(0)
End Sub
Option Compare Database
Option Explicit
Global Const SW_HIDE = 0
Global Const SW_SHOWNORMAL = 1
Global Const SW_SHOWMINIMIZED = 2
Global Const SW_SHOWMAXIMIZED = 3
Private Declare Function apiShowWindow Lib "user32" _
Alias "ShowWindow" (ByVal hwnd As Long, _
ByVal nCmdShow As Long) As Long
Function fSetAccessWindow(nCmdShow As Long)
Dim loX As Long
Dim loForm As Form
On Error Resume Next
Set loForm = Screen.ActiveForm
If Err <> 0 Then
loX = apiShowWindow(hWndAccessApp, nCmdShow)
Err.Clear
End If
If nCmdShow = SW_SHOWMINIMIZED And loForm.Modal = True Then
MsgBox "Cannot minimize Access with " _
& (loForm.Caption + " ") _
& "form on screen"
ElseIf nCmdShow = SW_HIDE And loForm.PopUp <> True Then
MsgBox "Cannot hide Access with " _
& (loForm.Caption + " ") _
& "form on screen"
Else
loX = apiShowWindow(hWndAccessApp, nCmdShow)
End If
fSetAccessWindow = (loX <> 0)
End Function
How do I either temporarily reverse/disable the On Load code but only when a Report is run or perhaps there is an alternative way of viewing the Report that would work whilst Access is hidden by the On Load code?
Comment out the code line (the single-quote) to prevent the call of the function:
Private Sub Form_Load()
' Call fSetAccessWindow(0)
End Sub
or call it to "show the window normal":
Private Sub Form_Load()
Call fSetAccessWindow(1)
End Sub

Download File from IE11 + Create a folder to store it

I had lots of issues dealing with that IE 11 download bar when downloading a file.
I checked different solutions but the only way to make it work the most reliably possible was to put two of them together.
Then I set the default internet download folder as my Desktop so that whenever I download a file with SendKeys I know where to find it with the code.
For the little story, my code is downloading the attached files for all the different incident cases. The number/type of attachments can vary and to oragnize it a little bit I decided to create a folder with the name of the incident case and store the attachments inside.
So here is my code, if you see a part which could be improved let me know :)
Option Explicit
Private objIE As SHDocVw.InternetExplorer
Private ContentFrame As HTMLIFrame
Public Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long
Public Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Public Const MOUSEEVENTF_LEFTDOWN = &H2
Public Const MOUSEEVENTF_LEFTUP = &H4
Public Const MOUSEEVENTF_RIGHTDOWN As Long = &H8
Public Const MOUSEEVENTF_RIGHTUP As Long = &H10
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub LeftClick()
mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
Sleep 50
mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
End Sub
Sub DownloadAttachment()
'make sure Cell A4 isn't empty because it has to contain the incident case
If Sheets(1).Cells(4, 1) = "" Or Sheets(1).Cells(4, 1) = " " Then End
'make sure it's a valid case No. before going on
On Error GoTo Fin
If Len(Cells(4, 1)) <> 8 Or CLng(Sheets(1).Cells(4, 1)) = 0 Then
MsgBox "Please enter a valid Case No."
End
End If
Call GetDataFromIntranet(Sheets(1).Cells(4, 1)
'Delete content on cell A4
Fin:
Sheets(1).Cells(4, 1) = ""
End Sub
Function GetDataFromIntranet(CaseNo As Long)
Dim i As Integer
If ("attachmentDivId").Children(0).Children(1).Children.Length >= 1 Then Call CreateFolder(CaseNo) ' If there is at least 1 attachment then we'll create a folder which has the name of the incident case
For i = 0 To objIE.document.frames(1).frames(1).document.getElementById("attachmentDivId").Children(0).Children(1).Children.Length - 1 ' For each attachment...
RetourALaCaseDepart:
objIE.document.frames(1).frames(1).document.getElementById("attachmentDivId").Children(0).Children(1).Children(i).Click ' Click on it so that it gets activated (blue)
objIE.document.frames(1).frames(1).document.getElementsByName("download")(0).Click 'Click on Save as
'The following bit send keyboard keys to cope with the Internet Downaload window that appears down the page -> downloads the file and save it on the Desktop
Application.Wait Now + TimeSerial(0, 0, 10)
Application.SendKeys "%{S}"
Application.Wait Now + TimeSerial(0, 0, 10)
SendKeys "{F6}", True
SendKeys "{TAB}", True
SendKeys "{ENTER}", True
'Here we close the Desktop window which sometimes open because it can alter the SendKey codes which is very sensitive
Dim objShellWindows As New SHDocVw.ShellWindows
Dim win As Object
For Each win In objShellWindows
If win.LocationName = "Desktop" Then
win.Quit
End If
Next win
Application.Wait Now + TimeSerial(0, 0, 1)
If MakeSureDownloaded(objIE.document.frames(1).frames(1).document.getElementById("attachmentDivId").Children(0).Children(1).Children(i).Children(0).innerText, CaseNo) = False Then GoTo RetourALaCaseDepart ' We check if the attachment was successfully saved, if not we redo the saving process from "RetourALaCaseDepart
Next i
Exit Function
Fini:
MsgBox "No attachments found or attachment tab not found"
End Function
Function MakeSureDownloaded(FileName As String, CaseNo As Long) As Boolean
Dim FileSys As Object 'FileSystemObject
Dim objFile As Object 'File
Dim myFolder
Dim strFilename As String
Const myDir As String = "C:\Users\Seb\Desktop\"
'set up filesys objects
Set FileSys = CreateObject("Scripting.FileSystemObject") 'New FileSystemObject
Set myFolder = FileSys.GetFolder(myDir)
For Each objFile In myFolder.Files
If objFile.Name Like FileName & "*" Then ' If the file was saved then we will add it to the folder created earlier for that Case
strFilename = objFile.Name
MakeSureDownloaded = True
GoTo BienBien
End If
Next objFile
MakeSureDownloaded = False
Set FileSys = Nothing
Set myFolder = Nothing
Exit Function
BienBien:
Dim fso As Object
Set fso = VBA.CreateObject("Scripting.FileSystemObject")
Call fso.MoveFile("C:\Users\Seb\Desktop\" & strFilename, "Path...\Case_Attachments\" & CaseNo & "\" & strFilename)
Set FileSys = Nothing
Set myFolder = Nothing
End Function
Sub CreateFolder(CaseNo As Long)
Dim fsoFSO
Set fsoFSO = CreateObject("Scripting.FileSystemObject")
If fsoFSO.FolderExists("Path...\Case_Attachments\" & CaseNo) Then ' do nothing actually...
Else
fsoFSO.CreateFolder ("Path...\Case_Attachments\" & CaseNo)
End If
End Sub

How can I bring my current Access DB to foreground via VBA?

I'm trying to get my access db to come to the foreground based on the couple of lines, below. I'm not sure why it isn't working though, or if there are better methods to do this.
From what I've read, this should work:
Access.Visible = False
Access.Visible = True
But doesn't actually bring the database to the front.
Edit for more info:
Private Sub Form_Open(Cancel As Integer)
getStrUserName = Environ("username")
dbName = "myDB.accdb" ' database name
versionChckDB = "versionCheckDB.accdb" ' version check db name
strServer = "C:\My\Path\to\Server" ' server location string
strDesktop = "C:\My\Path\to\Local" ' desktop location string
strVersionCheck = "C:\My\Path\to\Version" ' version check location
Static acc As Access.Application
Dim db As DAO.Database
Dim strDbName As String
If FileLocked(strDesktop & "\" & versionChckDB) Then
Dim objAccess As Access.Application
Set objAccess = GetObject(strDesktop & "\" & versionChckDB)
objAccess.Application.Quit acQuitSaveAll
Set objAccess = Nothing
DoCmd.OpenForm "frmMainMenu"
DoCmd.RunCommand acCmdAppMaximize
Access.Visible = False
Access.Visible = True
GoTo exitSub
Else
strDbName = strDesktop & "\" & versionChckDB
Set acc = New Access.Application
acc.Visible = True
Set db = acc.DBEngine.OpenDatabase(strDbName, False, False)
acc.OpenCurrentDatabase strDbName
End If
'db.Close
exitSub:
Call SetForegroundWindow(Application.hWndAccessApp) ' bringing access DB to foreground
End Sub
Usually one uses an API function for that.
From http://www.access-programmers.co.uk/forums/showthread.php?t=132129 :
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
and then
Call SetForegroundWindow(Application.hWndAccessApp)
Edit
If you want to get the newly opened Access application window to the front, you need its hWnd:
Call SetForegroundWindow(acc.hWndAccessApp)
Edit 2
This works for me. Notepad is briefly in the foreground, then the Access window.
Module:
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Sub TestAccessToForeground()
Sleep 300
Shell "notepad.exe", vbNormalFocus
Sleep 300
Call SetForegroundWindow(Application.hWndAccessApp)
End Sub
Form:
Private Sub Form_Open(Cancel As Integer)
Call TestAccessToForeground
End Sub

MS Access application return value

I need to make automatic application called from windows command line. This app start, make some DB operations and automatically exit.
Next I need to handle return values from Access. I tried everything what I find on Google but Access returns 0 every time. I need to return 1 or something else to tell cmd that there is error in database.
here is some code in VBA (access call Form when application starts):
Private Sub Form_Load()
Dim xSQL As String
Dim xRS As DAO.Recordset
xSQL = "select * from table1"
Set xRS = CurrentDb.OpenRecordset(xSQL, dbOpenDynaset)
xRS.MoveLast: xRS.MoveFirst
DoEvents
If xRS!field1 = "" Or IsNull(xRS!field1) Then
' HERE I WANT TO RETURN 1 OR ANY TRUE VALUE = ERROR
Application.Quit acQuitSaveAll
End If
' HERE I RETURN 0 = SUCCESS
Application.Quit acQuitSaveAll
End Sub
Some ideas how to do it?
I think your best solution can be setting an environment variable with the desired return value.
From your VBA code:
Private Declare Function SetEnvironmentVariable Lib "kernel32" _
Alias "SetEnvironmentVariableA" _
(ByVal lpName As String, _
ByVal lpValue As String) As Long
Private Sub Form_Load()
Dim xSQL As String
Dim xRS As DAO.Recordset
xSQL = "select * from table1"
Set xRS = CurrentDb.OpenRecordset(xSQL, dbOpenDynaset)
xRS.MoveLast: xRS.MoveFirst
DoEvents
If xRS!field1 = "" Or IsNull(xRS!field1) Then
SetEnvironmentVariable "ACCESSEXITCODE", "1"
Application.Quit acQuitSaveAll
End If
SetEnvironmentVariable "ACCESSEXITCODE", "0"
Application.Quit acQuitSaveAll
End Sub
From de command line:
IF %ACCESSEXITCODE%==1 GOTO HandleError

VBA script to close every instance of Excel except itself

I have a subroutine in my errorhandling function that attempts to close every workbook open in every instance of Excel. Otherwise, it might stay in memory and break my next vbscript. It should also close every workbook without saving any changes.
Sub CloseAllExcel()
On Error Resume Next
Dim ObjXL As Excel.Application
Set ObjXL = GetObject(, "Excel.Application")
If Not (ObjXL Is Nothing) Then
Debug.Print "Closing XL"
ObjXL.Application.DisplayAlerts = False
ObjXL.Workbooks.Close
ObjXL.Quit
Set ObjXL = Nothing
Else
Debug.Print "XL not open"
End If
End Sub
This code isn't optimal, however. For example, it can close 2 workbooks in one instance of Excel, but if you open 2 instances of excel, it will only close out 1.
How can I rewrite this to close all Excel without saving any changes?
Extra Credit:
How to do this for Access as well without closing the Access file that is hosting this script?
You should be able to use window handles for this.
Public Sub CloseAllOtherAccess()
Dim objAccess As Object
Dim lngMyHandle As Long
Dim strMsg As String
On Error GoTo ErrorHandler
lngMyHandle = Application.hWndAccessApp
Set objAccess = GetObject(, "Access.Application")
Do While TypeName(objAccess) = "Application"
If objAccess.hWndAccessApp <> lngMyHandle Then
Debug.Print "found another Access instance: " & _
objAccess.hWndAccessApp
objAccess.Quit acQuitSaveNone
Else
Debug.Print "found myself"
Exit Do
End If
Set objAccess = GetObject(, "Access.Application")
Loop
ExitHere:
Set objAccess = Nothing
On Error GoTo 0
Exit Sub
ErrorHandler:
strMsg = "Error " & Err.Number & " (" & Err.Description _
& ") in procedure CloseAllOtherAccess"
MsgBox strMsg
GoTo ExitHere
End Sub
It appears to me GetObject returns the "oldest" Access instance. So that sub closes all Access instances started before the one which is running the sub. Once it finds itself, it stops. Maybe that's fine for your situation. But if you need to also close Access instances started after the one which is running the code, look to Windows API window handle functions.
I didn't try this approach for Excel. But I did see Excel provides Application.Hwnd and Application.Hinstance ... so I suspect you can do something similar there.
Also, notice I got rid of On Error Resume Next. GetObject will always return an Application object in this sub, so it didn't serve any purpose. Additionally, I try to avoid On Error Resume Next in general.
Update: Since GetObject won't do the job for you, use a different method to get the window handles of all the Access instances. Close each of them whose window handle doesn't match the one you want to leave running (Application.hWndAccessApp).
Public Sub CloseAllAccessExceptMe()
'FindWindowLike from: '
'How To Get a Window Handle Without Specifying an Exact Title '
'http://support.microsoft.com/kb/147659 '
'ProcessTerminate from: '
'Kill a Process through VB by its PID '
'http://en.allexperts.com/q/Visual-Basic-1048/Kill-Process-VB-its-1.htm '
Dim lngMyHandle As Long
Dim i As Long
Dim hWnds() As Long
lngMyHandle = Application.hWndAccessApp
' get array of window handles for all Access top level windows '
FindWindowLike hWnds(), 0, "*", "OMain", Null
For i = 1 To UBound(hWnds())
If hWnds(i) = lngMyHandle Then
Debug.Print hWnds(i) & " -> leave myself running"
Else
Debug.Print hWnds(i) & " -> close this one"
ProcessTerminate , hWnds(i)
End If
Next i
End Sub
Differentiating open instances of an application is a very old problem, and it is not unique to VBA.
I've tried to figure this out myself over the years, never with greater success than the time before.
I think the long and short of it is that you can never know if the application instance you're referencing is the one in which the code is executing (so terminating it might leave other instances open).
I just tried the following with both Excel and Access :
Dim sKill As String
sKill = "TASKKILL /F /IM msaccess.exe"
Shell sKill, vbHide
If you change the msaccess.exe to excel.exe, excel will be killed.
If you want a bit more control over the process, check out:
http://www.vbaexpress.com/kb/getarticle.php?kb_id=811
I know this is an old post but for those who visit here from searches may find it helpful.
This code was found and modified. It will give you every SHEET in every WORKBOOK in every INSTANCE. From there you can determine the active instance.
Module..............
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
Declare Function GetClassName Lib "User32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Declare Function IIDFromString Lib "ole32" (ByVal lpsz As Long, ByRef lpiid As UUID) As Long
Declare Function AccessibleObjectFromWindow Lib "oleacc" (ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As UUID, ByRef ppvObject As Object) As Long
Type UUID 'GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Code…………………...
Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Const OBJID_NATIVEOM As Long = &HFFFFFFF0
Sub ListAll()
Dim I As Integer
Dim hWndMain As Long
On Error GoTo MyErrorHandler
hWndMain = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)
I = 1
Do While hWndMain <> 0
Debug.Print "Excel Instance " & I
GetWbkWindows hWndMain
hWndMain = FindWindowEx(0&, hWndMain, "XLMAIN", vbNullString)
I = I + 1
Loop
Exit Sub
MyErrorHandler:
MsgBox "GetAllWorkbookWindowNames" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Sub
Sub GetWbkWindows(ByVal hWndMain As Long)
Dim hWndDesk As Long
Dim hWnd As Long
Dim strText As String
Dim lngRet As Long
On Error GoTo MyErrorHandler
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 = GetClassName(hWnd, strText, 100)
If Left$(strText, lngRet) = "EXCEL7" Then
GetExcelObjectFromHwnd hWnd
Exit Sub
End If
hWnd = FindWindowEx(hWndDesk, hWnd, vbNullString, vbNullString)
Loop
On Error Resume Next
End If
Exit Sub
MyErrorHandler:
MsgBox "GetWbkWindows" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Sub
Function GetExcelObjectFromHwnd(ByVal hWnd As Long) As Boolean
Dim fOk As Boolean
Dim I As Integer
Dim obj As Object
Dim iid As UUID
Dim objApp As Excel.Application
Dim myWorksheet As Worksheet
On Error GoTo MyErrorHandler
fOk = False
Call IIDFromString(StrPtr(IID_IDispatch), iid)
If AccessibleObjectFromWindow(hWnd, OBJID_NATIVEOM, iid, obj) = 0 Then 'S_OK
Set objApp = obj.Application
For I = 1 To objApp.Workbooks.Count
Debug.Print " " & objApp.Workbooks(I).Name
For Each myWorksheet In objApp.Workbooks(I).Worksheets
Debug.Print " " & myWorksheet.Name
DoEvents
Next
fOk = True
Next I
End If
GetExcelObjectFromHwnd = fOk
Exit Function
MyErrorHandler:
MsgBox "GetExcelObjectFromHwnd" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Function
I hope this helps someone :)
This is a response to an old post, but same as the poster in 2012, hopefully it helps someone who may come here based on a generic web search.
Background
My company uses XLSX "models" to turn our data into "pretty" automatically. The data exports from SAS as XLS; we do not have the licensing or add-ons to export as XLSX. The normal process is to copy/paste each of the 14 SAS outputs into the XLSX. The code below iterates through the first two exports where data is copied from the XLS, pasted into the XLSX, and the XLS closed.
Please note: The XLSX file is saved to the hard drive. The XLS files are NOT SAVED, i.e. the path goes to "My Documents/" but there is no file name or file visible there.
Sub Get_data_from_XLS_to_XLSX ()
Dim xlApp1 As Excel.Application
Dim xlApp2 As Excel.Application
'Speed up processing by turning off Automatic Calculations and Screen Updating
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
'Copies data from Book1 (xls) and pastes into ThisWorkbook (xlsx), then closes xls file
Set xlApp1 = GetObject("Book1").Application
xlApp1.Workbooks("Book1").Sheets("Sheet1").Range("A2:E2").Copy
Application.ThisWorkbook.Worksheets("Data1").Cells(5, 2).PasteSpecialPaste:=xlPasteValues
'Clears the clipboard, turns off the "do you want to save" alert, closes xls file
xlApp1.CutCopyMode = False
xlApp1.DisplayAlerts = False
xlApp1.Quit
xlApp1.DisplayAlerts = True
'Same as the first one above, but now it's a second/different xls file, i.e. Book2
Set xlApp2 = GetObject("Book2").Application
xlApp2.Workbooks("Book2").Sheets("Sheet1").Range("A2:E2").Copy
Application.ThisWorkbook.Sheets("Data2").Cells(10, 2).PasteSpecial Paste:=xlPasteValues
'Clears the clipboard, turns off the "do you want to save" alert, closes xls file
xlApp2.CutCopyMode = False
xlApp2.DisplayAlerts = False
xlApp2.Quit
xlApp2.DisplayAlerts = True
'Sub continues for 12 more iterations of similar code
End Sub
You need to be explicit in qualifying your statements. i.e. instead of Workbooks("Book_Name") make sure you identify the application you are referring to, be it Application.Workbooks("Book_Name") or xlApp1.Workbooks("Book_Name")
try putting it in a loop
Set ObjXL = GetObject(, "Excel.Application")
do until ObjXL Is Nothing
Debug.Print "Closing XL"
ObjXL.Application.DisplayAlerts = False
ObjXL.Workbooks.Close
ObjXL.Quit
Set ObjXL = Nothing
Set ObjXL = GetObject(, "Excel.Application") ' important!
loop