Hidden Excel Workbook after automation

M

malgoro

Hello to all,

From Access, I'm using VBA to create an Excel workbook with one sheet; then
I also have macros to export information from my database to that same
Workbook. As a result a get an .xls file with 5 sheets. After all this, I do
some formatting on the last 4 sheets and save it. Until that point all is
perfect, but when my users open the spreadsheet it's hidden and, of course, i
don't want them to have to go to windows, unhide in excel.

how can i avoid this problem? Many thanks in advance.
 
S

strive4peace

I am assuming this line is in your code:

xlApp.ActiveWindow.Visible = False

this will hide the active workbook -- so just reverse this before you
save it.

xlApp.ActiveWindow.Visible = true

Alternately, you can do this:

don't show what you are doing -->
xlApp.ScreenUpdating = False

then, you are not changing the workbook <smile>

WHERE
xlApp is the object variable for your Excel Application

Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*
 
M

malgoro

Let me copy parts of my code for you:

Dim Wbk, xlApp As Object

...
' --> this is the first part where is create the spreadsheet and generate
the first sheet

Set xlApp = CreateObject("Excel.Sheet")

xlApp.Application.cells(R, C + 1).Value = "Open items that should've
been closed by " & Date
xlApp.Application.Range("b1:d1").mergecells = True

xlApp.Application.cells(R, C + 5).Value = "OPEN"
xlApp.Application.cells(R, C + 11).Value = "ON HOLD"

....
....
....

xlApp.Application.Range("j" & R).addcomment
xlApp.Application.Range("j" & R).comment.Text "These are New Business"
xlApp.Application.Worksheets(1).Name = "Summary"

xlApp.SaveAs "\\WorkItems\WorkItems.xls"
DoCmd.RunMacro "macro2" ' --> This macro creates the other 4
sheets

xlApp.Application.Quit

' --> Then I open it again to do some formatting and after this is when it
becomes hidden:

Set xlApp = GetObject("\\WorkItems\WorkItems.xls")

Set Wbk = xlApp.Worksheets(2)
Wbk.Activate
Wbk.Range("a1:f1").interior.colorindex = 11
Wbk.Range("a1:f1").Font.colorindex = 2

Set Wbk = xlApp.Worksheets(3)
Wbk.Activate
Wbk.Range("a1:f1").interior.colorindex = 11
Wbk.Range("a1:f1").Font.colorindex = 2

xlApp.Save
xlApp.Application.Quit

------> non of this process is visible while it's being executed
------> I'm not using: xlApp.ActiveWindow.Visible = False, but i
included the code you suggested right before
xlapp save
without success; I got this message: "Object doesn't support this property
or method"

Thanks for your help
 
S

strive4peace

Hi Malgoro (is that your name?)

make xlApp an application variable, not a sheet variable

Set xlApp = CreateObject("Excel.Application")

make another variable for the sheet

also, while you are developing, use early binding (xlApp As
Excel.Application). You can switch to late binding (xlApp As Object)
after the code is done.

Dick's Clicks, Early Binding vs. Late Binding, by Dick Kusleika
http://www.dicks-clicks.com/excel/olBinding.htm

~~~

make Excel visible and single-step through your code to find the line
that causes it to hide.

xlApp.visible = true

put this statement in to stop the code:
Stop

then, press the F8 key to execute one line, F8 to execute the next line,
and so on. Press F5 to execute normally without stepping


Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*
 
M

malgoro

Hi Crystal,

First of all, thank you very, very much for your help, unfortunately for
you, I'm self tought and I do manage to do what's needed but I lack the
fundamentals so I have to bother you again with some other questions. Before
going any further, my name is Mauricio.

if instead of

Set xlApp = CreateObject("Excel.Sheet")

I use

Set xlApp = CreateObject("Excel.Application")

can I continue to use

xlApp.Application.cells(R, C + 1).Value ... ... ...

or should I change to

xlapp.cells(R... ... or something like that, since it's defined as an
application already?

or maybe I have to set the Wbk first?


sorry again, please be patient with me. Thanks in advance.
 
S

strive4peace

Hi Mauricio,

actually, to have less to change in your code, rename
xlApp --> xlSheet
since it represents a sheet, not an Application, and you can get to the
Application anytime by using
xlSheet.Application

without seeing all your code, I cannot really advise you on what object
variables are best to use

since a Range is on a sheet, you would NOT use the Application as you
did before
xlApp.Application.cells...
Excel is forgiving and lets you do this, but the logic is not right

if xlApp is really xlSheet, you just need
xlSheet.cells...

when you are in a VBE window, press the F2 key -- this gets you to the
Object Browser. It shows what properties and methods are available for
each type of object. In the left column, look at what is available for:
Application
Workbook
Worksheet
.... for starters :)

