Is Powerpoint still a single instance app?

H

Howard Kaikow

Steve Rindsberg said:
To summarize my (slightly less brief) email reply, I'm seeing somewhat similar
things with PPT2000, but in one case I saw the thread count go down almost
immediately on opening a new file - I'll have to leave it sit for a bit on a
different box to see if it behaves differently. Need to add a few fixes to the
code (little stuff; file not found errors when it tries to make the text file
the first time I think).

If you ran the code exactly as I posted, and in VB 6, there cannot be a file
not found error as the file is always re-created.

If you moved the code into a VBA app, then something happened when
transferring the code, e.g., the dim intfile statement could have been moved
to the wrong place,

I do not believe that it is worth further following the thread count
apprach.
 
H

Howard Kaikow

You would get a file not found error if you used the "Get Thread Count"
button BEFORE using the Start button.

That was intentional, as the Get Thread Count button makes no sense prior to
the Start button.
I forgot to tell you this.
Actually, better is to hide the Get Thread Start button in the load event
and make it visible on the btnStart event.
 
S

Steve Rindsberg

You would get a file not found error if you used the "Get Thread Count"
button BEFORE using the Start button.

Yep. Doublechecked the code after your last msg and realized the same thing.
That was intentional, as the Get Thread Count button makes no sense prior to
the Start button.
I forgot to tell you this.

NBD. You sent the code. It builds more character this way. ;-)
Actually, better is to hide the Get Thread Start button in the load event
and make it visible on the btnStart event.

For a production app, yes; for a simple "test fixture" we can live w/o it.
Swat the user upside the head, tell him this is a "DDT".
 
H

Howard Kaikow

I've concluded that the idea of using thread counts is a flawed approach.

When I 1st started this quest, I noticed that a particular data structure
does have a "Module use count in the context of the owning process". I did
not try using that variable because thaere was another variable in the
structure that has a "Global usage count on the module", but those two
values are ALWAYS the same , so I had no reason to know which definition was
valid.

Note that the above semantics are from the Feb 2003 MSDN library.

I then noticed that the semantics were changed in the latest MSDN library at
MSFT's web site. It now states:

GlblcntUsage is "The load count of the module, which is not generally
meaningful, and usually equal to 0XFFF".

ProccntUsage is "Same as GlblcmtUsage: the load count of the module, which
is not generally meaningful, and usually equal to 0XFFF".

At least that clears up that both values are supposed to be the same.

The semantics are very badly stated. The only reasonable interpretation is
that the counts for each module can be set any which way but loose at the
whim of the module/process software.

Further investigating, it does appear that the counts do vary on a process,
not a global, basis.
For example, I started Excel, Word and PoerPoint and found that there was no
apparent relationship for the counts, for the same module, across processes.

So I then changed the basis of my PPT detection code to use ProccntUsage
instead of thread counts.
Seemed to work, but again there is the issue of timing to get the updated
counts.

A few nights ago, just as I was goin to bed, I got the idea of trying
PowerPoint's events.
Alas, I did not jot down the idea and could not recall the details when I
awake.

Fortunately, somebody else suggested that approach, which reminded me of the
idea, so I'll next try using PPT events.

But first I've got some other things to do.
 
H

Howard Kaikow

Well, I looked at powerpernt's events.
Do not see any event that would let me know that the powerpernt session is
closing.

The usage count method likely would work, but the performance hot of waiting
for counts to update makes that solution impractical.

The MSFT powerpernt developers really screwed up by not providing a
mechanism to detect the number of uses in any practical way.
 
S

Steve Rindsberg

Well, I looked at powerpernt's events.
Do not see any event that would let me know that the powerpernt session is
closing.

The usage count method likely would work, but the performance hot of waiting
for counts to update makes that solution impractical.

The MSFT powerpernt developers really screwed up by not providing a
mechanism to detect the number of uses in any practical way.

Yes and no ... my guess is that their design brief simply didn't extend to
covering the kinds of situations we're looking at here. Of course that passes
the blame up the ladder a level is all <g>

PPT also seems to get the short end of the resource stick when it comes to this
sort of thing. A pity for us but justifiable on MS' part. Lots of people buy
Office to get Word and/or Excel and seem happy to have PPT thrown into the
deal. I don't imagine many buy it to get PPT and consider Word/Excel the nice
little "free bonus apps".
 
H

Howard Kaikow

My mind must have been working overtime whilst I slept last night (of
course, I did not get to bed until after 5 AM, so maybe it was today, not
last night).

Upon awakening, an idea popped into mind.

Seems to have narrowed the problem.

