I am trying to run acad code from a WCF service in process. I get most everything work just fine but for some reason I get this cryptic error when I try to loop through a selection set of AcadEntity objects. The weird part is that if I place the same loop in a simple IExtensionApplication command it works fine.
Does anyone have any suggestions how to make this work? I assume something to do with wcf service behavior/acad threading model but not sure what.
Here is the full source code, note that I am not using Editor.WriteMessage because that's one of the things I cannot get working from WCF so I am relying on NLog to generate the output on the bottom of this message. If you have an idea how to make Editor.WriteMessage work from WCF I'd appreciate your help as well.
Thanks in advance,
Steve
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.Interop
Imports Autodesk.AutoCAD.Interop.Common
Imports Autodesk.AutoCAD.ApplicationServices
Imports MgdAcApplication = Autodesk.AutoCAD.ApplicationServices.Application
Imports NLog
#Region "WCF SERVICE"
<ServiceContract()>
Public Interface ISelectItems
<OperationContract()>
Sub SelectItems()
End Interface
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Single)>
Public Class wcfSelectItems
Implements ISelectItems
Private log As Logger = LogManager.GetCurrentClassLogger
Public Sub SelectItems() Implements ISelectItems.SelectItems
log.Info("This will crash")
DoSelection()
End Sub
Public activeDoc As Document = MgdAcApplication.DocumentManager.MdiActiveDocument
Public Sub DoSelection()
Try
Using acDocLck As DocumentLock = activeDoc.LockDocument()
Dim td As AcadDocument = DocumentExtension.GetAcadDocument(activeDoc)
Dim ssetObj As AcadSelectionSet = td.SelectionSets.Add("SSALL0") ' creates named selection set
ssetObj.Select(AcSelect.acSelectionSetAll)
log.Trace("We have a selection of size = {0}", ssetObj.Count)
Try
For Each ent As AcadEntity In ssetObj
log.Trace("{0}", ent.ObjectName)
Next
Catch
log.Error("This is the crash: '{0}'", Err.Description)
End Try
End Using
Catch
log.Error(Err.Description)
End Try
End Sub
End Class
#End Region
Public Class clsSelectedItems
Implements IExtensionApplication
Private log As Logger = LogManager.GetCurrentClassLogger
#Region "ACAD COMMAND"
<CommandMethod("DoSelection")> _
Public Sub DoSelection_Method()
log.Info("This is OK")
DoSelection()
End Sub
#End Region
Public m_DefaultService As String = "ISelectItems"
Public m_DefaultIP As String = "127.0.0.1"
Public m_DefaultPort As String = "7200"
Public m_DefaultProtocol As String = "http"
Private m_serviceHost As ServiceHost = Nothing
Public Sub Initialize() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Initialize
hostService(m_DefaultIP, m_DefaultPort)
End Sub
Public Sub Terminate() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Terminate
If Not m_serviceHost Is Nothing Then
m_serviceHost.Close()
End If
End Sub
Public activeDoc As Document = MgdAcApplication.DocumentManager.MdiActiveDocument
'Do selection routine, exact same as above. This one works:
Public Sub DoSelection()
Try
Using acDocLck As DocumentLock = activeDoc.LockDocument()
Dim td As AcadDocument = DocumentExtension.GetAcadDocument(activeDoc)
Dim ssetObj As AcadSelectionSet = td.SelectionSets.Add("SSALL1") ' creates named selection set
ssetObj.Select(AcSelect.acSelectionSetAll)
log.Trace("We have a selection of size = {0}", ssetObj.Count)
Try
For Each ent As AcadEntity In ssetObj
log.Trace("{0}", ent.ObjectName)
Next
Catch
log.Error("This is the crash: '{0}'", Err.Description)
End Try
End Using
Catch
log.Error(Err.Description)
End Try
End Sub
' Set up a WCF service endpoint and start service
Public Sub hostService(ByVal _hostIP As String, ByVal _port As String)
Dim wcfEndpoint As String = String.Format("{0}://{1}:{2}/{3}", m_DefaultProtocol, _hostIP, _port, m_DefaultService)
Try
Dim baseAddress As Uri = New Uri(wcfEndpoint)
m_serviceHost = New ServiceHost(GetType(wcfSelectItems), baseAddress)
Dim binding = CreateNewHttpBinding(GetType(wcfSelectItems).FullName)
m_serviceHost.AddServiceEndpoint(GetType(ISelectItems), binding, "IAcadInProc")
Dim smb As New ServiceMetadataBehavior()
smb.HttpGetEnabled = True
m_serviceHost.Description.Behaviors.Add(smb)
Catch ex As Exception
log.Error("EX 2. {0}", ex.ToString)
End Try
m_serviceHost.Open()
log.Info("Listening on {0}", wcfEndpoint)
End Sub
'We avoid using config files for now:
Private Shared Function CreateNewHttpBinding(ByVal name As String) As WSHttpBinding
Dim result As New WSHttpBinding
result.Name = name
result.OpenTimeout = New TimeSpan(0, 1, 0)
result.ReceiveTimeout = New TimeSpan(0, 10, 0)
result.SendTimeout = New TimeSpan(0, 1, 0)
result.BypassProxyOnLocal = False
result.TransactionFlow = False
result.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard
result.MaxBufferPoolSize = 2147483647
result.MaxReceivedMessageSize = 2147483647
result.MessageEncoding = WSMessageEncoding.Text
result.TextEncoding = System.Text.Encoding.UTF8
result.UseDefaultWebProxy = True
result.AllowCookies = False
result.ReaderQuotas.MaxStringContentLength = 2147483647
result.ReaderQuotas.MaxDepth = 12
result.ReaderQuotas.MaxArrayLength = 16384
result.ReaderQuotas.MaxBytesPerRead = 4096
result.ReaderQuotas.MaxNameTableCharCount = 16384
result.ReliableSession.Ordered = False
result.ReliableSession.InactivityTimeout = New TimeSpan(0, 10, 0)
result.ReliableSession.Enabled = False
result.Security.Mode = SecurityMode.None
result.Security.Transport.ClientCredentialType = HttpClientCredentialType.None
result.Security.Message.ClientCredentialType = MessageCredentialType.None
Return (result)
End Function
End Class
OUTPUT:
Info clsSelectedItems Listening on http://127.0.0.1:7200/ISelectItems
Solved! Go to Solution.
Solved by Alexander.Rivilis. Go to Solution.
Thanks a lot, I am getting the same result. It's probably something with my autocad version/installation. I also noticed that every now and then it works in particular when I step through the debugger so I tried adding sleep delays with no results so far. Again, thanks for your help.
Try this version
I got rid of the line that I added previously and change the hostService method to:
Public Sub hostService(ByVal _hostIP As String, ByVal _port As String) Dim wcfEndpoint As String = String.Format("{0}://{1}:{2}/{3}", m_DefaultProtocol, _hostIP, _port, m_DefaultService) Try Dim wcfSelectSingleton As New wcfSelectItems Dim baseAddress As Uri = New Uri(wcfEndpoint) m_serviceHost = New ServiceHost(wcfSelectSingleton, baseAddress)
This works for me. I am thinking that by creating new here and passing it into the ServiceHost that the property gets set properly. Try it and see if any different for you.
with your current attachment as is I am getting this error on the same line (activeDoc 😃
Unable to cast COM object of type 'System.__ComObject' to interface type 'Autodesk.AutoCAD.Interop.AcadDocument'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{0EB66A59-8FF4-410B-B697-8813116B2C5F}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
the only way I can get it to run is under the VS 2010 debugger. It runs as expected with or without breakpoints set. I think it must be something with AutoCAD 2013. I am running build G.114 SP 1.1 on Windows 7 but I even tried G.55 on Windows XP (no SP) with the same exact result.
Are you using the attached project as-is? That line should be commented out:
Public Class wcfSelectItems Implements ISelectItems Private log As Logger = LogManager.GetCurrentClassLogger Public Sub SelectItems() Implements ISelectItems.SelectItems log.Info("This will crash") DoSelection() End Sub Public activeDoc As Document = MgdAcApplication.DocumentManager.MdiActiveDocument Public Sub DoSelection() Try 'activeDoc = MgdAcApplication.DocumentManager.MdiActiveDocument Using acDocLck As DocumentLock = activeDoc.LockDocument() Dim td As AcadDocument = DocumentExtension.GetAcadDocument(activeDoc) Dim ssetObj As AcadSelectionSet = td.SelectionSets.Add("SSALL0") ' creates named selection set ssetObj.Select(AcSelect.acSelectionSetAll) log.Trace("We have a selection of size = {0}", ssetObj.Count) activeDoc.Editor.WriteMessage("We have a selection of size = {0}", ssetObj.Count) Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog("We have a selection of size = " & ssetObj.Count.ToString()) Try For Each ent As AcadEntity In ssetObj log.Trace("{0}", ent.ObjectName) Next Catch log.Error("This is the crash: '{0}'", Err.Description) End Try End Using Catch log.Error(Err.Description) End Try End Sub End Class #End Regio
If you take the DLL files from the debug folder from the last project I uploaded and try to netload that does it work?
Yeah I know it sounds weird but it doesn't. At least we know that the code itself is OK thanks to your help but there must be some weird issue with my version of autocad because no matter what build on what windows version I try it doesn't work.
Just a followup to this thread:
The 1st post still stands. As far as I know this does not work in ACAD 2013 it might work in electric.
The issue is that .NET is throwing an exception whenever the code reaches MoveNext in a loop through collection items such as this:
ssetObj.Select(AcSelect.acSelectionSetAll)
Try
For Each ent As AcadEntity In ssetObj
log.Trace("{0}", ent.ObjectName)
Next
Catch
log.Error("This is the crash: '{0}'", Err.GetException.ToString)
End Try
The detailed exception is as follows:
This is the crash: 'System.Runtime.InteropServices.COMException (0x8000FFFF): Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Runtime.InteropServices.CustomMarshalers.EnumeratorViewOfEnumVariant.MoveNext()
at ClassLibrary1.wcfSelectItems.DoSelection() in H:\_TEST\ClassLibrary2\Class1.vb:line 85'
Throughout this thread there was a lot of trial and error mainly involving the active document and how to initialize it in the context of new WCF requests. Since yesterday I learned a great deal about this topic thanks to tonofsteel and Paul Schelppy's excelent video (http://au.autodesk.com/?nd=event_class&jid=610987&session_id=7182) and I am ready to share but it's unrelated to this problem.
I also found that the code in post 1 runs perfectly well under the VS 2010 debugger. I still don't know what's that all about.
If anyone out there knows a solution please let me know.
I'm not sure that this code is thread-safe. The AutoCAD API's do not support multithreading. You should only call the API functions from the main thread:
Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"
Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Alexander.Rivilis, thanks for the link, I'll check that out, although I was under the impression that when I set ConcurrencyMode to Single in a WCF server that's what it does: the host and the Service execute in the same thread. I can verify if that's true fro sure by dumping the native thread ids from both, I'll post my findings.
To wrap this thread up, I was finally successful hosting and starting a functional WCF server inside the ACAD process and I can call functions that expose core acad functionality to any program running anywhere on the network. The solution came as a combination of Adam Nagy's example in the reply thanks to Alexander.Rivilis that solved the core multithreading problem and Paul Shleppy's excellent video from AU that taught me how to pass objects to a custom ServiceHost class.
Here is the full solution for anyone out there who's trying to accomplish something similar, please give everyone who contributed to it credit for it by response/kudos , thanks.
Imports System.Windows.Forms
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.Interop
Imports Autodesk.AutoCAD.Interop.Common
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports MgdAcApplication = Autodesk.AutoCAD.ApplicationServices.Application
Public Class AcServiceHost
Inherits ServiceHost
Private _ctrl As clsControl = Nothing
Public Sub New(ByVal serviceType As Type, ByVal endpoint As Uri, ByVal ctrl As clsControl)
MyBase.New(serviceType, endpoint)
_ctrl = ctrl
End Sub
Public ReadOnly Property SyncControl As clsControl
Get
Return _ctrl
End Get
End Property
End Class
<ServiceContract()>
Public Interface ISelectItems
<OperationContract()>
Sub SelectItems()
End Interface
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Single)>
Public Class wcfSelectItems
Implements ISelectItems
Private myControl As clsControl = Nothing
Public Sub SelectItems() Implements ISelectItems.SelectItems
Try
Dim context As OperationContext = OperationContext.Current
Dim acHost As AcServiceHost = TryCast(context.Host, AcServiceHost)
myControl = acHost.SyncControl
myControl.SelectItems()
Catch
End Try
End Sub
End Class
Public Class clsControl
Inherits Control
Implements IDisposable
Private Delegate Sub DoSynchronizedSelection()
Public activeDoc As Document = MgdAcApplication.DocumentManager.MdiActiveDocument
Public ed As Editor = activeDoc.Editor
Public Sub SelectItems()
If InvokeRequired Then
Invoke(New DoSynchronizedSelection(AddressOf DoSelection))
Else
DoSelection()
End If
End Sub
Public Sub logMessage(ByVal format As String, ByVal ParamArray args As Object())
Dim msg As String = format
Try
msg = String.Format(format, args)
Catch
End Try
ed.WriteMessage(vbLf + msg)
End Sub
'Do selection routine, exact same as above. This one works:
Public Sub DoSelection()
Try
Using acDocLck As DocumentLock = activeDoc.LockDocument()
Dim td As AcadDocument = DocumentExtension.GetAcadDocument(activeDoc)
Dim ssetObj As AcadSelectionSet = td.SelectionSets.Add("SSALL" + DateTime.Now.ToString) ' creates named selection set
ssetObj.Select(AcSelect.acSelectionSetAll)
logMessage("We have a selection of size = {0}", ssetObj.Count)
Try
For Each ent As AcadEntity In ssetObj
logMessage("{0}", ent.ObjectName)
Next
Catch
logMessage("This is the crash: '{0}'", Err.GetException.ToString)
End Try
End Using
Catch
logMessage("EX 1. {0}", Err.GetException.ToString)
End Try
End Sub
End Class
Public Class clsSelectedItems
Implements IExtensionApplication
Public m_DefaultService As String = "ISelectItems"
Public m_DefaultIP As String = "127.0.31.173"
Public m_DefaultPort As String = "7101"
Public m_DefaultProtocol As String = "http"
Private m_serviceHost As ServiceHost = Nothing
Shared syncCtrl As clsControl = Nothing
Public Sub Initialize() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Initialize
syncCtrl = New clsControl()
syncCtrl.CreateControl()
hostService(m_DefaultIP, m_DefaultPort)
End Sub
Public Sub Terminate() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Terminate
If Not m_serviceHost Is Nothing Then
m_serviceHost.Close()
End If
End Sub
' Set up a WCF service endpoint and start service
Public Sub hostService(ByVal _hostIP As String, ByVal _port As String)
Dim wcfEndpoint As String = String.Format("{0}://{1}:{2}/{3}",
m_DefaultProtocol, _hostIP, _port, m_DefaultService)
Try
Dim baseAddress As Uri = New Uri(wcfEndpoint)
m_serviceHost = New AcServiceHost(GetType(wcfSelectItems), baseAddress, syncCtrl)
Dim binding = CreateNewHttpBinding(GetType(wcfSelectItems).FullName)
m_serviceHost.AddServiceEndpoint(GetType(ISelectItems), binding, m_DefaultService)
Dim smb As New ServiceMetadataBehavior()
smb.HttpGetEnabled = True
m_serviceHost.Description.Behaviors.Add(smb)
Catch
syncCtrl.logMessage("EX 2. {0}", Err.GetException.ToString)
End Try
m_serviceHost.Open()
syncCtrl.logMessage("Listening on {0}", wcfEndpoint)
End Sub
'We avoid using config files for now:
Private Shared Function CreateNewHttpBinding(ByVal name As String) As WSHttpBinding
Dim result As New WSHttpBinding
result.Name = name
result.OpenTimeout = New TimeSpan(0, 1, 0)
result.ReceiveTimeout = New TimeSpan(0, 10, 0)
result.SendTimeout = New TimeSpan(0, 1, 0)
result.BypassProxyOnLocal = False
result.TransactionFlow = False
result.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard
result.MaxBufferPoolSize = 2147483647
result.MaxReceivedMessageSize = 2147483647
result.MessageEncoding = WSMessageEncoding.Text
result.TextEncoding = System.Text.Encoding.UTF8
result.UseDefaultWebProxy = True
result.AllowCookies = False
result.ReaderQuotas.MaxStringContentLength = 2147483647
result.ReaderQuotas.MaxDepth = 12
result.ReaderQuotas.MaxArrayLength = 16384
result.ReaderQuotas.MaxBytesPerRead = 4096
result.ReaderQuotas.MaxNameTableCharCount = 16384
result.ReliableSession.Ordered = False
result.ReliableSession.InactivityTimeout = New TimeSpan(0, 10, 0)
result.ReliableSession.Enabled = False
result.Security.Mode = SecurityMode.None
result.Security.Transport.ClientCredentialType = HttpClientCredentialType.None
result.Security.Message.ClientCredentialType = MessageCredentialType.None
Return (result)
End Function
End Class
I got this error 0x8000ffff windows 10 store error Fixed. I am referring this tutorial 0x8000ffff windows 10 store error Fixed . This tutorial Is right for me? please suggest some tips.
Thanks
Can't find what you're looking for? Ask the community or share your knowledge.