TheSwamp
Code Red => .NET => Topic started by: jparts on December 27, 2012, 07:16:26 AM
-
Hi All,
I'm trying to write a stand alone application that monitors the state of autocad. We are running autocad on a server to generate pdf/dxf/dwg files fully automatically. The jobs for autocad on this batchserver are generated automatically from our PLM system and picked up automatically by an addin in autocad. The problem however is keeping autocad alive without user interference. The biggest problem I'm facing is to kill AutoCAD when it runs into a Fatal Error :evil: . If autocad now runs into a fatal error the whole process of creating pdf/dxf or dwg files is now stopped until someone manually closes autocad and restart it.
So I hope someone here can help me how to "catch" a fatal error in autocad.
Thanks alot!
Joris
This is what I've got so far...:
Imports System
Imports System.Runtime.InteropServices
Imports Autodesk.AutoCAD.Interop
Public Class Form1
Private Property acApp As AcadApplication = Nothing
Private Property acDoc As AcadDocument = Nothing
Private Const progID As String = "AutoCAD.Application.18"
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Me.lblText1.Text = "Checking for AutoCAD Dialogs"
Me.btnStart.Text = "Start"
Me.btnStart.Enabled = True
Me.Update()
End Sub
Private Sub btnStart_Click(sender As System.Object, e As System.EventArgs) Handles btnStart.Click
Select Case Me.btnStart.Text
Case "Start"
Me.btnStart.Text = "Stop"
acApp = Nothing
acDoc = Nothing
bgw.RunWorkerAsync()
Case "Stop"
Me.btnStart.Text = "Start"
bgw.CancelAsync()
End Select
End Sub
Private Sub bgw_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw.DoWork
Do
If (bgw.CancellationPending) Then
e.Cancel = True
Exit Do
End If
Try
acApp = CType(Marshal.GetActiveObject(progID), AcadApplication)
acDoc = acApp.ActiveDocument
'catch dialogs that require user input and close autocad
AddHandler acApp.BeginModal, AddressOf CloseAutoCAD
'catch a fatal error and close autocad
'...?
'...?
'...?
Catch ex As System.Exception
acApp = Nothing
acDoc = Nothing
End Try
Do While acDoc IsNot Nothing
If (bgw.CancellationPending) Then
e.Cancel = True
RemoveHandler acApp.BeginModal, AddressOf CloseAutoCAD
acApp = Nothing
acDoc = Nothing
Exit Do
End If
If Process.GetProcessesByName("acad").Count = 0 Then
Exit Do
End If
Threading.Thread.Sleep(500)
Loop
Threading.Thread.Sleep(500)
Loop
End Sub
Public Sub CloseAutoCAD()
Dim prs = Process.GetProcessesByName("acad")
For Each pr As Process In prs
pr.Kill()
Next
Do
Loop Until Process.GetProcessesByName("acad").Count = 0
End Sub
End Class
-
I found this on msdn site that might help you.
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.responding.aspx (http://msdn.microsoft.com/en-us/library/system.diagnostics.process.responding.aspx)
-
Hi Keith,
Thanks for the tip!
However this method doesn't seem to work, because when a fatal error occurs the process still returns that the program is running i.s.o. not responding.
I found a solution anyway (thanks to http://cjwdev.wordpress.com/2010/06/03/get-visible-windows/ (http://cjwdev.wordpress.com/2010/06/03/get-visible-windows/) ). With this method I can catch the fatal error by the title of the window that appears ("AutoCAD Error Aborting").
If anybody is interested here is my working code:
Imports System
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Autodesk.AutoCAD.Interop
Public Class Form1
Private Property acApp As AcadApplication = Nothing
Private Property acDoc As AcadDocument = Nothing
Private Const progID As String = "AutoCAD.Application.18"
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Me.lblText1.Text = "Checking for AutoCAD Dialogs"
Me.btnStart.Text = "Start"
Me.btnStart.Enabled = True
Me.Update()
End Sub
Private Sub btnStart_Click(sender As System.Object, e As System.EventArgs) Handles btnStart.Click
Select Case Me.btnStart.Text
Case "Start"
Me.btnStart.Text = "Stop"
acApp = Nothing
acDoc = Nothing
bgw.RunWorkerAsync()
Case "Stop"
Me.btnStart.Text = "Start"
bgw.CancelAsync()
End Select
End Sub
Private Sub bgw_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw.DoWork
Do
If (bgw.CancellationPending) Then
e.Cancel = True
Exit Do
End If
Try
acApp = CType(Marshal.GetActiveObject(progID), AcadApplication)
acDoc = acApp.ActiveDocument
'catch dialogs that require user input and close autocad
AddHandler acApp.BeginModal, AddressOf CloseAutoCAD
Catch ex As System.Exception
acApp = Nothing
acDoc = Nothing
End Try
Do While acDoc IsNot Nothing
If (bgw.CancellationPending) Then
e.Cancel = True
RemoveHandler acApp.BeginModal, AddressOf CloseAutoCAD
acApp = Nothing
acDoc = Nothing
Exit Do
End If
'catch a fatal error and close autocad
Dim Windows = GetWindows()
If Windows.Count <> 0 Then
Dim ErrorFound As Boolean = False
For Each Window As ManagedWindow In Windows
Select Case Window.Title.ToUpper
Case "AUTOCAD ERROR ABORTING"
ErrorFound = True
Case "FATAL ERROR"
ErrorFound = True
Case "AUTOCAD"
ErrorFound = True
Case "NETWORK LICENSE ERROR"
ErrorFound = True
End Select
Next
If ErrorFound = True Then
CloseAutoCAD()
End If
End If
If Process.GetProcessesByName("acad").Count = 0 Then
Exit Do
End If
Threading.Thread.Sleep(500)
Loop
Threading.Thread.Sleep(500)
Loop
End Sub
Public Sub CloseAutoCAD()
Dim prs = Process.GetProcessesByName("acad")
For Each pr As Process In prs
pr.Kill()
Next
Do
Loop Until Process.GetProcessesByName("acad").Count = 0
End Sub
Public Shared Function GetWindows() As List(Of ManagedWindow)
Dim WindowList As New List(Of ManagedWindow)
Dim ListHandle As GCHandle = GCHandle.Alloc(WindowList)
Try
NativeAPI.EnumWindows(New NativeAPI.EnumWindowsProc(AddressOf EnumWindowsCallBack), GCHandle.ToIntPtr(ListHandle))
Finally
ListHandle.Free()
End Try
Return WindowList
End Function
Private Shared Function EnumWindowsCallBack(ByVal handle As IntPtr, ByVal lParam As IntPtr) As Boolean
If NativeAPI.IsWindowVisible(handle) Then
Dim TitleBuilder As New System.Text.StringBuilder(NativeAPI.GetWindowTextLength(handle) + 1)
Dim WndList As List(Of ManagedWindow) = DirectCast(GCHandle.FromIntPtr(lParam).Target, List(Of ManagedWindow))
NativeAPI.GetWindowText(handle, TitleBuilder, TitleBuilder.Capacity)
Dim ProcessID As Integer = -1
NativeAPI.GetWindowThreadProcessId(handle, ProcessID)
Dim ClassNameBuilder As New System.Text.StringBuilder(255)
NativeAPI.GetClassName(handle, ClassNameBuilder, ClassNameBuilder.Capacity)
WndList.Add(New ManagedWindow(handle, TitleBuilder.ToString, Process.GetProcessById(ProcessID), ClassNameBuilder.ToString))
End If
Return True
End Function
End Class
Public Class ManagedWindow
Private _Title As String = String.Empty
Public Property Title() As String
Get
Return _Title
End Get
Set(ByVal value As String)
_Title = value
End Set
End Property
Private _Handle As IntPtr
Public Property Handle() As IntPtr
Get
Return _Handle
End Get
Set(ByVal value As IntPtr)
_Handle = value
End Set
End Property
Private _OwningProcess As Process
Public Property OwningProcess() As Process
Get
Return _OwningProcess
End Get
Set(ByVal value As Process)
_OwningProcess = value
End Set
End Property
Private _ClassName As String = String.Empty
Public Property ClassName() As String
Get
Return _ClassName
End Get
Set(ByVal value As String)
_ClassName = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal Hwnd As IntPtr, ByVal TitleText As String, ByVal Owner As Process, ByVal NativeClassName As String)
_Handle = Hwnd
_Title = TitleText
_OwningProcess = Owner
_ClassName = NativeClassName
End Sub
Public Overrides Function ToString() As String
If OwningProcess Is Nothing Then
Return Handle.ToString & " —- " & ClassName & " —- " & Title
Else
Return Handle.ToString & " —- " & ClassName & " —- " & Title & " —- " & OwningProcess.ProcessName
End If
End Function
End Class
Public Class NativeAPI
<UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)> _
Public Delegate Function EnumWindowsProc(ByVal hwnd As System.IntPtr, ByVal lparam As System.IntPtr) As Boolean
<DllImportAttribute("user32.dll", EntryPoint:="EnumWindows")> _
Public Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsProc, <MarshalAsAttribute(UnmanagedType.SysInt)> ByVal lParam As IntPtr) As <MarshalAsAttribute(UnmanagedType.Bool)> Boolean
End Function
<DllImportAttribute("user32.dll", EntryPoint:="IsWindowVisible")> _
Public Shared Function IsWindowVisible(<InAttribute()> ByVal hWnd As System.IntPtr) As <MarshalAsAttribute(UnmanagedType.Bool)> Boolean
End Function
<DllImportAttribute("user32.dll", EntryPoint:="GetWindowTextW")> _
Public Shared Function GetWindowText(<InAttribute()> ByVal hWnd As System.IntPtr, <OutAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpString As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function
<DllImportAttribute("user32.dll", EntryPoint:="GetWindowTextLengthW")> _
Public Shared Function GetWindowTextLength(<InAttribute()> ByVal hWnd As System.IntPtr) As Integer
End Function
<DllImportAttribute("user32.dll", EntryPoint:="GetWindowThreadProcessId")> _
Public Shared Function GetWindowThreadProcessId(<InAttribute()> ByVal hWnd As System.IntPtr, <OutAttribute()> ByRef lpdwProcessId As Integer) As UInteger
End Function
<DllImportAttribute("user32.dll", EntryPoint:="GetClassNameW")> _
Public Shared Function GetClassName(<InAttribute()> ByVal hWnd As System.IntPtr, <OutAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function
End Class