It appears t hat it ids NOT necessary to Quit a New instance of PPT created
by code, except in one circumstance I will describe below. If one just sets
the app object = Nothing and exits the code, the right thing happens in all
but 1 circumstance.

The problem occurs only when another use of PPT is started IN CODE and that
use is not visible. In thiscase, setting the app for the New instance =
nothing also kills the other non-visble instance started by code.

Note that if the other instance started by code is visible, then there's no
problem. Presentation count can even be 0.

This requires further investigation.
 
S

Steve Rindsberg

It appears t hat it ids NOT necessary to Quit a New instance of PPT created
by code, except in one circumstance I will describe below. If one just sets
the app object = Nothing and exits the code, the right thing happens in all
but 1 circumstance.

2 perhaps. Instances started by code may not go away when you set the object =
Nothing in PPT97 (and 2000 ... right, Mike?)
The problem occurs only when another use of PPT is started IN CODE and that
use is not visible. In thiscase, setting the app for the New instance =
nothing also kills the other non-visble instance started by code.

Note that if the other instance started by code is visible, then there's no
problem. Presentation count can even be 0.

This requires further investigation.

And less hair ... ;-)
 
H

Howard Kaikow

Steve Rindsberg said:
2 perhaps. Instances started by code may not go away when you set the object =
Nothing in PPT97 (and 2000 ... right, Mike?)

So far, testing only in Excel 2003, it appears that there's NEVER a
REQUIREMENT to use Quit or to set the PPT object to nothing. Of course, one
might do so in the context of a larger/longer running app to free resources,
BUT, as my headbanging these past few weeks has confirmed, it is NEVER safe
to release such objects if one's code created a NEW instance as it could
wipe other uses out of the water.

The only case that bothers me is when you create a NEW non-visible instance
in code AND add a presentation (not visible because the PPT is not
visible), if you shut down the code WITHOUT a QUIT, PPT goes away anyway.

This prevents one code from creating a critter that can be accessed by
separately running code. I guess MSFT figures that one can always SAVE the
presentation and the othe code can open the presentation. Probably, the
right thing do do, but it still bothers me,
 
H

Howard Kaikow

Well, I've just run a test in PowerPoint 97, 2000, 2002, and 2003.

Same resuls in all four (with 1 exception in, surprise!, PPT 97),

It appears that there's NEVER a REQUIREMENT to use Quit or to set the PPT
object to nothing. Indeed, one can get in trouble doing so.

Of course, one might wish to do so in the context of a larger/longer running
app to free resources,
BUT, as my headbanging these past few weeks has confirmed, it is NEVER safe
to release a PPT object, especially if one's code created a NEW instance, as
such an action could
wipe other uses of PPT, in particualr thaose started by other code.

The only case that bothers me is when you create a NEW non-visible instance
of PPT in code AND then some other code creates another non-visible intance
which adds a presentation (not visible because the PPT is not
visible), if you shut down the code WITHOUT a QUIT, PPT goes away anyway.
However, this does not occur with PPT 97. Idendependently of whether one
likes the idea of PPT affecting another session with a non-visible
presentation, that's a rather significant difference between PPT 97 and
later versions, You'd think that the Officve 2000 documentation would have
pointed this out. Has it? I've not checked.

The code I used for my test is given below, it's a hack, so it's not pretty.

Put the code in a VB 6 Form.

Form has 7 buttons and 1 listbox:

btnPPTNew: "Create New PPT"
btnWordNew: "Create New Word"
btnPPTVisible: "Start a Visible PPT"
btnPPTNonVisible: "Start a Non-Visible PPT"
btnPPTNonVisibleWithPresentation: "Start a Non-Visible PPT with
presentation"
btnByeBye: "ByeBye!"
btnClearList: "Clear List"

lstActions

A file named PPTTest.txt is created in the directory in which the code is
executed.
Use the Task Manger to watch when PowerPoint is created/deleted.

' Author: Howard Kaikow
' Author URL: http://www.standards.com/
' Date: 11 June 2005
Option Explicit
Private Const LB_SETHORIZONTALEXTENT = &H194

Private Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, lParam As Any) As Long

Private appPPTOriginal As PowerPoint.Application
Private appPPTVisible As PowerPoint.Application
Private appPPTNonVisible As PowerPoint.Application
Private appPPTNonVisibleWithPresentation As PowerPoint.Application
Private appWord As Word.Application

Private intFile As Integer

