Page 1 of 1

Плагины на .NET языках

Posted: 07 Dec 2008, 0:34 Sun
by bopoh
Кто-нибудь задумывался как это можно реализовать. Или может кто то уже продвинулся в этом направлении?
Я как раз сделал первые шаги.

Posted: 07 Dec 2008, 14:09 Sun
by bopoh
Если никому не будет интересно, то плагин для поддержки плагинов на NET делать не имеет смысла.

Posted: 09 Dec 2008, 14:03 Tue
by bopoh
При разработке столкнулся с проблемой которая не дает мне воспользоваться xpath выражениями для парсинга xml.

Posted: 12 Dec 2008, 22:37 Fri
by flatline
bopoh wrote:Кто-нибудь задумывался как это можно реализовать. Или может кто то уже продвинулся в этом направлении?
Я как раз сделал первые шаги.
Задумывался. Много раз - с тех пор как начал активно качать все и отовсюду :shock: Первые шаги сделал пару месяцев назад - в основном реализовал, что хотел.
bopoh wrote:Если никому не будет интересно, то плагин для поддержки плагинов на NET делать не имеет смысла.


Ну почему же... Для саморазвития - очень даже интересно. Основной проблемой для меня было отсутствие в C# нормальных средств для экспорта функций (чуваки из Мирового Зла оставили для шарпа лазейки в неуправляемый код только через CCW и прочий Interop/PInvoke, (в интернетах есть зверские люди, которые обошли проблему экспорта, вручную правя IL код... И даже мастер для Delphi .Net замутили, который делает всю чорную магию за них), а по человечески экспорт функций в VS2008 делается только в "управляемых плюсах"... Со всеми шаманскими плясками вокруг GCHandle. И да, "искажение имен" - бяка та еще). Пришлось поизвращаться с оберткой и трансляцией вызовов C++ в Delphi. (Но теперь dmaster умеет поднимать на себе WCF-сервис, через который им можно "немножко рулить" по сети :twisted:
bopoh wrote:При разработке столкнулся с проблемой которая не дает мне воспользоваться xpath выражениями для парсинга xml.


Не сталкивался... Хотя возвращаемый по некоторым вызовам (GetDownloadInfoByID, что ли) текст, обрамленный тегами, который в readme назывался "форматом XML", действительно непохож на well formed (не говоря уже о valid) XML (рута(!) нет, инфы о кодировке нет... Может, у меня версия DM устарела, конечно, и это уже исправили.)

Posted: 12 Dec 2008, 22:58 Fri
by flatline
bopoh wrote:При разработке столкнулся с проблемой которая не дает мне воспользоваться xpath выражениями для парсинга xml.
А в чем конкретно проблема?

Posted: 12 Dec 2008, 23:49 Fri
by bopoh
flatline wrote: Основной проблемой для меня было отсутствие в C# нормальных средств для экспорта функций (чуваки из Мирового Зла оставили для шарпа лазейки в неуправляемый код только через CCW и прочий Interop/PInvoke. Пришлось поизвращаться с оберткой и трансляцией вызовов C++ в Delphi. (Но теперь dmaster умеет поднимать на себе WCF-сервис, через который им можно "немножко рулить" по сети :twisted:
Эту задачу я решил через использование Interop и C++ обертки, которую тут кто-то выкладывал. Если коротко то плагин написан на C++, который создает упровляемую среду, домен в ней, грузит туда управляемую библиотеку, создает управляемый объект и ретранслирует все вызовы DM в управляемый код через интерфейс

Posted: 12 Dec 2008, 23:53 Fri
by bopoh
flatline wrote:
bopoh wrote:При разработке столкнулся с проблемой которая не дает мне воспользоваться xpath выражениями для парсинга xml.
А в чем конкретно проблема?
Все зависает напрочь если я пытаюсь выполнить любое действие с использованием XPath выражения, т.е. выбрать ноды в XML используя стандартные решения .NET
Дебаг фреймворка показал что все виснет при компиляции XPath выражения (глубже дебагер не заходит). Неприятная вещь, но я ее обошел отказом от использования стандартных классов, в место них использую MSXML COM объект :)

