Как известно, WCF-сервисы могут в качестве хостинга использовать не только IIS и WAS, но так же и произвольные приложения (консольные или GUI). Как показывает практика, в качестве хоста можно использовать
acad.exe. В идеале хотелось бы иметь возможность хостить службы в
accoreconsole.exe, но не забываем, что это
Autodesk, а это означает, что скучать не придётся...
Когда это может оказаться интересным?Как известно, в параметрах запуска
accoreconsole.exe можно указывать набор ключей, в т.ч. и ключ
/s, при помощи которого разрешено передавать имя файла скрипта (
SCR-файла). По завершению работы скрипта приложение так же автоматически завершит свою работу. Однако порой может возникнуть потребность
интерактивного использования
функционала, предоставляемого
accoreconsole.exe (
т.е. само по себе консольное окно при этом не требуется) не закрывая приложение столько времени, сколько потребуется (можно просто скрыть консольное окошко).
Хостинг службы в AutoCAD позволяет другим приложениям (т.н.
клиентам) взаимодействовать с ним не прибегая к использованию AutoCAD COM API и при этом получая возможность задействовать возможности AutoCAD .NET API. Кроме того, в любой момент служба может быть перемещена на любой др. компьютер абсолютно прозрачно для клиентов.
В случае необходимости, клиент так же сможет отправлять службе произвольный набор команд, которые будут выполняться в AutoCAD на локальной или удалённо расположенной машине. Например с планшета, работающего под управлением
OS Android пользователь сможет отправлять команды пакетной обработки чертежей (очистка, аудит, пересохранение и т.п.).
Это не означает, что на удалённой или локальной машинке обязательно должен быть постоянно запущен AutoCAD. Нет. Клиент может обратиться к службе, которая хостится в IIS или WAS с требованием что-то сделать в AutoCAD. Эта служба запускает AutoCAD (устанавливая видимость его окна в False) и в свою очередь является клиентом для другой службы, хостящейся в AutoCAD, передавая ей ваши запросы, а вам - её ответы. После того, как необходимый вам набор операций в AutoCAD будет выполнен служба, хостящаяся в IIS или WAS завершает работу AutoCAD и ждёт следующих обращений. В случае необходимости, параллельно может быть запущено несколько экземпляров AutoCAD, выполняющих каждый свою задачу, полученную от клиента. Для упрощения в данной теме я не использую промежуточную службу.
Сервис может, к примеру, проверять наличие обновлений (для AutoCAD и его расширений) на сервере компании и в случае их обнаружения сообщать об этом пользователю (или выполнять обновление - это на откуп администраторов CAD).
В идеале, конечно же, лучше всего на роль хоста службы подошёл бы
accoreconsole.exe...
СлужбаПредположим, что служба, предназначенная для хостинга в AutoCAD, реализует такой интерфейс:
namespace Bushman.CAD.Services {
[ServiceContract(Namespace = "www.gpsm.ru")]
interface IMyContract {
[OperationContract]
void Write(string msg); // Write message into AutoCAD command console.
[OperationContract]
string GetVersion(); // Get AutoCAD version
}
}
Реализуем обозначенный интерфейс как-то так:
using cad = Autodesk.AutoCAD.ApplicationServices.Core.Application;
using Autodesk.AutoCAD.ApplicationServices;
namespace Bushman.CAD.Services {
class MyService : IMyContract {
public string GetVersion() {
return cad.Version.ToString();
}
public void Write(string msg) {
Document doc = cad.DocumentManager.MdiActiveDocument;
if (null != doc) {
doc.Editor.WriteMessage(msg);
}
}
}
Никаких команд определять не будем, а инициализацию расширения выполним следующим образом:
using System; using System.ServiceModel;
using cad = Autodesk.AutoCAD.ApplicationServices.Core.Application;
usingAutodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
[assembly: ExtensionApplication(typeof(Bushman.CAD.Services.ExtensionApplication))]
namespace Bushman.CAD.Services {
public class ExtensionApplication : IExtensionApplication {
static ServiceHost host = null;
static ExtensionApplication() {
try{
host = new ServiceHost(typeof(MyService));
host.Open();
host.UnknownMessageReceived += Host_UnknownMessageReceived;
AppDomain.CurrentDomain.ProcessExit += ProcessExit;
}
catch(System.Exception ex) {
Document doc = cad.DocumentManager.MdiActiveDocument;
if (null != doc) doc.Editor.WriteMessage(ex.Message);
}
}
private static void Host_UnknownMessageReceived(object sender,
UnknownMessageReceivedEventArgs e) {
Document doc = cad.DocumentManager.MdiActiveDocument;
if(null != doc) doc.Editor.WriteMessage(e.Message.ToString());
}
private static void ProcessExit(object sender, EventArgs e) {
if(null != host) host.Close();
}
public void Initialize() {
string status = null == host ? "null" : host.State.ToString();
Document doc = cad.DocumentManager.MdiActiveDocument;
if(null != doc) doc.Editor.WriteMessage("\nHost status: {0}.\n", status);
}
public void Terminate() {
// Nothing is here.
}
}
}
КлиентКлиента реализуем следующим образом:
using System; using System.ServiceModel;
usingBushman.MyClient.ServiceReference1;
namespace Bushman.MyClient {
classProgram {
static void Main(string[] args) {
Console.Title = "CAD client";
try{
using (MyContractClient client = new MyContractClient("http")) {
if (client.InnerChannel.State != CommunicationState.Faulted) {
client.Open();
string version = client.GetVersion();
Console.WriteLine("CAD version: {0}", version);
client.Write("Client said: Hello, AutoCAD.\n");
}
}
}
catch(Exception ex) {
Console.WriteLine(ex.Message);
}
Console.WriteLine("Press any key for exit...");
Console.ReadKey();
}
}
}
Конфигурационные файлы
acad.exe.config и
accoreconsole.exe.config настраиваю на работу с обозначенной выше службой:
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
<!--All assemblies in AutoCAD are fully trusted so there's no point generating publisher evidence-->
<runtime>
<generatePublisherEvidence enabled="false"/>
</runtime>
<system.serviceModel>
<services>
<service name="Bushman.CAD.Services.MyService" behaviorConfiguration="MEXGET">
<host>
<baseAddresses>
<add baseAddress="http://win7x64ac2:8001"/>
</baseAddresses>
</host>
<endpoint name="http"
binding="wsHttpBinding"
address="MyService"
bindingConfiguration="MyContract"
contract ="Bushman.CAD.Services.IMyContract">
</endpoint>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="MyContract"/>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MEXGET">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<diagnostics wmiProviderEnabled="true">
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
/>
</diagnostics>
</system.serviceModel>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="myUserTraceSource"
switchValue="Information, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="\\hyprostroy\dfs\Обмен\Бушман\logs\Service-Traces.svclog" />
</sharedListeners>
</system.diagnostics>
</configuration>
Конфигурационный файл клиента выглядит так:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="http" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://win7x64ac2:8001/MyService" binding="wsHttpBinding"
bindingConfiguration="http" contract="ServiceReference1.IMyContract"
name="http">
<identity>
<!-- WARNING: change the value according your user principal name. -->
<userPrincipalName value="admin@hyprostr" />
</identity>
</endpoint>
</client>
<diagnostics wmiProviderEnabled="true">
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
/>
</diagnostics>
</system.serviceModel>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="myUserTraceSource"
switchValue="Information, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="\\hyprostroy\dfs\Обмен\Бушман\logs\Client-Traces.svclog" />
</sharedListeners>
</system.diagnostics>
</configuration>
Запускаем службу и клиентаСлужба и клиент могут находится как на одном компьютере, так и на разных компьютерах в сети (или в Интернет). Если в качестве хоста использовать
acad.exe, то всё нормально работает:
Но вот если в качестве хоста использовать
accoreconsole.exe, то достучаться до сервиса не удастся даже с локального компьютера, на котором запущен хост сервиса. При этом сервис успешно запускается, но не доступен:
В логах клиента можно посмотреть описание проблемы:
По обозначенной теме мне ответил Августо Гонсалес:
Augusto Goncalves (API Evangelist at Autodesk):
As far as I remember trying, accoreconsole.exe doesn't accept new calls (from automation) after is open (but I haven't tried with WCF). Acad.exe is a little different... I don't believe accoreconsole will remain receiving calls after it was launched, it was designed to launch with a list of commands on the .scr file.
Если Августо прав, то это будет очередной, весьма досадный недостаток
accoreconsole.exe...
Продолжение темы -
здесь...