Private Sub btnPPTNonVisibleWithPresentation_Click()
Dim presPPT As PowerPoint.Presentation
On Error Resume Next
Set appPPTNonVisibleWithPresentation = GetObject(,
"PowerPoint.Application")
If Err.Number = 0 Then
Print #intFile, "PowerPoint: Another non-Visible, with presentation,
instance was created."
lstActions.AddItem "PowerPoint: Another non-Visible, with
presentation, instance was created."
Debug.Print "PowerPoint: Another non-Visible, with presentation,
instance was created."
Set presPPT = appPPTNonVisibleWithPresentation.Presentations.Add
Else
Print #intFile, "PowerPoint: Another non-Visible, with presentation,
Huh!"
lstActions.AddItem "PowerPoint: Another non-Visible, with
presentation, Huh!"
Debug.Print "PowerPoint: Another non-Visible, with presentation,
Huh!"
End If
On Error GoTo 0
btnPPTNonVisibleWithPresentation.Visible = False
End Sub

Private Sub btnPPTNew_Click()
On Error Resume Next
'Check if PowerPoint is running
Do
Set appPPTOriginal = GetObject(, "PowerPoint.Application")
If Err.Number = 0 Then
If vbCancel = MsgBox("Stop all running instances of PowerPoint,
and then choose Retry to continue this test." _
& vbCrLf & vbCrLf & "Or choose Cancel to cancel this test.",
vbInformation + vbRetryCancel, "PowerPoint is currently running") Then
Unload Me
Exit Sub
End If
Else
Err.Clear
Exit Do
End If
Loop
' Verify that PowerPoint is still not running
Set appPPTOriginal = GetObject(, "PowerPoint.Application")
If Err.Number = 0 Then
MsgBox "PowerPoint is still running", vbInformation + vbOK, "Test
cancelled"
Unload Me
Else
Set appPPTOriginal = New PowerPoint.Application
Print #intFile, "PowerPoint: New non-visible instance was created."
lstActions.AddItem "PowerPoint: New non-visible instance was
created."
Debug.Print "PowerPoint: New non-visible instance was created."
btnPPTNew.Visible = False
btnPPTVisible.Visible = True
btnPPTNonVisible.Visible = True
btnPPTNonVisibleWithPresentation.Visible = True
End If
On Error GoTo 0
btnClearList.Visible = True
End Sub

Private Sub btnPPTVisible_Click()
On Error Resume Next
Set appPPTVisible = GetObject(, "PowerPoint.Application")
If Err.Number = 0 Then
Print #intFile, "PowerPoint: Visible instance was created."
lstActions.AddItem "PowerPoint: Visible instance was created."
Debug.Print "PowerPoint: Visible instance was created."
appPPTVisible.Visible = True
Else
Print #intFile, "PowerPoint: Visible Huh!"
lstActions.AddItem "PowerPoint: Visible Huh!"
Debug.Print "PowerPoint: Visible Huh!"
End If
On Error GoTo 0
btnPPTVisible.Visible = False
End Sub

Private Sub btnPPTNonVisible_Click()
On Error Resume Next
Set appPPTNonVisible = GetObject(, "PowerPoint.Application")
If Err.Number = 0 Then
Print #intFile, "PowerPoint: Another non-Visible instance was
created."
lstActions.AddItem "PowerPoint: Another non-Visible instance was
created."
Debug.Print "PowerPoint: Another non-Visible instance was created."
Else
Print #intFile, "PowerPoint: Another non-Visible Huh!"
lstActions.AddItem "PowerPoint: Another non-Visible Huh!"
Debug.Print "PowerPoint: Another non-Visible Huh!"
End If
On Error GoTo 0
btnPPTNonVisible.Visible = False
End Sub

Private Sub btnWordNew_Click()
On Error Resume Next
'Check if Word is running
Do
Set appWord = GetObject(, "Word.Application")
If Err.Number = 0 Then
If vbCancel = MsgBox("Stop all running instances of Word, and
then choose Retry to continue this test." _
& vbCrLf & vbCrLf & "Or choose Cancel to cancel this test.",
vbInformation + vbRetryCancel, "Word is currently running") Then
Unload Me
Exit Sub
End If
Else
Err.Clear
Exit Do
End If
Loop
' Verify that Word is still not running
Set appWord = GetObject(, "Word.Application")
If Err.Number = 0 Then
MsgBox "Word is still running", vbInformation + vbOK, "Test
cancelled"
Unload Me
Else
Set appWord = New Word.Application
Print #intFile, "Word: New non-visible instance was created."
lstActions.AddItem "Word: New non-visible instance was created."
Debug.Print "Word: New non-visible instance was created."
Print #intFile, "Word: New non-visible instance was NOT Quit."
lstActions.AddItem "Word: New non-visible instance was NOT Quit."
Debug.Print "Word: New non-visible instance was NOT Quit."
Print #intFile, "Word: Use Task Manager to end WinWord.exe."
lstActions.AddItem "Word: Use Task Manager to end WinWord.exe."
Debug.Print "Word: Use Task Manager to end WinWord.exe."
btnWordNew.Visible = False
End If
On Error GoTo 0
End Sub

