Visio automation: get process ID - vbscript

I'm running Visio using automation and I'm having trouble getting the process ID of the Visio process to check when its complete. Here's my VB script:
Set visio = CreateObject("Visio.InvisibleApp")
Wscript.Echo visio.ProcessID
Set document = visio.Documents.OpenEx("somefile.vsd", &H88)
document.ExportAsFixedFormat 1, "somefile.pdf", 1, 0
visio.Quit
and running it with cscript // nologo.
The problem is that visio.ProcessID returns a number that isn't the actual Windows process ID (e.g. 6613 when the actual process ID is 8146). The cscript host seems to finish before the Visio process exits causing problems cleaning up temp files.
Here is the Visio reference notes for:
ProcessID
Quit
So the question is: how can I get the Visio process ID or detect when it has properly exited?
Thanks!

Just noticed this in the help for Visio.Application.ProcessID:
"The value returned by ProcessID is not the same as the Windows Process ID of the current Visio instance."
So I guess these are just so you can distinguish between multiple instances of Visio.
There are also Visio.Application.WindowHandle32 and Visio.Application.WindowHandle, which might be helpful, although maybe not so much for an invisible app instance.

Can't see how this id would help you. I suspect you get an error in visio. Have you tried with
on error resume next
and after each line that could give an error
if err.number <> 0 then
wscript.echo err.description
err.clear
end if
There seems to be an issue if you omit parameters, so use them all.
See http://msdn.microsoft.com/en-us/library/office/ms409271(v=office.12).aspx for the values.
Before quiting use document.saved = true, you could check first if the result file exists.
EDIT: check if a process is running, could be you have to adapt the name of the service (check your téaskmanager)
set service = GetObject ("winmgmts:")
for each Process in Service.InstancesOf ("Win32_Process")
If lcase(Process.Name) = "visio.exe" then
wscript.echo "visio still running"
wscript.quit
End If
next
wscript.echo "visio no longer running"
EDIT2: to get the processid of the active visio app (If more than one Visio instance is running, GetObject returns the active instance. When a program is run as an add-on or by double-clicking a shape, the active instance is the one that the program was run from. Otherwise, it is the instance that was most recently run or brought to the front. If no Visio instance is running, GetObject causes an error)
cfr http://webmail.vh.com.tw/Microsoft/Developing%20Microsoft%20Visio%20Solutions/27.htm
set appObj = GetObject(, "visio.application")
if appObj Is Nothing Then
wscript.echo "There is no active Visio."
else
wscript.echo "ProcessID: " & appObj.ProcessID
end if

Related

Visual Basic 6 How to use Exceptions?

I have looked all over the Internet for an answer that addresses exception handling in VB6 and can't find anything useful. I want to show an error to the user and halt execution of the program where it encountered that error, I don't want execution to continue.
I've looked into using End but this isn't what I'm looking for. End closes all forms and exits. I've also used Stop but that temporary suspends execution.
I'm looking to handle logic exceptions if possible with this version of Visual Basic. If exceptions aren't available, is there a way to emulate them?
Here is a template I use in all my projects:
Private Sub Test()
On Error GoTo catch
'your code which may cause an error
'if you wanted to raise a user-defined error which start at 513
Err.Raise vbObjectError + 513, "MyModule", "MyMessage"
finally:
'any code that always has to happen
Exit Sub
catch:
'logging, message boxes, etc, however you want to handle the error
MsgBox Err.Number & ": " & Err.Description
Resume finally
End Sub

Is it possible to hide an error message generated by VBS script?