when you see something you want more information on, press F1 for Help


Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*
 
M

malgoro

Hi Crystal,

Okay, I put aside the project that brought me to you to take some time to
understand the basics a little bit more, so I created a form with just one
button with the following silly code:

Dim xlapp As Object
Dim xlwbk As Object
Dim xlsht As Object

Set xlapp = CreateObject("excel.application")
xlapp.Visible = True
Set xlwbk = xlapp.Workbooks.Add
Set xlsht = xlapp.ActiveWorkbook.Sheets(1)

xlsht.range("a1").Value = "hi"
xlwbk.Worksheets(1).Name = "MySheet"

Set xlsht = xlapp.ActiveWorkbook.Sheets(2)
xlsht.range("a2").Value = "hi"
xlwbk.Worksheets(2).Name = "MySheet2"

Set xlsht = xlapp.ActiveWorkbook.Sheets("MySheet")
xlsht.range("b1:d1").Value = "something else"
xlwbk.SaveAs "d:\documents and settings\mgonn\Desktop\mine.xls"
xlapp.Quit

Set xlapp = CreateObject("excel.application")
xlapp.Visible = True
Set xlwbk = GetObject("d:\documents and settings\mgonn\Desktop\mine.xls")
Stop
Set xlsht = xlwbk.Sheets("mihoja2")
xlsht.range("c1").Value = "another thing"
xlwbk.Save

I hope it seems to you like I'm getting better. Anyway, as soon as I use
Set xlapp = CreateObject("excel.application")
the second time, I'm not able to see what is happening with the rest of the
code and consecuently I can't figure out what is making that spreadsheet
hide, even if I press F2 as you suggested!

What can be still making that spreadsheet hidden?

thanks again.
 
M

malgoro

Crystal,
I'm sorry. I made a couple mistakes on my prior message:
instead of Set xlsht = xlwbk.Sheets("mihoja2")
it's Set xlsht = xlwbk.Sheets("MySheet2")

and it's not F2 what you suggested to debug step by step, it's F8.

Thanks.
 
R

RoyVidar

malgoro said:
Hi Crystal,

Okay, I put aside the project that brought me to you to take some
time to understand the basics a little bit more, so I created a form
with just one button with the following silly code:

Dim xlapp As Object
Dim xlwbk As Object
Dim xlsht As Object

Set xlapp = CreateObject("excel.application")
xlapp.Visible = True
Set xlwbk = xlapp.Workbooks.Add
Set xlsht = xlapp.ActiveWorkbook.Sheets(1)

xlsht.range("a1").Value = "hi"
xlwbk.Worksheets(1).Name = "MySheet"

Set xlsht = xlapp.ActiveWorkbook.Sheets(2)
xlsht.range("a2").Value = "hi"
xlwbk.Worksheets(2).Name = "MySheet2"

Set xlsht = xlapp.ActiveWorkbook.Sheets("MySheet")
xlsht.range("b1:d1").Value = "something else"
xlwbk.SaveAs "d:\documents and settings\mgonn\Desktop\mine.xls"
xlapp.Quit