Private Sub Form_Load()
btnClearList.Visible = False
btnPPTVisible.Visible = False
btnPPTNonVisible.Visible = False
btnPPTNonVisibleWithPresentation.Visible = False
intFile = FreeFile
Open "PPTTest.txt" For Output As #intFile
End Sub

Private Sub Form_Activate()
With lstActions
SendMessage .hWnd, LB_SETHORIZONTALEXTENT, _
ScaleX(.Width, vbTwips, vbPixels) + 150, ByVal 0&
End With
End Sub

Private Sub btnByeBye_Click()
Dim PPTCount As Long
QuitPPT
Unload Me
End Sub

Private Sub QuitPPT()
If TypeName(appPPTOriginal) = "Application" Then
With appPPTOriginal
' Non-visible uses of PowerPoint added AFTER this code created
NEW non-visible
' instance get killed if there is no VISIBLE use of PowerPoint.
If .Presentations.Count = 0 Then
Print #intFile, "PowerPoint: (0)New non-visible instance was
NOT Quit."
lstActions.AddItem "PowerPoint: (0)New non-visible instance
was NOT Quit."
Debug.Print "PowerPoint: (0)New non-visible instance was NOT
Quit."
Else
Print #intFile, "PowerPoint: (not 0)New non-visible instance
was NOT Quit."
lstActions.AddItem "PowerPoint: (not 0)New non-visible
instance was NOT Quit."
Debug.Print "PowerPoint: (not 0)New non-visible instance was
NOT Quit."
End If
End With
End If
End Sub

Private Sub btnClearList_Click()
lstActions.Clear
End Sub

Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
Close #intFile
On Error GoTo 0
End Sub
 
M

Mike M.

When I want to let go of my PowerPoint instance in c++ I just call the
Release member. I don't do a quit. I start it up by doing a CreateInstance
of PowerPoint. Prior to 2002 there seemed to be a problem with reference
counting because my predecessor had commented out the code to do the release
with a notation about having trouble restarting another instance. I
implemented PowerPoint 2002 and put the release code back in and it worked
as expected.
 
H

Howard Kaikow

Mike M. said:
When I want to let go of my PowerPoint instance in c++ I just call the
Release member. I don't do a quit. I start it up by doing a CreateInstance
of PowerPoint. Prior to 2002 there seemed to be a problem with reference
counting because my predecessor had commented out the code to do the release
with a notation about having trouble restarting another instance. I
implemented PowerPoint 2002 and put the release code back in and it worked
as expected.

Ayup, I was looking into using the CreateInstance stuff, but I consider that
to be a poor choice.

PPT seems to kill the process as needed.
If there's a bug in any particular PPT version, too bad, attempting to
program around such an issue is not appropriate, as one cannot determine
whether other uses of PPT have started.

Everything I've ever seen states that the ONLY thing one can count on with
reference counts is whether they are 0 or knot.
 
H

Howard Kaikow

The bottom line.

The thing that get me sucked into this topic was a decision I made about
two months ago to include, in a VB 6 DLL, code for some Word macros that do
something with PPT via Automation.

In the DLL, for each Word macro, I've got code such as:

Public Sub DoWhateverWithPPT()
Dim appPPT As PowerPoint.Application

'Get existing instance of PowerPoint; otherwise create a new
one
' Powerpoint is a single instance application
On Error Resume Next
Set appPPT = GetObject(, "PowerPoint.Application")
If Err.Number <> 0 Then
Set appPPT = New PowerPoint.Application
End If
On Error GoTo 0

' Here: Do whatever with PPT

With appPPT
.ActivePresentation.Close
End With
End Sub

The PowerPoint process is created, and then removed from Task Manager,
wihout my using Quit or = Nothing as soon as the macro completes.

Why?
I expect because appPPT has scope local to the Sub and the reference goes
away when the Sub is exited.
If I were to fiddle around with Quit or = Nothing, then the code would risk
affecting other NON-VISIBLE uses of PPT that may have started AFTER a NEW
instance was created by the code. They may be affected anyway, but there's
nothing I can do about that as I cannot detect the other uses.
 
S

Steve Rindsberg

The bottom line.