I have a customisation on an application that opens the comport to start reading data from the GPS. Now that I am using a Bluetooth GPS receiver the port doesn't always open the first time. If it fails it give the message "Error 121 when attempting to open communications port COM(comport number):"
Now when it does this I can check to see if the command worked, and if it didn't fire it again... (see code for the button below) However it displays an error.
This mimics what happens when you do it manually... sometimes it takes the Bluetooth stack multiple tries to connect to the GPS... It doesn't display an error though...
Anyway, it works, but it would be great to not show the error message on the times where the comport doesn't open...
Any and all suggestions are welcome, and appreciated.
Notes:
-The program is working without problems on everything but new units which have to use the Microsoft Bluetooth stack.
-While this error is annoying, the system is otherwise working, and when the Bluetooth connection is made after 1-3 attempts, all is well...
-Before I added "On Error Resume Next" the process would stop whenever the connection didn't get made.
Sub subGPSOnOffButton
'turns the GPS and tracklog on (or off) from a custom button...
'if the gps is on, make sure the user wants it off...
'if the gps is off, turn it on, and tell the user when done.
Dim iRes
If Application.GPS.IsOpen = True Then
iRes = msgbox("GPS is already on!" & vbnewline & vbnewline & "Turn it off?", vbYesNo, "GPS Active:")
If iRes = 6 then
Application.GPS.Close()
Else
Exit Sub
End if
Else
subGPSOn
msgbox "GPS is now on!", vbOKOnly, "GPS Active:"
End If
End Sub
Sub subGPSOn
On Error Resume Next
Do While Application.GPS.IsOpen = False
' Turn the GPS on
Application.GPS.Open()
Loop
' Turn the tracklog on
Application.ExecuteCommand("gpstracklog")
End Sub

Close Open Excel Instance

Could someone please let me know if the following simple VBScript is correct?
It is supposed to close Excel after other processes have run (and left Excel open), but it doesn't work.
Set MyApp = CreateObject("Excel.Application")
MyApp.Quit
CreateObject creates a new object. If I understand your question correctly you want to attach to already running (orphaned) Excel processes to terminate them. You can do that with GetObject:
On Error Resume Next
Do
Set xl = GetObject(, "Excel.Application")
status = Err.Number
If status = 0 Then
For Each wb in xl.Workbooks
wb.Close False 'discard changes in open workbooks
Next
xl.Quit
ElseIf status <> 429 Then
WScript.Echo Err.Number & ": " & Err.Description
WScript.Quit 1
End If
Until status = 429
On Error Goto 0
Note that this will try to close all running Excel instances, discarding all changes in open workbooks. If you want it to save changes in open workbooks change the argument of the Close method to True. If you have Excel instances you want to keep running, you need to add code to exclude them from being closed.
Note also, that this will not forcibly terminate unresponsive instances. You'd need to kill the process for that:
Set wmi = GetObject("winmgmts://root/cimv2")
For Each xl In wmi.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'excel.exe'")
xl.Terminate
Next
Try this please.
ThisWorkbook.Saved = True
Application.Quit
CreateObject creates a COM object, so your
Set MyApp = CreateObject("Excel.Application")
starts a new Excel process. Use GetObject to "retrieve an existing object with the specified ProgID". See this for theory and praxis.

VBScript alternative of PHP's debug_backtrace

