K
Kevin McCartney
Hi, if you not familiar with api references you may not
want to try and help.
brief description: my database exports data to Excel, a
user reviews it and if the don't like it, they closes the
workbook window and not the application, Excel disappears
from the Task Bar but remains in the Task manager. I
retrieve the window handle of all hidden Excel session and
then I want to destroy them to close them so the stop
taking up resources. Problem the DESTROY WINDOW fails to
close the window handle that i pass it. The attached text
file contains an example simply put it in module and
execute the last sub procedure. I place comments where you
should put break points so you can start Task Manager and
then you'll see what I'm getting stressed about.
Hope you can help
Best regards
KM
Oh poo when did they not allow attachments. below is a lot
of code.
********************************************************
Option Explicit
Private Declare Function GetCurrentVbaProject
Lib "vba332.dll" Alias "EbGetExecutingProj" (hProject As
Long) As Long
Private Declare Function GetFuncID Lib "vba332.dll"
Alias "TipGetFunctionId" (ByVal hProject As Long, ByVal
strFunctionName As String, ByRef strFunctionId As String)
As Long
Private Declare Function GetAddr Lib "vba332.dll"
Alias "TipGetLpfnOfFunctionId" (ByVal hProject As Long,
ByVal strFunctionId As String, ByRef lpfn As Long) As Long
Private Declare Function GetWindowText Lib "user32"
Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString
As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32"
Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare Function GetClassName Lib "user32"
Alias "GetClassNameA" (ByVal hWnd As Long, ByVal
lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function IsWindowVisible Lib "user32"
(ByVal hWnd As Long) As Long
Private Declare Function EnumWindows Lib "user32" (ByVal
lpEnumFunc As Long, ByVal lngParam As Long) As Long
Private Declare Function DestroyWindow Lib "user32" (ByVal
hWnd As Long) As Long
Private Declare Function GetLastError Lib "kernel32" () As
Long
Private Declare Sub SetLastError Lib "kernel32" (ByVal
dwErrCode As Long)
Private Declare Function FormatMessage Lib "kernel32"
Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As
Any, ByVal dwMessageId As Long, ByVal dwLanguageId As
Long, ByVal lpBuffer As String, ByVal nSize As Long,
Arguments As Long) As Long
Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100
Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
Const LANG_NEUTRAL = &H0
Const SUBLANG_DEFAULT = &H1
Const ERROR_BAD_USERNAME = 2202&
Dim maryWindows() As String
Dim mZ As Integer
Private Function fncAddrOf(strFuncName As String) As Long
Dim hProject As Long
Dim lngResult As Long
Dim strID As String
Dim lpfn As Long
Dim strFuncNameUnicode As String
Const NO_ERROR = 0
' The function name must be in Unicode, so convert it.
strFuncNameUnicode = StrConv(strFuncName, vbUnicode)
' Get the current VBA project
Call GetCurrentVbaProject(hProject)
' Make sure we got a project handle... we always should,
but you never know!
If hProject <> 0 Then
' Get the VBA function ID (whatever that is!)
lngResult = GetFuncID(hProject, strFuncNameUnicode,
strID)
' We have to check this because we GPF if we try to
get a function pointer of a non-existent function.
If lngResult = NO_ERROR Then
' Get the function pointer.
lngResult = GetAddr(hProject, strID, lpfn)
If lngResult = NO_ERROR Then
fncAddrOf = lpfn
End If
End If
End If
End Function
Private Function fncCallBackFunc(ByVal hWnd As Long, ByVal
lngParam As Long) As Long
Dim strCaption As String
' The following expression is true unless lngParam is
True (you're looking only for visible
' windows) and the current window isn't visible. Look up
the Imp operator in online help for more info.
If CBool(lngParam) Imp IsWindowVisible(hWnd) Then
' Retrieve the window's caption. If it's got a
caption, add it to the list of windows.
strCaption = fncGetClassName(hWnd)
If Len(strCaption) > 0 And InStr(1,
strCaption, "XLMAIN") And IsWindowVisible(hWnd) = 0 Then
'frmCallbacks.AddItem strCaption, hwnd
ReDim Preserve maryWindows(mZ)
maryWindows(mZ) = Left(strCaption, InStr(1,
strCaption, "-") - 1)
mZ = mZ + 1
End If
End If
' Returning anything besides True causes the enumeration
to stop.
fncCallBackFunc = True
End Function
Private Function fncGetClassName(hWnd As Long) As String
Dim nCount As Long
Dim strBuffer As String
strBuffer = Space(80)
nCount = GetClassName(hWnd, strBuffer, 80)
fncGetClassName = hWnd & "-" & Mid(strBuffer, 1, nCount)
End Function
Public Sub subCloseHiddenExcel()
Dim Buffer As String
Dim hWnd As Long
Dim Z As Integer
mZ = 0
Call EnumWindows(fncAddrOf("fncCallBackFunc"), CLng
(False))
If Not IsEmpty(maryWindows) Then
For Z = 0 To UBound(maryWindows)
hWnd = CLng(maryWindows(Z))
' Place a breakpoint here because the next line is
supposed to destroy the hidden Excel Application.
' But the last error always states 'The specified
username is invalid'
If DestroyWindow(hWnd) = 0 Then
Buffer = Space(200)
SetLastError ERROR_BAD_USERNAME
FormatMessage FORMAT_MESSAGE_FROM_SYSTEM, ByVal
0&, GetLastError, LANG_NEUTRAL, Buffer, 200, ByVal 0&
MsgBox Buffer
End If
Next Z
End If
End Sub
Private Sub ExampleExcelHiddenWindow()
Dim xlapp As New Excel.Application
xlapp.SheetsInNewWorkbook = 1
xlapp.Workbooks.Add
' Numerous code to spin through several queries
exporting data and then format the sheets
' for presentational purpose adding sub totals and
adding graphs on additional sheets.
' In some case the user may not want to keep the report
because they would like to limit the
' queries again with different criteria driven from a
menu. Thus they just close the workbook window
' and the Excel application hides it self. I used to
force the user into saving the excel file so
' I could quit the application but users were not happy
having to save and delete unwanted reports.
' Thus I do not quit the application but I do not want
resources used up by hidden Excel sessions
' because the users don't close the application, they
just close the workbook.
' I've placed the code to simulate the problem. So place
a breakpoint on the below line, this will
' allow you to open Task Manager, you will then see an
Image Name EXCEL.EXE
' Now when you close the active workbook, (Use the close
window X on the window bar to simulate.)
' the Excel Application will disappear from your task
bar but
' remain in the tak manager, thus I want to use the
above procedure subCloseHiddenExcel to destroy
' the windows of hidden Excel sessions before a report
is generated. The only problem is the api
' call destroy window does not destroy the window handle
that I pass it and I don't know why.
xlapp.Visible = True
End Sub
*********************************************************
want to try and help.
brief description: my database exports data to Excel, a
user reviews it and if the don't like it, they closes the
workbook window and not the application, Excel disappears
from the Task Bar but remains in the Task manager. I
retrieve the window handle of all hidden Excel session and
then I want to destroy them to close them so the stop
taking up resources. Problem the DESTROY WINDOW fails to
close the window handle that i pass it. The attached text
file contains an example simply put it in module and
execute the last sub procedure. I place comments where you
should put break points so you can start Task Manager and
then you'll see what I'm getting stressed about.
Hope you can help
Best regards
KM
Oh poo when did they not allow attachments. below is a lot
of code.
********************************************************
Option Explicit
Private Declare Function GetCurrentVbaProject
Lib "vba332.dll" Alias "EbGetExecutingProj" (hProject As
Long) As Long
Private Declare Function GetFuncID Lib "vba332.dll"
Alias "TipGetFunctionId" (ByVal hProject As Long, ByVal
strFunctionName As String, ByRef strFunctionId As String)
As Long
Private Declare Function GetAddr Lib "vba332.dll"
Alias "TipGetLpfnOfFunctionId" (ByVal hProject As Long,
ByVal strFunctionId As String, ByRef lpfn As Long) As Long
Private Declare Function GetWindowText Lib "user32"
Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString
As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32"
Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare Function GetClassName Lib "user32"
Alias "GetClassNameA" (ByVal hWnd As Long, ByVal
lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function IsWindowVisible Lib "user32"
(ByVal hWnd As Long) As Long
Private Declare Function EnumWindows Lib "user32" (ByVal
lpEnumFunc As Long, ByVal lngParam As Long) As Long
Private Declare Function DestroyWindow Lib "user32" (ByVal
hWnd As Long) As Long
Private Declare Function GetLastError Lib "kernel32" () As
Long
Private Declare Sub SetLastError Lib "kernel32" (ByVal
dwErrCode As Long)
Private Declare Function FormatMessage Lib "kernel32"
Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As
Any, ByVal dwMessageId As Long, ByVal dwLanguageId As
Long, ByVal lpBuffer As String, ByVal nSize As Long,
Arguments As Long) As Long
Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100
Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
Const LANG_NEUTRAL = &H0
Const SUBLANG_DEFAULT = &H1
Const ERROR_BAD_USERNAME = 2202&
Dim maryWindows() As String
Dim mZ As Integer
Private Function fncAddrOf(strFuncName As String) As Long
Dim hProject As Long
Dim lngResult As Long
Dim strID As String
Dim lpfn As Long
Dim strFuncNameUnicode As String
Const NO_ERROR = 0
' The function name must be in Unicode, so convert it.
strFuncNameUnicode = StrConv(strFuncName, vbUnicode)
' Get the current VBA project
Call GetCurrentVbaProject(hProject)
' Make sure we got a project handle... we always should,
but you never know!
If hProject <> 0 Then
' Get the VBA function ID (whatever that is!)
lngResult = GetFuncID(hProject, strFuncNameUnicode,
strID)
' We have to check this because we GPF if we try to
get a function pointer of a non-existent function.
If lngResult = NO_ERROR Then
' Get the function pointer.
lngResult = GetAddr(hProject, strID, lpfn)
If lngResult = NO_ERROR Then
fncAddrOf = lpfn
End If
End If
End If
End Function
Private Function fncCallBackFunc(ByVal hWnd As Long, ByVal
lngParam As Long) As Long
Dim strCaption As String
' The following expression is true unless lngParam is
True (you're looking only for visible
' windows) and the current window isn't visible. Look up
the Imp operator in online help for more info.
If CBool(lngParam) Imp IsWindowVisible(hWnd) Then
' Retrieve the window's caption. If it's got a
caption, add it to the list of windows.
strCaption = fncGetClassName(hWnd)
If Len(strCaption) > 0 And InStr(1,
strCaption, "XLMAIN") And IsWindowVisible(hWnd) = 0 Then
'frmCallbacks.AddItem strCaption, hwnd
ReDim Preserve maryWindows(mZ)
maryWindows(mZ) = Left(strCaption, InStr(1,
strCaption, "-") - 1)
mZ = mZ + 1
End If
End If
' Returning anything besides True causes the enumeration
to stop.
fncCallBackFunc = True
End Function
Private Function fncGetClassName(hWnd As Long) As String
Dim nCount As Long
Dim strBuffer As String
strBuffer = Space(80)
nCount = GetClassName(hWnd, strBuffer, 80)
fncGetClassName = hWnd & "-" & Mid(strBuffer, 1, nCount)
End Function
Public Sub subCloseHiddenExcel()
Dim Buffer As String
Dim hWnd As Long
Dim Z As Integer
mZ = 0
Call EnumWindows(fncAddrOf("fncCallBackFunc"), CLng
(False))
If Not IsEmpty(maryWindows) Then
For Z = 0 To UBound(maryWindows)
hWnd = CLng(maryWindows(Z))
' Place a breakpoint here because the next line is
supposed to destroy the hidden Excel Application.
' But the last error always states 'The specified
username is invalid'
If DestroyWindow(hWnd) = 0 Then
Buffer = Space(200)
SetLastError ERROR_BAD_USERNAME
FormatMessage FORMAT_MESSAGE_FROM_SYSTEM, ByVal
0&, GetLastError, LANG_NEUTRAL, Buffer, 200, ByVal 0&
MsgBox Buffer
End If
Next Z
End If
End Sub
Private Sub ExampleExcelHiddenWindow()
Dim xlapp As New Excel.Application
xlapp.SheetsInNewWorkbook = 1
xlapp.Workbooks.Add
' Numerous code to spin through several queries
exporting data and then format the sheets
' for presentational purpose adding sub totals and
adding graphs on additional sheets.
' In some case the user may not want to keep the report
because they would like to limit the
' queries again with different criteria driven from a
menu. Thus they just close the workbook window
' and the Excel application hides it self. I used to
force the user into saving the excel file so
' I could quit the application but users were not happy
having to save and delete unwanted reports.
' Thus I do not quit the application but I do not want
resources used up by hidden Excel sessions
' because the users don't close the application, they
just close the workbook.
' I've placed the code to simulate the problem. So place
a breakpoint on the below line, this will
' allow you to open Task Manager, you will then see an
Image Name EXCEL.EXE
' Now when you close the active workbook, (Use the close
window X on the window bar to simulate.)
' the Excel Application will disappear from your task
bar but
' remain in the tak manager, thus I want to use the
above procedure subCloseHiddenExcel to destroy
' the windows of hidden Excel sessions before a report
is generated. The only problem is the api
' call destroy window does not destroy the window handle
that I pass it and I don't know why.
xlapp.Visible = True
End Sub
*********************************************************