4

Yes I know the problem is anything but new and I researched already A LOT but nothing helped me so far. Also this is my first stackoverflow post so if I do something wrong, please tell me. What I want to do is import multiple Excel sheets one at a time in access via VBA code. My code is working so far but I have the problem that there always remains an EXCEL.EXE instance in the task manager, causing problems when I want to import more than 1 file. I already tried the solutions proposed in following threads:

Closing Excel.exe process

VB.NET Excel Program Leaves EXCEL.EXE floating after completion

https://social.msdn.microsoft.com/Forums/en-US/908ba72a-3293-4eb9-b80e-fd2e6e78e185/vba-close-excel-problem?forum=accessdev

Remove Excel task from Task manager after running from Access using VBA

Here is my import code, I've only been using VBA for a few weeks, so please don't look too closely at it.

Public Function importExcelFiles()


Dim tablename As String
Dim spreadsheetType As String
Dim xlAppl As Excel.Application
Dim xlWB As Excel.Workbook
' **** function imports the excel file and copies data into target table  *********

' choose the file to import
path = OpenFile("Select file for import", "Excel-Files" & Chr$(0) & "*.xls; *.xlsx; *.xlsm", "Excel-Files", CurrentProject.path)

If Not IsNull(path) And Not path = "" And Not path = " " Then

    Set xlAppl = CreateObject("excel.application")
    Set xlWB = xlAppl.Workbooks.Open(path)
    
   
    spreadsheetType = acSpreadsheetTypeExcel9
    
    Call AlternateKKS("KKS_DB", xlAppl, xlWB)
    tablename = "KKS_DB"
    DoCmd.TransferSpreadsheet acImport, spreadsheetType, tablename, path, false, "merged!" 'import to excel'
            

    
    DoCmd.SetWarnings False
    xlAppl.DisplayAlerts = False
    xlWB.Close acSaveNo
    xlAppl.Quit
    
    Set xlWB = Nothing
    Set xlAppl = Nothing

    
End If


End Function 

I noticed that it works when I uncomment the Call AlternateKKS so probably there is something in there that keeps the Excel.exe from closing. There is quite some code in the function so before I post all of this I would like to know what I should be looking for that could cause the problem. What it does is basically read the sheet into an array, do some modification to the data and write it back on the sheet "merged". I already made sure that there is a xlWB. or xlAppl. whenever I refer to a sheet and I do not use the with statement as mentioned on the msdn forum (link in spoiler).

PS: On another note, the sheet I want to import is called "merged" but for some reason it only works if I put the ! at the end (a colleague told me to do it) but we have no idea why.

EDIT: Here is all the code that refers directly to the application or a sheet

Dim wsSource As Worksheet
Dim wsDestination As Worksheet

Public Function ReadIntoArray(name As String, ByRef xlWB As Excel.Workbook) As Variant


'    Dim ret As String
g_arrayrange = GetLastCell(name, xlWB)

Set wsSource = xlWB.Sheets(name)

Call Sort(wsSource, xlWB)

ReadIntoArray = wsSource.range("A1:" & g_arrayrange).Value


End Function

Sub WriteBackArray(data() As Variant, destination As String, ByRef xlWB As Excel.Workbook)


Set wsDestination = xlWB.Sheets(destination)

wsDestination.UsedRange.ClearContents

wsDestination.range("A1").resize(UBound(data, 1), UBound(data, 2)) = data


End Sub

Function GetLastCell(name As String, ByRef xlWB As Excel.Workbook) As String 'return coordinates of last cell with data'

Dim lrw As Long
Dim lcol As Long
Dim lastCell As String
Dim rng As range
Dim sheet As Worksheet
Dim col As String

Set sheet = xlWB.Sheets(name)
Set rng = xlWB.Sheets(name).Cells
'get last row '

lrw = rng.Find(What:="*", _
                After:=rng.Cells(1), _
                Lookat:=xlPart, _
                LookIn:=xlFormulas, _
                SearchOrder:=xlByRows, _
                SearchDirection:=xlPrevious, _
                MatchCase:=False).row


'get last column'
lcol = rng.Find(What:="*", _
                After:=rng.Cells(1), _
                Lookat:=xlPart, _
                LookIn:=xlFormulas, _
                SearchOrder:=xlByColumns, _
                SearchDirection:=xlPrevious, _
                MatchCase:=False).Column

g_lrw = lrw