Posted: 13 Dec 2008, 8:52 Sat
by flatline
Эту задачу я решил через использование Interop и C++ обертки, которую тут кто-то выкладывал. Если коротко то плагин написан на C++, который создает упровляемую среду, домен в ней, грузит туда управляемую библиотеку, создает управляемый объект и ретранслирует все вызовы DM в управляемый код через интерфейс
Ну... я походу обошелся без явной загрузки сборок (С++ используется только для оборачивания хоста и экспорта функций поднятия сервиса. За основу все же взят "нативный плагин" на Delphi (он чуть-чуть дописан, для обработки вызовов сервиса. Основная идея - дальнейшее использование инфраструктуры WCF для работы через сеть (из прочих Net примочек окно настроек - WinForms :D). Вся основная разработка - C#. В локалке руление через браузер уже доступно методом оборачивания вызовов WCF в REST (правда, запросы пока приходится писать руками в строке адреса :lol:) [/quote]

Posted: 13 Dec 2008, 10:56 Sat
by bopoh
flatline wrote: Ну... я походу обошелся без явной загрузки сборок (С++ используется только для оборачивания хоста и экспорта функций поднятия сервиса. За основу все же взят "нативный плагин" на Delphi (он чуть-чуть дописан, для обработки вызовов сервиса. Основная идея - дальнейшее использование инфраструктуры WCF для работы через сеть (из прочих Net примочек окно настроек - WinForms :D). Вся основная разработка - C#. В локалке руление через браузер уже доступно методом оборачивания вызовов WCF в REST (правда, запросы пока приходится писать руками в строке адреса :lol:)
У тебя NET "плагин" выполняется в отдельном процессе
Я правильно тебя понял?
У меня плагин на C++ является хостом NET плагина. Я обошелся без Delphi потому как мало разбираюсь в этом языке, поэтому взял за основу плагин на C++ который уже внутрь себя грузит NET плагин, т.е. все NET плагины выполняются в том же процессе что и DM

Posted: 13 Dec 2008, 11:59 Sat
by flatline
bopoh wrote: У тебя NET "плагин" выполняется в отдельном процессе
Я правильно тебя понял?
У меня плагин на C++ является хостом NET плагина. Я обошелся без Delphi потому как мало разбираюсь в этом языке, поэтому взял за основу плагин на C++ который уже внутрь себя грузит NET плагин, т.е. все NET плагины выполняются в том же процессе что и DM
Неа. Никаких отдельных процессов (по крайней мере видимых в TaskManager) нет: сервис WCF (а не NT :wink:) поднимается плагином через оболочку C++ именно на том процессе, к которому пристегнут плагин (как известно, WCF позволяет хостить сервисы на обыкновенных приложениях виндоус, в виде библиотек, внешне ничем не отличимых от DLL). Внутри С++ оболочки создается управляемый объект хоста для сервиса (вложений вызовов чуть больше, но фактически команду на старт хоста отдает дельфовский плагин при иницилизации (соответствующие вызовы выставлены наружу в виде экспортов функций). Остальное делает runtime WCF внутри опять таки процесса DM (библиотека сервиса хостится на нем (через C++ оболочку :)) - конфиг берется из домашнего каталога DM (собственно "приложением" или "доменом" Net-овский конфигуратор считает dmaster.exe))). Другое дело, что общаться с сервисом напрямую нельзя (SOA, лежащая в основе WCF, вообще ориентирована на слабосвязанных агентов) - так что функционал управления DMом работает через TCP транспорт). Однако настройку (вызов WinForms окна) можно делать через вызовы функций из библиотеки хоста (они прозрачно конвертятся в вызовы Delphi-оболочки плагина: при "Ok" сервис перезагружается, TCP привязка рестартует - и все снова работает без перезагрузки DM). Задача была, повторюсь, заюзать для управления DMом средства предоставляемые WCF (накладные расходы сравнительно небольшие - все равно вся инфраструктура находится в рантайме Net framework и мои DLL-ки получаются маленькие (в них - только описание сервиса и средств связи с Delphi-стороной). Что до процессов - если я убью в списке задач dmaster.exe - сервис немедленно перестанет быть доступен, так как хост убьется вместе с доменом. Какой же он "отдельный"?)

Posted: 13 Dec 2008, 12:41 Sat
by flatline
flatline wrote: Внутри С++ оболочки создается управляемый объект хоста для сервиса (вложений вызовов чуть больше, но фактически команду на старт хоста отдает дельфовский плагин при иницилизации (соответствующие вызовы выставлены наружу в виде экспортов функций).
Проще говоря, C++ у меня используется вынужденно - только потому, что С# вообще не умеет экспортировать функции. (С++-ная оболочка - это именно оболочка для хоста. Не больше. Сам хост целиком написан на C# (а Net'у оно побарабану - он понимает "обоих языкоф"). Delphi плагин, выставляя наружу привычный DMу "объект", скрывает от DM подробности реализации "сплюснутой" библиотеки, которая, в свою очередь, скрывает от Delphi плагина подробности реализации всего остального. Все вместе для dmastera выглядит как плагин - без всяких кавычек. :)

Posted: 13 Dec 2008, 13:00 Sat
by bopoh
Интересная реализация. Хотелось бы посмотреть на C++ хост для WCF

Posted: 13 Dec 2008, 18:41 Sat
by flatline
Как-то примерно так...

//всякие определения (wcfwrapper.h)
#pragma once

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace ::MyService;
using namespace ::MyServiceHost;

...

//Это то, что видит Delphi-оболочка

#pragma comment(linker, "/export:PluginConfigure=?PluginConfigure@@YGPADXZ,@3")
_declspec(dllexport) public char* PluginConfigure();

#pragma comment(linker, "/export:StartPlugService=?StartPlugService@@YGXXZ,@4")
_declspec(dllexport) public void StartPlugService();

#pragma comment(linker, "/export:StopPlugService=?StopPlugService@@YGXXZ,@5")
_declspec(dllexport) public void StopPlugService();

...

#pragma comment(linker, "/export:WriteLine=?WriteLine@@YGXPADH@Z,@9")
_declspec(dllexport) public void _stdcall WriteLine(char* line, int len);

...

//Это то, чего Delphi-оболочка не видит...

//Контейнер для управляемого класса хоста... Сокрытый в листве %)))


class WCFHostWrapper
{
private:
IntPtr _h_instPtr; //Указатель для хранения ссылки на экземпляр управляемого хоста
public:
WCFHostWrapper()
{
::MyServiceHost::MyServiceHost^ _hostManInst = gcnew::MyServiceHost::MyServiceHost();
_h_instPtr = static_cast<IntPtr>(GCHandle::Alloc(_hostManInst));
}
~WCFHostWrapper()
{
static_cast<GCHandle>(_h_instPtr).Free();
}
void Start(); //Контейнер метода Start()
void Stop(); //Конетйнер метода Stop()
void ShowWindow(); //Контейнер метода ShowWindow()
void WriteLine(char* line, int len);//И это тоже всего лишь контейнер метода )))
};


***
//Собственно реализация оболочки (wcfwrapper.cpp)

// This is the main DLL file.

#include "stdafx.h"

#include <msclr/gcroot.h>
#include "wcfwrapperlib.h"

using namespace ::msclr;
using namespace ::ServiceModel;
using namespace ::MyServiceHost;
using namespace ::Text;

static HostWrap hw; //Экземпляр контейнера хоста

...

_declspec(dllexport) public char* PluginConfigure()
{
hw.ShowWindow();
return "Params parsing result";
}

_declspec(dllexport) public void StartPlugService()
{
hw.Start();
}

_declspec(dllexport) public void StopPlugService()
{
hw.Stop();
}

...


_declspec(dllexport) public void WriteLine(char* line, int len)
{
hw.WriteLine(line, len);
}

...

//Метод запуска управляемого сервиса через оболочку
void WCFHostWrapper::Start()
{
::MyServiceHost::MyServiceHost^ h = safe_cast<::MyServiceHost::MyServiceHost^>(static_cast<GCHandle>(_h_instPtr).Target);
h->Start();

}

//Метод останова управляемого сервиса через оболочку
void WCFHostWrapper::Stop()
{
::MyServiceHost::MyServiceHost^ h = safe_cast<::MyServiceHost::MyServiceHost^>(static_cast<GCHandle>(_h_instPtr).Target);
h->Stop();
}

...

void WCFHostWrapper::ShowWindow()
{
::MyServiceHost::MyServiceHost^ h = safe_cast<::MyServiceHost::MyServiceHost^>(static_cast<GCHandle>(_h_instPtr).Target);
h->ShowWindow();
}

...

void WCFHostWrapper::WriteLine(char* line, int len)
{
::MyServiceHost::MyServiceHost^ h = safe_cast<::MyServiceHost::MyServiceHost^>(static_cast<GCHandle>(_h_instPtr).Target);

String ^ s = Marshal::PtrToStringAnsi((IntPtr) (char *) line, len);
h->WriteLine(s);
}