Is Powerpoint still a single instance app?

S

Steve Rindsberg

If we can keep him away from the teevie, we might have some neet code by
Monday. <g>
 
H

Howard Kaikow

Steve Rindsberg said:
If we can keep him away from the teevie, we might have some neet code by

Despite TV, VB .NET version is done.

Next is the one that counts, VB 6!
 
H

Howard Kaikow

Steve Rindsberg said:
Push, push! I think I can see its head already!

Appears to be done, except for cleaning up the fixed-length string buffers.

I also encountered a weird case, posted in
microsoft.public.vb.general.discussion with subject "Why would I have to use
If X = False, instead of If Not X, where X is Boolean?".
 
H

Howard Kaikow

Howard Kaikow said:
I also encountered a weird case, posted in
microsoft.public.vb.general.discussion with subject "Why would I have to use
If X = False, instead of If Not X, where X is Boolean?".

I fixed that.

C/C++ use a different value for True, so I modified th eC++, C#, VB .NET
and VB 6 versions to test for = 0 or for <> 0.

So, all 4 versions are done!.

Now to do a final test in Office 2003, then Office 2002, then Office 2000
and then Office 97, to see whether my hypothesis about the thread count is
valid.

If so, I'll make a test case that others can use to verify the result.
I'll need volunteers for that..
If interested, send me an email.
I will limit the number of volunteers for each version of Office to 3.
The test will require having VB 6 or the VB 6 runtimes (see
http://www.standards.com/index.html?UsingVB6).
 
S

Steve Rindsberg

I fixed that.

C/C++ use a different value for True, so I modified th eC++, C#, VB .NET
and VB 6 versions to test for = 0 or for <> 0.

O jeez. Here I thought I'd be teaching granny to suck eggs if I mentioned that
to you. <g> Seems the best thing to do is define your own constants for
true/false and change them as needed to suit the set of quirks local to the
programming tongue.
Now to do a final test in Office 2003, then Office 2002, then Office 2000
and then Office 97, to see whether my hypothesis about the thread count is
valid.

If so, I'll make a test case that others can use to verify the result.
I'll need volunteers for that..
If interested, send me an email.
I will limit the number of volunteers for each version of Office to 3.
The test will require having VB 6 or the VB 6 runtimes (see
http://www.standards.com/index.html?UsingVB6).

Anyone with Office 2000 or later should have the VB6 runtimes. IIRC they're
installed with Office.

IAC, I've got copies of all Office versions running under Win2K_in_VMWare and
can cross check at least one or two versions under native Win2k and/or XP and
would be happy to heave them all at your tests.

Give me a yell at steve at sign pptools dot com
 
H

Howard Kaikow

Steve Rindsberg said:
O jeez. Here I thought I'd be teaching granny to suck eggs if I mentioned that
to you. <g> Seems the best thing to do is define your own constants for
true/false and change them as needed to suit the set of quirks local to the
programming tongue.

I knew about that issue, but I was just trying to mechanically convert code
'tween the languages.
I've always objected to programming relying on internal representation, so
for languages not having a true boolean, the only proper course is to test
for =0 and said:
Anyone with Office 2000 or later should have the VB6 runtimes. IIRC they're
installed with Office.

IAC, I've got copies of all Office versions running under Win2K_in_VMWare and
can cross check at least one or two versions under native Win2k and/or XP and
would be happy to heave them all at your tests.

Give me a yell at steve at sign pptools dot com

OK, I'll count you as one of the volunteers.

Preliminary results, using PPT 2003, would indicate that the task is doable
based solely on the thread count.

First, I'm going to look over the rather bleak amount of PPT VBA stuff in
some books, then I'm going to more precisely state what is the problem.

I also have Office 97, Office 2000, Office XP and Office 2003.
All systems have VB 6 Enterprise.
The Office XP system has VS .NET Pro 2002.
The Office 2003 system has VS .NET Pro 2003.

Do not have .NET on the Office 97 or Office 2000 systems.
 
S

Steve Rindsberg

I knew about that issue, but I was just trying to mechanically convert code
'tween the languages.
I've always objected to programming relying on internal representation, so
for languages not having a true boolean, the only proper course is to test


OK, I'll count you as one of the volunteers.

Preliminary results, using PPT 2003, would indicate that the task is doable
based solely on the thread count.

First, I'm going to look over the rather bleak amount of PPT VBA stuff in
some books, then I'm going to more precisely state what is the problem.

I also have Office 97, Office 2000, Office XP and Office 2003.
All systems have VB 6 Enterprise.
The Office XP system has VS .NET Pro 2002.
The Office 2003 system has VS .NET Pro 2003.

Do not have .NET on the Office 97 or Office 2000 systems.

For the most part, .Net is .Not on the agenda here.
Let me know if that's a problem.
 
H

Howard Kaikow

For the most part, .Net is .Not on the agenda here.
Let me know if that's a problem.

Nope, my goal has always been a VB 6 DLL.

I have an automated test that does the following.

Since the only case of interest us when a NEW instance of PPT is started by
code, the testrefuses to run if PPT is alreadt running.

The test starts by creating a NEW PPT that is invisible, and displays the
thread count.
This had an unexpected problem, i.e., PPT takes its own sweet time about
creating the last thread, so I had to put in a Sleep before getting the
thread count. On my my ancient slow system, I had to use 2000 milliseconds,
1900 was not enough.

THe next step is to click on the Bye Bye button, which will determine
whether PPT should be Quit.

In the case, where the test created the PPT, and there are no other PPT
running, the PPT should be Quit.

Messages are displayed in a text file and in a listbox. It's the text file
that matters, because the listbox goes away in the ByeBye event.

Code works as expected, i.e., the NEW instance of PPT is Quit, subject to
the Sleep 2000 resulting in a correct initial thread count.

OK, but what if anther PPT is started before clicking the ByeBye button?

The test will instruct the user to start another instance of PPT, with no
presentation.
There is a button provided for getting the updated thread count at any time.

The only thing that matters is whether the current PPT thread count is not
equal to the initial PPT thread count.

If you now click on the Bye Bye button, the instance of PPT created by the
code does not get Quit as the different thread count is detected.

Everything is fine now, but is it really?

After the user starts another PPT, BEFORE clicking on the ByeBye button,
what if the user closes the user created PPT, should not that resulkt in the
sane condition as if the user had not created the PPT in the first place?

Of course, the answer is a resounding Yes!!

But, and I do have a big butt, it appears that PPT takes its own sweet time
killing its threads AFTER the user exits from PPT,

So, I had to include another Sleep in the ByeBye event to wait for the
threads to be deleted.

Alas, this can take some time, so I am currently using Sleep 60000 (30000
was not enough!).

I need to find out if there's a way to speed up killing the PPT threads
associated with the user's PPT, I have my doubts.

So the thread count mechanism I suggested does seem to work, at least with
Excel 2003, but the performance hit due to the Sleep 60000 may make it
impractical.
 
S

Steve Rindsberg

I'm sorry to hear about the timing issues; so close and then ... a slap in the
face with a wet rag. :-(
 
H

Howard Kaikow

Steve Rindsberg said:
I'm sorry to hear about the timing issues; so close and then ... a slap in the
face with a wet rag. :-(

Weather is pretty warm here lately, a wet rag might be welcome.

Actually, the "flushing" of the thread (yes, it's only a single thread!) is
at the whim of the OS, not really PPT.
I just ran a case that flushed almost immediately.
I guess it depends on what else is going on.

I hope there's a way to expedite the flushing of the thread.
 
H

Howard Kaikow

Here's the code I am currently using.
Form has 4 buttons and a listbox.

' Author: Howard Kaikow
' Author URL: http://www.standards.com/
' Date: 7 June 2005
Option Explicit
Private Const LB_SETHORIZONTALEXTENT = &H194
Private Const MAX_PATH As Long = 260
Private Const PROCESS_ALL_ACCESS As Long = &H1F0FFF
Private Const TH32CS_SNAPPROCESS As Long = &H2

Private Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * MAX_PATH
End Type

Private Declare Function CloseHandle Lib "KERNEL32.dll" _
(ByVal hObject As Long) As Long

Private Declare Function CreateToolhelp32Snapshot Lib "KERNEL32.dll" _
(ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long

Private Declare Function EnableWindow Lib "user32.dll" _
(ByVal hWnd As Long, ByVal fEnable As Long) As Long

Private Declare Function GetDesktopWindow Lib "user32.dll" () As Long

Private Declare Function OpenProcess Lib "KERNEL32.dll" _
(ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long

Private Declare Function Process32First Lib "KERNEL32.dll" _
(ByVal hSnapshot As Long, ByRef lppe As PROCESSENTRY32) As Long

Private Declare Function Process32Next Lib "KERNEL32.dll" _
(ByVal hSnapshot As Long, ByRef lppe As PROCESSENTRY32) As Long

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 Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private appPPT As PowerPoint.Application
Private hWndDesktop As Long
Private intFile As Integer
Private InitialThreadCount As Long

Private Sub btnGetThreadCount_Click()
Dim NowThreadCount As Long
NowThreadCount = DetectPPT()
Print #intFile, "PowerPoint: Current tread count = " &
CStr(NowThreadCount)
lstActions.AddItem "PowerPoint: Current tread count = " &
CStr(NowThreadCount)
End Sub

Private Sub btnStart_Click()
Dim status As Boolean

On Error Resume Next
'Check if PowerPoint is running
Do
Set appPPT = 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
DoEvents
' Disable keyboard and mouse.
EnableWindow hWndDesktop, False
' Verify that PowerPoint is still not running
Set appPPT = GetObject(, "PowerPoint.Application")
If Err.Number = 0 Then
MsgBox "PowerPoint is still running", vbInformation + vbOK, "Test
cancelled"
Unload Me
Else
intFile = FreeFile
Open "PPTTest.txt" For Output As #intFile
Set appPPT = New PowerPoint.Application
Print #intFile, "PowerPoint: New instance was created."
lstActions.AddItem "PowerPoint: New instance was created."
DoEvents
Sleep 5000
InitialThreadCount = DetectPPT()
Print #intFile, "PowerPoint: Current tread count = " &
CStr(InitialThreadCount)
lstActions.AddItem "PowerPoint: Current tread count = " &
CStr(InitialThreadCount)
End If
' Enable keyboard and mouse.
EnableWindow hWndDesktop, True
On Error GoTo 0
End Sub

Private Sub Form_Load()
' Get handle for Desktop
hWndDesktop = GetDesktopWindow()
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()
QuitPPT
On Error Resume Next
Close #intFile
On Error GoTo 0
Unload Me
End Sub

Private Sub QuitPPT()
Dim CurrentThreadCount As Long
If TypeName(appPPT) = "Application" Then
With appPPT
DoEvents
' Disable keyboard and mouse.
EnableWindow hWndDesktop, False
Sleep 60000
CurrentThreadCount = DetectPPT()
Print #intFile, "PowerPoint: Current tread count = " &
CStr(CurrentThreadCount)
lstActions.AddItem "PowerPoint: Current tread count = " &
CStr(CurrentThreadCount)
If .Presentations.Count = 0 Then
If CurrentThreadCount = InitialThreadCount Then
.Quit
Print #intFile, "PowerPoint: New instance was Quit."
lstActions.AddItem "PowerPoint: New instance was Quit."
Else
Print #intFile, "PowerPoint: New instance was NOT Quit."
lstActions.AddItem "PowerPoint: New instance was NOT
Quit."
End If
Else
Print #intFile, "PowerPoint: New instance was NOT Quit."
lstActions.AddItem "PowerPoint: New instance was NOT Quit."
End If
' Enable keyboard and mouse.
EnableWindow hWndDesktop, True
End With
Set appPPT = Nothing
Else
Print #intFile, "PowerPoint: Invalid application object, instance,
if valid, was not Quit."
lstActions.AddItem "PowerPoint: Invalid application object,
instance, if valid, was not Quit."
End If
End Sub
Private Sub btnClearList_Click()
lstActions.Clear
End Sub

Private Function DetectPPT() As Long
Const powerpnt As String = "POWERPNT.EXE"
Dim hProcessSnap As Long
Dim hProcess As Long
Dim pe32 As PROCESSENTRY32

' Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
If hProcessSnap = 0 Then
lstActions.AddItem "Error: CreateToolhelp32Snapshot"
DetectPPT = 0
Exit Function
End If

pe32.dwSize = Len(pe32)
If Process32First(hProcessSnap, pe32) = 0 Then
lstActions.AddItem "Error: Process32First"
DetectPPT = 0
Else
' Walk the snapshot of processes, and
' display information about each process
Do
If InStr(UCase$(pe32.szExeFile), powerpnt) <> 0 Then
hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0,
pe32.th32ProcessID)
If hProcess = 0 Then
lstActions.AddItem "Error: OpenProcess"
DetectPPT = 0
Else
DetectPPT = pe32.cntThreads
End If
Exit Do
End If
Loop While (Process32Next(hProcessSnap, pe32) <> 0)
End If
CloseHandle (hProcessSnap)
End Function
 
H

Howard Kaikow

Steve Rindsberg said:
With a little luck, I may get some time this evening to give it a go.
Thanks!

I just ran the code for the first time using PPT 97 and PPT 2000.
There would likely need to be some algorithm adjust with (lack of) respect
to the thread counts.

But, more importantly, the timing issues are even worse using PPT 2000.

First, for BOTH PPT 97 and PPT 2000, I was surprised to find that the thread
count went DOWN by 1 when the 2nd use of PPT started, so that would indeed
require version specific algorithm adjustment, if even possible.

Second, and worst, the timing issues with PPT 2000 are horrendous.
6 threads were created for the NEW PPT created by the code.
At first, I saw that the 2nd use of PPT, started via the desktop, did not
cause the number of threads to change, which I found surprising. So I stared
at the screen for (quite) a while, the darn screen just stared back, not
uttering a word. For some reason, I decided to check the thread count after
a few minutes. To my surprise, the count had dropped to 5 without me or the
code doing anything.

So, even if proper algorithms, based on thread count, could be found to
determine whether a NEW instance of PPT created by code can be Quit, it
seems the timing issues would not make such an approach practical.

If somebody could confirm the above, perhaps, I'll be allowed to leave the
rubber room from time to time.
 
H

Howard Kaikow

Unfortunately, the PPT process reference count is not changed, so one cannot
use the powerpnt.exe process reference count.

However, there are modules associated with the powerpnt.exe process that do
change reference counts, but I was ASSuME-ing that those counts also reflect
use by other apps, e.g., comctl32.dll is not used only by powerpnt.exe.

Of course, if those module reference counts are tied to the powerpnt.exe
process, then those counts could be used to solve the problem. I need to
construct a test to verify this.

Dag nab it!

I just did a preliminary test.
Further proof that one should not ASSuME things!

I started PowerPoint, saved the module usage counts.
Started Word.
Again saved the PowerPoint usage counts.
There were no changes!

Perhaps, the module usage counts are indeed tied to the process, if sdo, the
solution is easy!
I'll test this after lunch!!
 
S

Steve Rindsberg

I just ran the code for the first time using PPT 97 and PPT 2000.
There would likely need to be some algorithm adjust with (lack of) respect
to the thread counts.

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).
 
H

Howard Kaikow

I just posted the following elsewhere.

If the assertion is correct, then we have a solution.
--------------------------------------
When ennumerating the modules for a process, there are reference counts
provided for most, but not all modules.

It seems that those reference counts apply only to the process, i.e., they
might not appear to include ALL references to the particular DLL.

Any pointers to documentation for this assertion?

For example, if I start Excel, PowerPoint and Word and then look at the
reference count for certain DLLs used by all 3, I see, e.g., the following
(numbers are hexadecimal):

SHLWAPI.DLL: F,2,7 (Excel, PowerPoint,Word)
SHELL32.DLL: 8,2,7
COMCTL32.DLL: 10, 7, E
OLEAUT32.DLL: 10, C, 1E

Further testing would indicate whether shutting down one of the apps affects
the reference counts for the others, but that should be unnecessary as the
relationship between a module's reference count and a process must be
documented somewhere.
 

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