The thing that get me sucked into this topic was a decision I made about
two months ago to include, in a VB 6 DLL, code for some Word macros that do
something with PPT via Automation.

In the DLL, for each Word macro, I've got code such as:

Public Sub DoWhateverWithPPT()
Dim appPPT As PowerPoint.Application

'Get existing instance of PowerPoint; otherwise create a new
one
' Powerpoint is a single instance application
On Error Resume Next
Set appPPT = GetObject(, "PowerPoint.Application")
If Err.Number <> 0 Then
Set appPPT = New PowerPoint.Application
End If
On Error GoTo 0

' Here: Do whatever with PPT

With appPPT
.ActivePresentation.Close
End With
End Sub

The PowerPoint process is created, and then removed from Task Manager,
wihout my using Quit or = Nothing as soon as the macro completes.

Why?
I expect because appPPT has scope local to the Sub and the reference goes
away when the Sub is exited.
If I were to fiddle around with Quit or = Nothing, then the code would risk
affecting other NON-VISIBLE uses of PPT that may have started AFTER a NEW
instance was created by the code. They may be affected anyway, but there's
nothing I can do about that as I cannot detect the other uses.


Not the solution you'd hoped for, but it ends up being a good deal simpler than
what you'd expected it to be. I guess that's A Good Thing?
 
H

Howard Kaikow

Not the solution you'd hoped for, but it ends up being a good deal simpler than
what you'd expected it to be. I guess that's A Good Thing?

It's not quite the solution I had hoped for.

For example, in a VB 6 class that needs an insrance of Excel or Word, I'd
create that instance and keep it alive for the life of the class.

Not sure I can do that with PPT because there's a chance somebody might
start another use while the class is still running.

I guess that I could keep the object alive for the life of the class, but,
as of now, I feel uncomfortable doing that.

At least I do not have to worry about PPT 97. One of the Word macros will
not run in Word 97, so I'm not going to support the DLL for Word 97.
 
S

Steve Rindsberg

Not sure I can do that with PPT because there's a chance somebody might
start another use while the class is still running.

Luckily, in most cases that will also create another presentation, so you can
trap a lot of the ugliness based on presentation count. Not perfect, but ...
well. What is?
 
H

Howard Kaikow

Steve Rindsberg said:
Luckily, in most cases that will also create another presentation, so you can
trap a lot of the ugliness based on presentation count. Not perfect, but ....
well. What is?

Presentation count is not relevant.

The only thing relevant is whether the PPT use is visible,
 
H

Howard Kaikow

The following seems to do the deed, where appPPT is a PPT object with scope
of the class, and the following code is included in each procedure that
needs PPT.

appPPT is never Quit nor set = Nothing.

If TypeName(appPPT) <> "Application" Then
'Get existing instance of PowerPoint; otherwise create a
new one
' Powerpoint is a single instance application
On Error Resume Next
Set appPPT = GetObject(, "PowerPoint.Application")
If Err.Number <> 0 Then
Set appPPT = New PowerPoint.Application
End If
On Error GoTo 0
End If

The worst case is when the code uses GetObject to set appPPT, but the NEW
instance that was created by somebody else isexited. In that case, the
TypeName test causes my class to reset appPPT and, so on .. ..
 
S

Steve Rindsberg

The following seems to do the deed, where appPPT is a PPT object with scope
of the class, and the following code is included in each procedure that
needs PPT.

appPPT is never Quit nor set = Nothing.

If TypeName(appPPT) <> "Application" Then
'Get existing instance of PowerPoint; otherwise create a
new one
' Powerpoint is a single instance application
On Error Resume Next
Set appPPT = GetObject(, "PowerPoint.Application")
If Err.Number <> 0 Then
Set appPPT = New PowerPoint.Application
End If
On Error GoTo 0
End If

The worst case is when the code uses GetObject to set appPPT, but the NEW
instance that was created by somebody else isexited. In that case, the
TypeName test causes my class to reset appPPT and, so on .. ..

And, to be certain I have the pieces in order, when the instance you create
here goes out of scope, PPT quits also, but only if your instance is the only
one?

Pretty neat!
 
H

Howard Kaikow

Steve Rindsberg said:
And, to be certain I have the pieces in order, when the instance you create
here goes out of scope, PPT quits also, but only if your instance is the only
one?

Pretty neat!

The PPT process automatically goes away when there are no more VISIBLE uses
of PPT, and when the NEW instance is Quit.

The implication is that if one never Quits PPT, then one does not adversely
affect others, but others can affect your code if they created the NEW
instance.

It appears that Outlook also works this way, tho i've not tested, so it
appears to be the MSFT "standard" for single instance apps.
 

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