'change to string for further use as range'
lastCell = GetCellAsString(lcol, lrw)

GetLastCell = lastCell


End Function

And in another function

source = xlAppl.Transpose(source)

Rest of the code only manipulates arrays

Community
  • 1
  • 1
J Doe
  • 53
  • 6
  • 1
    Is there any code related to Excel instance in the portion of code which you have not included in this post. Such as related to closing of Excel instance. Can you post the code of AlternativeKKS() – skkakkar Jun 29 '16 at 12:14
  • 1
    Look for *anywhere* in your app where you use any Excel objects or methods in conjunction with a WITH-block of code. It is a commonly known issue that Unless your code explicitly declares and clears automation-based object variables used as the object of a with-block (With...End With syntax), those objects can get stuck in memory as "orphaned" com objects. This will then prevent the release of their automation server classes (since a constituent object still has a non-zero COM reference count). This will present as the symptom you are observing: Excel won't close despite the call to .Quit. – skkakkar Jun 29 '16 at 12:22
  • Automation-based object instances should ALL be used like this: Dim objTemp as (Object/Excel.range/whatever) set objTemp = (whatever) With ObjTemp 'do stuff here End With set objTemp = Nothing what you should NOT be doing is this: With objExclApp.worksheets(1).range("myNamedrange") 'do stuff End With : THIS is the sort of thing that can leave phantom references for a range object [LINK](https://social.msdn.microsoft.com/Forums/en-US/908ba72a-3293-4eb9-b80e-fd2e6e78e185/vba-close-excel-problem?forum=accessdev) – skkakkar Jun 29 '16 at 12:26
  • It is appreciated that you have already referred the link which I have mentioned in my comments. Somehow it escaped my attention. – skkakkar Jun 29 '16 at 12:38
  • I deleted my answer as i miss-understood the issue. Sorry about that... Thanks @J Doe – Dawid SA Tokyo Jun 29 '16 at 12:41
  • I can post the whole code but I would prefer to be able to filter what is relevant before posting a few hundred lines therefore my question – J Doe Jun 29 '16 at 12:42
  • problem is: the error is probably in that piece of code. Try to narrow it down somehow and look for `With`-statements. – Tom K. Jun 29 '16 at 12:46
  • Alternatively you could just kill the process via shell: http://stackoverflow.com/questions/32101010/run-a-powershell-command-not-script-from-excel-vba – Tom K. Jun 29 '16 at 12:50
  • 1
    For variables like `wsSource` or `wsDestination`: have you removed their `Dim` statements? Or are they global variables? Or don't the modules have `Option Explicit`? (yeeks!) – Andre Jun 29 '16 at 13:26
  • They are globals, forgot to mention that, ill edit it – J Doe Jun 29 '16 at 14:12
  • 1
    Then you **must** do `Set wsSource = Nothing` etc. once they are no longer used, or they will hold their reference indefinitely. Local variables (that are passed as parameters to other functions where necessary) are safer in this regard. – Andre Jun 29 '16 at 14:57
  • Ill try this tomorrow and let you know if it solved the problem :) – J Doe Jun 29 '16 at 17:22
  • Try this: http://stackoverflow.com/questions/26303173/how-can-i-kill-task-manager-processes-through-vba-code – John Bingham Jun 30 '16 at 04:31

2 Answers2

2

Global variables (Dimed on module level) keep their values indefinitely, object variables keep their references. So a global variable like

Dim wsSource As Worksheet

will keep its reference to the Excel object, and thus prevent EXCEL.EXE from shutting down.

Solutions:

  1. Set wsSource = Nothing once it is no longer used. This can be tricky - you must handle every possible path through your program logic.

  2. Use local variables instead (Dimed on function level). Pass them as parameters to other functions where necessary. This is a safer way.

Andre
  • 26,751
  • 7
  • 36
  • 80
1

Since there seems to be no function that lets me mark Andre's comment as answer:

Then you must do Set wsSource = Nothing etc. once they are no longer used, or they will hold their reference indefinitely. Local variables (that are passed as parameters to other functions where necessary) are safer in this regard

Both of your suggested solutions worked! Set wsSource = Nothing when it was global as well as rewriting the code so that wsSource and wsDestination were local variables made the EXCEL.EXE close properly.

Thank you!

@Andre Feel free to post your comment as answer and I'll mark it as solution

J Doe
  • 53
  • 6