Set xlapp = CreateObject("excel.application")
xlapp.Visible = True
Set xlwbk = GetObject("d:\documents and
settings\mgonn\Desktop\mine.xls") Stop
Set xlsht = xlwbk.Sheets("mihoja2")
xlsht.range("c1").Value = "another thing"
xlwbk.Save

I hope it seems to you like I'm getting better. Anyway, as soon as I
use Set xlapp = CreateObject("excel.application")
the second time, I'm not able to see what is happening with the rest
of the code and consecuently I can't figure out what is making that
spreadsheet hide, even if I press F2 as you suggested!

What can be still making that spreadsheet hidden?

thanks again.

I would say - don't create another instance of Excel, use the same
instance all the time.

Compare it with opening Excel in the interface, do something with
Excel, close Excel, then reopen it again, to do something else.

Usually, in the interface, too, one would work with the same instance
of Excel, but with different workbooks.

Now - if you alredy have one instance of Excel, and you use GetObject
to open a workbook, then that would probably also create another
instance of Excel.

Here's how I usually go about creating an instance of Excel, open a
workbook, close, save and quit Excel.

Sub rvsTesting()

Dim xl As Object
Dim wr As Object
Dim shIdx As Object
Dim sh As Object

Dim idx As Long
Dim SQL As String
Dim fxl As Boolean

On Error Resume Next

' instantiate an xl object varible, utilizing
' open xl-instance if such exists
fxl = True
Set xl = GetObject(, "excel.application")
If Err.Number <> 0 Then
' excel not running, instantiate
Err.Clear
Set xl = CreateObject("excel.application")
If Err.Number <> 0 Then
' ouch - is xl installed at all???
Err.Clear
Exit Sub
End If
fxl = Not fxl
End If

On Error GoTo myerr

Set wr = xl.Workbooks.Open("c:\test.xls")
Set shIdx = wr.Sheets(1)

' do something interesting with the workbook and/or sheet

myexit:
Set sh = Nothing
Set shIdx = Nothing
If Not wr Is Nothing Then
If Len(wr.Name) Then
wr.Close True
End If
End If
Set wr = Nothing

' Here, I'm quitting Excel, if it no instance of Excel
' was open prior to executing the code
If Not fxl Then
xl.Quit
End If
Set xl = Nothing
Exit Sub
myerr:
MsgBox Err.Description
Resume myexit
End Sub

If you wish the user to see the whole process, use

xl.visible = true

As in the interface, you can open more than one book in the same
instance of Excel, say by declaring an extra object variable and
do stuff

Dim wr2 as object
Dim sh2 as object

Set wr2 = xl.Workbooks.Open("c:\newtest.xls")
set sh2 = wr2.sheets(1)
sh2.Range("A1:C15").Value = sh.Range("A1:C15").Value

With sh2.Range("A1").CurrentRegion
.Columns.AutoFit
.HorizontalAlignment = -4108 ' xlCenter
.VerticalAlignment = -4107 ' xlBottom
End With

set sh2 = nothing
set sh = nothing
...
 
S

strive4peace

Hi Roy,

thanks for jumping in ...I have generic code for an Excel conversation
along the line of what you do ...but Nate, an esteemed Excel MVP, says
he does not like to "Hijack" another instance of Excel, so he does this:

Set xlApp = New Excel.Application 'you used xl for the variable name

Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*
 
R

RoyVidar

strive4peace said:
Hi Roy,

thanks for jumping in ...I have generic code for an Excel
conversation along the line of what you do ...but Nate, an esteemed
Excel MVP, says he does not like to "Hijack" another instance of
Excel, so he does this:

Set xlApp = New Excel.Application 'you used xl for the variable name

Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*

I'm not entirely sure what the "use early binding, always create new
instance" of yours is directed at, but I'll address some of the
issues, and try to relate it to the OPs challenge.

I always use CreateObject when doing automation, based on both
recommendations from MS and experience.

"When creating an instance of an Microsoft Office application, use
CreateObject instead of New. CreateObject more closely maps to the
creation process used by most Visual C++ clients, and allows for
possible changes in the server's CLSID between versions. CreateObject
can be used with both early-bound and late-bound objects."

http://support.microsoft.com/?kbid=244264

I never, ever use early binding when automating, a praxis which seems
to be recommended by most developers. Since my apps have to work 2000
through 2007, which also seems to be a common enough requirement,
early binding/vtable binding is not possible.

In addition, both since the OP is using late binding, and since the
exixtance and creation of extra and unnecessary instances of Excel
is a large part of the OPs problem, I think you're quite possibly
creating unneeded confusion by your advice.

But first "hi-jacking", as you call it, existing applications - well,
some of us have customers with limited computer recourses. On my
computer, opening three instances of Excel (in the interface) each
with a smallish workbook, uses 141MB (47MB each), while opening the
same three workbooks in the same instance, uses 48,5MB. So, say the
user clicks umpteen different report buttons, they will get umpteen
new instances of Excel, each with their own workbook containing the
report (47MB * umpteen instances -> computer slowing to a halt).

Also, with regards to recourses, seems to me, creating a new instance
of the automated application, takes more time than reusing an already
existing instance, and pretty much regardless of coding skills,
automation is dead slow.

This is why I mentioned, and also demonstrated how to use such, but
really, (re)using an existing instance of the automated application
(as I would say), is entirely outside the scope of the OPs problem,
as that is something you'd work on, or decide upon, with regards to
how to fetch the Excel instance in the first place, not how you use
it within the process.

Then to the problem.

The problem is that the OP first creates one instance of Excel, in
which a workbook is created, then formatted a bit, some sheets are
added etc. Then this workbook is closed, and they quit the Excel
application.

Note - the reference is not released, so quite likely, as it did on
my setup, the instance will be kept in memory.

Further down, a new instance of Excel is instantiated, which is set
to visible. This is what I addressed - there is no need to do this,
and this is actually one of the causes of the OPs problem, and has
nothing to do with whether the initial instance of Excel was a
preexisting instance, or created for this process.

Then, through the GetObject method, the same workbook which was
previously saved and closed, is opened to an existing instance of
Excel - but which of them?

Since most likely two instances of Excel exists in memory (or, with
your advice, they might have even more instances of Excel in memory),
which of these, will "inherit" the opened workbook? As indicated by
the OP, it isn't the last one instantiated (which was set to visible),
but the one created at the start of the routine (or perhaps,
following your advice, an instance of Excel that was open prior to
running this process?).

The two principles here, is primarily usage of the same Excel
instance throughout the whole process, or as I said last reply

"I would say - don't create another instance of Excel, use the same
instance all the time."

Then, it's the matter of object referencing. This is referred to as
"implicit", where one relies on the automated application to do
whatever it is one hoped it should do (quite commonly, and
especially when doing automation, it will do something entirely
else).

Here, there's no guidance regarding which Excel instance GetObject
should assign the newly opened workbook to -> implicit.

I find it imperative to be "explicit", so that there's no doubt
whatsoever, neither in my mind, nor for Access/VBA/the automated
application, which object belongs to which object hierarchy/parent
objects (here, the (correct) application object). Failing to do so,
often results in strange and unpredictable automation errors, 1004,
462, 429, -2147023174, -2147417848, or such as here, two instances
of Excel, one is visible, the other (which isn't visible), contains
the workbook... (are there more instances of Excel in memory? Check
with Task Manager (ctrl+shift+esc))

I must confess I find other object models much more forgiving than
the different Office application objects.

Now, by just using the same instance through the whole routine, which
was my main point, and some smallish changes, this could easily be
avoided. The smallish change, would be to use the .Workbooks.Open
method, and directly assign the workbook to a workbook variable (see
below).

Again, this has nothing to do with reusing an existing instance or
not (or "hi-jacking" as you say), but simply to use the same
instance through the whole process, regardless of whether it is a
separate instance created for this process, or reusing an existing
instance, in addition to explicit object referencing.

Here, rewriting the last piece trying to illustrate the technique,
(without reuse).

Dim xlapp As Object
Dim xlwbk As Object
Dim xlsht As Object

' Initializing - once
Set xlapp = CreateObject("excel.application")
xlapp.Visible = True
Set xlwbk = xlapp.Workbooks.Add
Set xlsht = xlapp.ActiveWorkbook.Sheets(1)

xlsht.Range("a1").Value = "hi"
xlwbk.Worksheets(1).Name = "MySheet"

Set xlsht = xlapp.ActiveWorkbook.Sheets(2)
xlsht.Range("a2").Value = "hi"
xlwbk.Worksheets(2).Name = "MySheet2"

Set xlsht = xlapp.ActiveWorkbook.Sheets("MySheet")
xlsht.Range("b1:d1").Value = "something else"
xlwbk.SaveAs "d:\documents and settings\mgonn\Desktop\mine.xls"

' no quitting and reinstantiating - keeping the
' same instance open
' xlapp.Quit -
' Set xlapp = CreateObject("excel.application")
' xlapp.Visible = True

' to open a workbook, use the .Workbooks.Open method
' Set xlwbk = GetObject("c:\mine.xls")
Set xlwbk = xlapp.Workbooks.Open( _
"d:\documents and settings\mgonn\Desktop\mine.xls")

Set xlsht = xlwbk.Sheets("MySheet2")
xlsht.Range("c1").Value = "another thing"
xlwbk.Save
Stop
' NOTE - close and release all object variables
Set xlsht = Nothing

' if necessary (if you quit, you should close first)
xlwbk.Close
Set xlwbk = Nothing

' if necessary
xlapp.Quit
Set xlapp = Nothing
 
M

malgoro

Hello,

Roy & Crystal, you both have provided me with so much knowledge!! Everything
is perfect now. I'm not creating another Excel instance, I'm using
xlapp.Workbooks.Open
instead of GetObject

I've learned how to navigate through my Excel sheets thanks to both of you
and my spreadsheet is no longer hidden!!

Happy New Year! :)
 
S

strive4peace

Hi Roy,

you bring up some compelling arguments to use an instance of Excel if it
is already there. It was interesting to read your memory figures, thank
you for sharing :)