I have an old VBScript project I need to debug and due to it's complexity I cannot see where are some functions being called from. Is there any VBScript alternative to PHP's debug_backtrace function or similar to see the stack trace?
So at this point, after a lot of searching, I am going to answer my own question as follows: VBScript is relatively old language and does not seem to have any built-in function/method or other means which would reliably return stack trace of function call (similar to PHP's debug_backtrace() function.
I managed to track the execution path with a simple, custom Function logBreakPointToFile() which I wrote and I placed throughout 100s of .asp files (everywhere where the method in question was referenced directly and indirectly - nested). This gave me a very messy idea what was actually happening. However is cost me a lot of prep. time and clean-up time which I was trying to avoid in the first place.
If anyone finds a better solution, please share.
Thx
VBScript does not throw any exception with stacktrace information in case of error.
#Runtime, in case of error, VBScript throws an Err object.
Ex: Below script will throw 'Division by zero' error & script will not get executed further.
Msgbox 5/0
Msgbox "Hello" 'This will not execute
On Error:
VBScript uses 'On Error Resume Next' statement which starts the error handler. It means, in case of error just skip current line and goto the next line. 'On Error GoTo 0' disables the current error handler. (like you are closing an 'if' block). Please check MSDN for more details.
Consider this example.
On Error Resume Next
Msgbox 5/0
Msgbox "Hello" 'This will say "Hello"
Msgbox Err.Number '11
Msgbox err.Description 'division by zero
On Error GoTo 0 'stops the error handler. any run time error after this will stop the program
Err.Number will be 0 in case no error is found.
So, You can have function like
Function Divide(ByVal A, ByVal B)
On Error Resume Next
Dim Result
Result = A/B
If Err.Number = 0 Then
Divide = Result
Else
Divide = "Infinite"
End If
On Error GoTo 0
End function
Remember this - as it simply goes to the next line in case of error,
On Error Resume Next
If 5/0 Then
Msgbox "???" 'This will get executed. It does not go to Else
Else
Msgbox "Hello" 'It will not show this
End If
On Error GoTo 0
You need to configure IIS to send errors to the browser. Its under Classic ASP settings on IIS 7 - 8. You have the option to send errors to browser on local requests, or all requests. For testing on a dev box usually its set for local. It will tell you what line number you are erroring out on.
As for getting more detail on what functions are slowing your application down. With classic asp you need to write your own timing code to measure such metrics.

In VS2010, is there a way to know which application pool a given w3wp.exe is serving, to then decide to attach the debugger to?

So I'm debugging some websites (one from trunk, one from branch) running locally, in separate apppools. I have trunk and branch solutions open in two VS instances. I'd like to debug trunk in one, and branch in the other. I'd like to know if there's a way to know which application pool each w3wp.exe is serving, to know which one is which when attaching the debugger.
Update: the point of this is to write a macro within VS to then let me have a button (per app-pool that is interesting) which will be clickable to attach the debugger to. So solutions should preferably not involve other programs.
Update2: this is on Windows 7 against IIS7.
Below is my core macro. Write a few one-line subs calling it, like AttachToW3wp("DefaultAppPool") naming each app pool you’re interested in, and make buttons and hotkeys for them.
Private Sub AttachToW3wp(ByVal appPoolName As String)
Const processName As String = "w3wp.exe"
Dim userName As String = String.Format("IIS APPPOOL\{0}", appPoolName)
Try
Dim debugger As EnvDTE90.Debugger3 = CType(DTE.Debugger, EnvDTE90.Debugger3)
'debugger.DetachAll()
Dim transport As EnvDTE80.Transport = debugger.Transports.Item("Default")
Dim qualifier As String = Environment.MachineName '= My.Computer.Name
Dim engines(3) As EnvDTE80.Engine
engines(0) = transport.Engines.Item("Managed")
engines(1) = transport.Engines.Item("Script")
engines(2) = transport.Engines.Item("T-SQL")
Dim successMessage As String = String.Empty
For Each process As EnvDTE80.Process2 In debugger.GetProcesses(transport, qualifier)
With process
Dim fi As New System.IO.FileInfo(.Name)
If fi.Name = processName AndAlso (String.Compare(.UserName, 0, userName, 0, Len(userName), True) = 0) Then
If .IsBeingDebugged Then Throw New Exception(String.Format("{0} {1} is already attached to a debugger.", processName, userName))
process.Attach2(engines)
successMessage = String.Format("Attached to {0} for {1} ({2})", processName, userName, .ProcessID)
Exit For
End If
End With
Next
If successMessage = String.Empty Then
Throw New Exception(String.Format("{0} {1} not found.", processName, userName))
Else
Trace.WriteLine(successMessage)
End If
Catch ex As System.Exception
MsgBox(ex.Message)
End Try
End Sub
Another option would be the WADA - W3WP Advanced Attacher available in the Visual Studio Gallery. I found it by searching in the Online Gallery of Extension Manager for "attach worker".
Look at the answers to this question. There are built in scripts you can run from a command window to do this.
If you can execute a request on each branch, you could use something like Process Explorer or Task Manager to see which ID is which possibly as one may be taking up CPU cycles that is currently processing a request assuming you can get such separation.
You can use task manager to view the user name under which the process is running (which in general is the same as the application pool name) and the process ID, but you have to turn on these columns in task manager, and also the process name has to be the same as the application pool (which is the default as far as I know).
Also note that all methods listed on this page might only display the processes that are currently running, which means that if your particular process has shut down due to idle time you have first to use the site in order to bring the process up in the list, and in your case it means you should first access all sites to make sure that the process associated with them is runing.

Resources