Even when new instances are made, they would certainly not be created
multiple times within a program. I have always done as you do by
checking GetObject then using CreateObject if it is not available --
just thought I'd throw out another opinion. I did not say to always do
it -- but I know how it goes when you are scanning posts; perhaps I gave
that impression, didn't mean to.

I read some of the same things you did about using New, and am mainly an
Access programmer with some dangerous Excel skills -- figured one who
does it all the time would know better than I. On New not being
recommended: consider lookup fields, auto corrupt, and other things that
are promoted but are not good ideas.

I use early binding to develop and late binding to deploy -- so I am not
quite sure what you are referring to about binding but it is probably a
moot point.

Interesting theory as to why the workbook was hidden; sounds entirely
reasonable -- existing workbook that was already that way and due to not
releasing and no error handling, he never knew.

I see you Dim variables as I do as well -- first object variables, then
regular variables. Then I copy my Dim block for objects to my exit code
and modify to be sure I get them all ;)

~~~
Mauricio,

note on Close and Release:

The general rule on Close is that, if you Open it, you close it. A
workbook is opened, but a worksheet is not since it is part of a workbook.

set obj = nothing

This is called "releasing" -- and your exit code should (close, if
applicable, and) release object variables.

Any variable you use Set to assign should be released. Not all object
variables use Set, though -- ie CreateObject or For Each. Sometimes,
object variables clear themselves out, like if you loop though sheets
like this:

for each sht in xlWb.Sheets
....
next sht

if you do not break out of the loop, the sht variable is released
without you doing it; but if the loop is not completed, then it is not
-- so best to release it to be sure -- and it does not hurt to set
something to nothing that is already nothing <smile>

One way to tell if everything is released after you have run automation
code from Access on Excel is to look at the Task Manager (ctrl-alt-del).
If Excel is still running, something did not get released. If you
release the application but still have a worksheet variable, the
application may not release -- so order is important.

Also, it is important to include an error handler in all procedures

set up the Error Handler at the top of your program, right after the
procedure declaration (skip a line first for better readability)

then come the statements of your procedure

then the exit code and error handler at the bottom -- be sure to replace
ProcedureName

'~~~~~~~~~~~~~~~~~~~~~~
'set up Error Handler
On Error GoTo Proc_Err
'~~~~~~~~~~~~~~~~~~~~~~

... then your statements


put this at the end of the procedure

'~~~~~~~~~~~~~~~~~~~~~~
Proc_Exit:
On Error Resume Next
'(close and) release object variables if any
Exit Sub 'or Exit Function

Proc_Err:
MsgBox Err.Description, , _
"ERROR " & Err.Number _
& " ProcedureName"

Resume Proc_Exit

'if you want to single-step code to find error, CTRL-Break at MsgBox
'then set this to be the next statement
Resume
'~~~~~~~~~~~~~~~~~~~~~~

WHERE
ProcedureName is the name of your procedure so you can identify what
code the problem is in if you get an error message

The line labels do not matter (Proc_Exit:, Proc_Err:), I like to use the
same ones all the time -- they only have to be unique within a procedure.

if you get an error, press CTRL-BREAK when the message box pops up,
Enter to dismiss the dialog box

this takes you into the code

right-click on the *Resume* statement
from the shortcut menu, choose --> Set Next Statement

then press F8 to Resume with the statement that caused the problem --
you can fix it! -- or at least see what the problem is

pressing F8 executes one statement at a time

press F5 to continue execution automatically
'~~~~~~~~~~~~~~~~~~~~~~


Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*
 
S

strive4peace

very happy to hear that, Mauricio! you are welcome and Happy New Year
to you too <smile>

Warm Regards,
Crystal

remote programming and training

Access Basics
8-part free tutorial that covers essentials in Access
http://www.AccessMVP.com/strive4peace

*
:) have an awesome day :)
*
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top