Сервер выступает только для получения и отправки данных, а все что связано с HTML и простыми функциями можно делать на клиенте.
По сути это замена Silverlight
Так или иначе удобнее делать вызов через методы аля WCF
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> Это значительно сложнее. Суть делать все на стороне клиента
Нативный / managed код на клиенте? Ну так поднимите там любой web api-host, несложно абсолютно.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> Это значительно сложнее. Суть делать все на стороне клиента S>Нативный / managed код на клиенте? Ну так поднимите там любой web api-host, несложно абсолютно.
И смысл? Либо я должен все методы и классы прописать на стороне web api-host/ При этом данные получать не ввиде метода, в ввиде
Который можно получить более удобоваримо и сразу с автоматической оберткой
this.forecasts = await wrap.WeatherForecasts();
Эту обертку можно сделать и к нативу и Вэб сервисам и к HTTP сервисам.
Кстати и в 1С лучше будут использовать через Вэб или HTTP сервисы нежели напрямую работать с объектами .Net.
Хотя удобнее сразу работать с классами .Net все в одном месте и так же проблема с объектами. Хранить их на сервере или сериализовывать передавая туда и обратно
Мой подход удобнее чем WCF.
Но он никому не нужен. Я дурак.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> Это значительно сложнее. Суть делать все на стороне клиента S>Нативный / managed код на клиенте? Ну так поднимите там любой web api-host, несложно абсолютно.
Ну смотря какая логика. Возьмем пример нынешнего Xamarin. Xamarin.Forms очень беден, поэтому многие делают общую логику отдельно (которая может быть сложной),
а морду рисуют для каждой ОС отдельно.
Сейчас для отличных от Win декстопов нет UI.
Но например у тебя есть приложение на UWP или WPF. Можно выделить логику отдельно, а морду нарисовать на Angular 2
Разница использования .Net кода только в том, что все методы асинхронные. При этом можно подписываться к событиям 1С,.Net Core. Динамическая компиляция класса обертки для получения событий .Net объекта в 1С. Для Вэб апи нужно городить SignalR или WebSocket.
То есть перенести приложение достаточно легко, в отличие от web api-host.
Но вот мне интересно, а чем тебе не нравится мой вариант. Чем он хуже web api-host?
Я вижу только одни достоинства.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> Ну смотря какая логика. Возьмем пример нынешнего Xamarin. Xamarin.Forms очень беден, поэтому многие делают общую логику отдельно (которая может быть сложной), S>а морду рисуют для каждой ОС отдельно.
Таки посмотри вот это и вот это. Нет смысла ломиться в открытую дверь
S> Сейчас для отличных от Win декстопов нет UI. S>Но например у тебя есть приложение на UWP или WPF. Можно выделить логику отдельно, а морду нарисовать на Angular 2
Если есть человек пять, которые несколько лет будут работать на доведение до ума интеропа, биндинга и прочего-прочего-прочего — ок. Если нет — затея из серии писать в одно лицо СУБД.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> Ну смотря какая логика. Возьмем пример нынешнего Xamarin. Xamarin.Forms очень беден, поэтому многие делают общую логику отдельно (которая может быть сложной), S>>а морду рисуют для каждой ОС отдельно. S>Таки посмотри вот это и вот это. Нет смысла ломиться в открытую дверь
Ну моложцы развиваются. Но это не для декстопа.
S>> Сейчас для отличных от Win декстопов нет UI. S>>Но например у тебя есть приложение на UWP или WPF. Можно выделить логику отдельно, а морду нарисовать на Angular 2 S>Если есть человек пять, которые несколько лет будут работать на доведение до ума интеропа, биндинга и прочего-прочего-прочего — ок. Если нет — затея из серии писать в одно лицо СУБД.
Ну во первых оно уже работает для 1С. Сделано за две недели одним человеком при этом программистом 1С.
Биндинг уже есть в Angular 2.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> Ну моложцы развиваются. Но это не для декстопа.
Десктоп как рынок жив только для профессионального софта. В моде нынче рынок консумеров, для которых компьютер — чисто бытовое устройство, как телевизор или мясорубка.
Всё остальное — веб.
S>>Если есть человек пять, которые несколько лет будут работать на доведение до ума интеропа, биндинга и прочего-прочего-прочего — ок. Если нет — затея из серии писать в одно лицо СУБД. S> Ну во первых оно уже работает для 1С. Сделано за две недели одним человеком при этом программистом 1С.
Нет. Сделано — это не есть код. Сделано — это есть продукт. Т.е. помимо кода — сайт, документашка и community, как минимум. Иначе мы обсуждаем вещь, которая интересна только автору и которая загнётся сразу же, как только автор потеряет к ней интерес.
S>Биндинг уже есть в Angular 2.
К значениям из хоста — нет. Подсказка: как только дело от примитивных текстовых полей переходит, скажем, к гриду или дереву с возможностью перетаскивания узлов, так всё сразу становится ооочень тоскливо.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> Ну моложцы развиваются. Но это не для декстопа. S>Десктоп как рынок жив только для профессионального софта. В моде нынче рынок консумеров, для которых компьютер — чисто бытовое устройство, как телевизор или мясорубка. S>Всё остальное — веб.
Корпоративный софт является профессиональным? Я в нем работаю.
S>>>Если есть человек пять, которые несколько лет будут работать на доведение до ума интеропа, биндинга и прочего-прочего-прочего — ок. Если нет — затея из серии писать в одно лицо СУБД. S>> Ну во первых оно уже работает для 1С. Сделано за две недели одним человеком при этом программистом 1С. S>Нет. Сделано — это не есть код. Сделано — это есть продукт. Т.е. помимо кода — сайт, документашка и community, как минимум. Иначе мы обсуждаем вещь, которая интересна только автору и которая загнётся сразу же, как только автор потеряет к ней интерес.
Поэтому я и вышел с опросом идеи. Но кстати есть кроссплатформенная замена COM XPCOM
Я предлагаю легкий способ замены COM для использования классов .Net Core
Понятно, что если я и зделаю, то мало кто будет пользоваться.
Но я не пойму в чем идея то плоха?
S>>Биндинг уже есть в Angular 2. S>К значениям из хоста — нет. Подсказка: как только дело от примитивных текстовых полей переходит, скажем, к гриду или дереву с возможностью перетаскивания узлов, так всё сразу становится ооочень тоскливо.
Ну и Angular 2 развивается.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>Десктоп как рынок жив только для профессионального софта. S> Корпоративный софт является профессиональным? Я в нем работаю.
Да.
S>Поэтому я и вышел с опросом идеи. Но кстати есть кроссплатформенная замена COM XPCOM
Которая просто костыль для портирования аддонов? У меня плохие новости.
S>Но я не пойму в чем идея то плоха?
Сама идея — замечательная, но только пока мы обсуждаем в духе "за всё хорошее и против всего плохого". Как только переходим к реальной жизни, так сразу начинаются реальные проблемы. Поддержка там, перфоманс, простота использования, наличие специалистов на рынке, вот это всё.
S>>>Биндинг уже есть в Angular 2. S>>К значениям из хоста — нет. Подсказка: как только дело от примитивных текстовых полей переходит, скажем, к гриду или дереву с возможностью перетаскивания узлов, так всё сразу становится ооочень тоскливо. S> Ну и Angular 2 развивается.
Такое впечатление, что вы не читаете то, что вам пишут и вместо этого подставляете первый ответ из гугла, без обид. Нет, angular не развивается в направлении "двусторонний сериализуемый биндинг".
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>Десктоп как рынок жив только для профессионального софта. S>> Корпоративный софт является профессиональным? Я в нем работаю. S>Да.
S>>Поэтому я и вышел с опросом идеи. Но кстати есть кроссплатформенная замена COM XPCOM S>Которая просто костыль для портирования аддонов? У меня плохие новости.
Ну я говорю пока про Native Client Messaging System
Такая же система обмена есть и в EDGE/ Кстати а у тебя не ссылочки на создания плагинов для Edge в VS.
Так или иначе роль обертки минимальна.
S>>Но я не пойму в чем идея то плоха? S>Сама идея — замечательная, но только пока мы обсуждаем в духе "за всё хорошее и против всего плохого". Как только переходим к реальной жизни, так сразу начинаются реальные проблемы. Поддержка там, перфоманс, простота использования, наличие специалистов на рынке, вот это всё.
1. Поддержка. Если этим заинтересуется тот же гугл поддержка обеспечена!
2. Перфоманс. Но мы же не будем в циклах крутить вызовы. Обычно нужно просто получить событие от хоста и передать данные на хост при событии на клиенте. Это обычная логика GUI/
3. Ну синтаксис использования то мало отличается от C#, а TypeScript набирает обороты.
S>>>>Биндинг уже есть в Angular 2. S>>>К значениям из хоста — нет. Подсказка: как только дело от примитивных текстовых полей переходит, скажем, к гриду или дереву с возможностью перетаскивания узлов, так всё сразу становится ооочень тоскливо. S>> Ну и Angular 2 развивается. S>Такое впечатление, что вы не читаете то, что вам пишут и вместо этого подставляете первый ответ из гугла, без обид. Нет, angular не развивается в направлении "двусторонний сериализуемый биндинг".
Может быть я не совсем понял. Но например через Proxy можно подсунуть любой объект который будет взаимодействовать с хостом, а в нем автоматическая сереализация десериализация параметров.
Можно кстати сделать и синхронный вызов. Нужно посмотреть как там с мьютексами семаформаи эвентами.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinix, Вы писали: S>Если есть человек пять, которые несколько лет будут работать на доведение до ума интеропа, биндинга и прочего-прочего-прочего — ок. Если нет — затея из серии писать в одно лицо СУБД.
Там интеропа то нет. Есть просто вызов нескольких статических методов .Net.
Плюс один нативный метод для событий.
Вот и все взаимодействи на стороне натива.
А вот на стороне .Net там немного покруче. Там и параметы массивы, ref параметры, автовывотд дженерик типов, поиск методов расширений и опять же с дженериками.
Но то, что сделано на строне .Net легко применить при любом изменении WEB плугина
Опять же есть поддержка событий и асинхронных методов через динамическую компиляцию.
Можно и без асинхронности вызова обычных методов обойтись.
При этом можно зарегистрировать различные обработчики.
В итоге можно вызывать асинхронные методы например добавив кллючевое слово async, а для свойств можно использовать @ в начале свойства
let url= "https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx");
let response= await client.async.GetStringAsync(url);
let response= client.GetStringAsync(url).@Result;
для ref параметров можно использовать класс обертку
class RefParam{
isRefparam=true;
constructor(public Value:any);
}
и солнце б утром не вставало, когда бы не было меня
Честно говоря, учитывая что ты нашел все необходимые материалы — в чём вопрос-то?
Если есть желания связаться с CEF — вопросы лучше задавать на оригинальном форуме (там больше людей в теме).
Если есть необходимость — я могу помочь в скайпе (направить в нужную сторону или попытаться объяснить на пальцах по мере возникновения вопросов), как делаются те или иные вещи в CEF.
В плане JS->Native вызовов всё сводится к нескольким подходам:
1. XHR и в browser process реализуется resource handler. Преимущество: renderer->browser IPC достаётся бесплатно. Недостаток: только асинхронные вызовы.
2. V8Handler/V8Accessor. Преимущество: выставляем любой JS API. Недостаток: renderer->browser IPC бесплатно не достаётся, но посылку сообщений делаем через CefProcessMessage (только асинхронный обмен). Если нужен иной IPC — никто не помешает создать. В принципе это самый универсальный вариант.
3. Используем CEF's message router. Как бы всё готово, но по сути делает оно тоже что и (2), выставляя готовый шлюз в JS.
Так или иначе на стороне JS должна быть какая-то клиентская библиотека (генерённая?) выставляющая вменяемый API для потребления, поэтому каким именно образом оно реализовано (1-3) обычно не так уж важно.
Ах, для CefGlue v1 (это для CEF 1), я делал автоматический экспозер C# объектов в JS. Для CefGlue v3 я такой функциональности не делал. Хотя нехило бы воскресить. В принципе там рутины больше.
UPD: Но сейчас, если бы я начал это делать — я бы поддержал это дело ещё лучше — от async/await до колбэков. Хотя с ещё большей вероятностью все вызовы из JS сделал бы асинхронными, т.к. блокировка потоков — это как то чего в хроме лучше не делать. Хотя в принципе можно. Вопрос насколько это необходимо.
Здравствуйте, Serginio1, Вы писали:
S> Огромное спасибо за ответ. 2 дня боролся. Но поборол. У меня опыта на С++ мало. Вспоминаю, то что забыл и учу то, что не знал. Но сегодня подключил
Молодец.
Не многие юзеры начинаю с вики, и в итоге задают глупые вопросы.
S> У CEF есть и dev tools. Но вот нужно правильно подключить файлы из CefClient. Разберусь. S>Главное сдвинулся с мертвой точки. Еще раз огромное спасибо!
devtools открываются просто вызовом метода из CefBrowserHost по моему. Для этого от CefClient ничего не нужно. Из cefclient обязательно перенеси манифест приложения, иначе потом напорешься на всякие неприятные штуки.
Во всём остальном вроде всё норм, в смысле с моей стороны добавить нечего — дерзай!
PS: Потом, когда получится что-то интересное (если это публично доступно), не забудь похвастаться в соответствующим разделе форума по CEF.
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
S>> Огромное спасибо за ответ. 2 дня боролся. Но поборол. У меня опыта на С++ мало. Вспоминаю, то что забыл и учу то, что не знал. Но сегодня подключил F> Молодец. F> Не многие юзеры начинаю с вики, и в итоге задают глупые вопросы.
Ну в Вики мало чего. Нужно реальный код смотреть. А его не так и много. Но разобрался. S>> У CEF есть и dev tools. Но вот нужно правильно подключить файлы из CefClient. Разберусь. S>>Главное сдвинулся с мертвой точки. Еще раз огромное спасибо! F> devtools открываются просто вызовом метода из CefBrowserHost по моему. Для этого от CefClient ничего не нужно. Из cefclient обязательно перенеси манифест приложения, иначе потом напорешься на всякие неприятные штуки.
Ну я пока на sefSimple эксперементирую. F> Во всём остальном вроде всё норм, в смысле с моей стороны добавить нечего — дерзай!
F> PS: Потом, когда получится что-то интересное (если это публично доступно), не забудь похвастаться в соответствующим разделе форума по CEF.
Спасибо! А то руки опускаются, что никому это не интересно
Кстати в Proxy можно переопределить new
let HttpClient= NetWrap.GetType("System.Net.Http.HttpClient","System.Net.Http.dll");
let HttpClientHandler = await NetWrap.GetType("System.Net.Http.HttpClientHandler","System.Net.Http");
let client=new HttpClient(HttpClientHandler);
let url= "https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx");
let response= await client.async.GetStringAsync(url);
let response= client.GetStringAsync(url).@Result;
}
Кроме того можно реализовать enumerate для доступа через
for(let prop in list)
То есть код максимально приблизить к C#
и солнце б утром не вставало, когда бы не было меня
Что удалось
1. Вызов методов и свойств
2. Установка свойств
3. Вызов объекта как функции
4. Создание объекта (new) через объект
5. Применение енумератора к объекту
export interface NetObjectinterface {
(): void;
Id: number;
isNetObject: boolean;
}
function getNetObject(id: number): NetObjectinterface {
let netObject = <NetObjectinterface>function (start: number) { };
netObject.Id = id;
netObject.isNetObject = true;
return netObject;
}
export class NetObject {
static GetNetObject(id: number) { return getNetObject(id); }
}
export var NetObjectHandler: ProxyHandler<NetObject> = {
get: (target: NetObject, name: any) => {
if (name === Symbol.iterator) {
return function () {
let nextIndex=5;
return {
next: function () {
return nextIndex < 10 ?
{ value: nextIndex++, done: false } :
{ value: undefined, done: true };
}
}
}
}
if (name.startsWith('_')) {
return "propery";
}
return (...args: any[]) => {
let res: string = name;
for (let param of args) {
res += ", " + (typeof param);
if (typeof param == "object") {
res += ", " + param().Id.toString();
}
else {
res += ", " + param.toString();
}
}
return res;
}
},
set: function (target, prop, value, receiver) {
console.log("called: " + prop.toString() + " = " + value);
return true;
},
apply: (target, that, args) => {
if (args.length == 0)
return target;
return args[0];
},
construct: (target, argumentsList, newTarget) => {
var res = NetObject.GetNetObject(5);
return new Proxy(res, NetObjectHandler)
}
}
И использование
submit(user:User) {
//let res = new NetObject(1);
let res = NetObject.GetNetObject(1);
let obj:any = new Proxy(res, NetObjectHandler);
this.PropertyStr = obj._Property;
obj._Property = 5;
try{
this.PropertyStr = obj().Id.toString();
this.MethodStr = obj("Hello");
this.ConstructStr = new obj(3)().Id.toString();
}
catch (e) {
console.log("ошибка " + e);
}
for (let i of obj)
console.log(i);
let p = new Proxy([1,2,3], {
enumerate: (target) => {
return ["a", "b", "c"];
}
});
for (var x of p) { // "called"
console.log(x); // "a"
}
}
Так как не нашел финализатора думаю удалять объекты через ()
Например StringBuilder при вызове методов возвращает ссылку на себя
let =Net.GetType("System.Text.StringBuilder");
let sb= new StringBuilder("Первый");
sb.Append("Новая Строка")(1);
sb.AppendLine()(1);
sb.Append("Вторая Строка")(1);
sb.AppendLine()(1);
sb.AppendFormat("AppendFormat {0}, {1}, {2}, {3}, {4},", "Строка", 21, 45.89, ТекущаяДата(),истина )(1);
sb.AppendLine()(1);
sb(1);
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
F>> Не многие юзеры начинаю с вики, и в итоге задают глупые вопросы. S> Ну в Вики мало чего. Нужно реальный код смотреть. А его не так и много. Но разобрался.
Если есть чёткое понимание, что нужно сделать — но не знаешь куда копать — всегда можно спросить на CEF forum. Там ткнут быстро в нужные точки. В вики покрыты основные/частые вопросы. Безусловно в отсутствии серьезных врайтеров вики — информации может и маловато. Но потом понимаешь что там в общем-то всё было, просто информация часто оказывается недопонятной. Так же абсолютно нельзя игнорировать документацию к API.
F>> PS: Потом, когда получится что-то интересное (если это публично доступно), не забудь похвастаться в соответствующим разделе форума по CEF. S> Спасибо! А то руки опускаются, что никому это не интересно
Я не очень понимаю что именно ты делаешь на самом деле.
Здравствуйте, Serginio1, Вы писали:
S>Кстати а не подскажешь как мне прикрутить аналог финализатора. S>Нашел <span class='lineQuote level1'>S>Make weak references to JavaScript Objects</span> S> К обычному JS у меня не получилось прикрутить. Может как то из CEF можно прикрутить финализатор?
Финализатор чего? Если речь идёт о JS объекте — то никак. Можешь полностью игнорировать финализаторы V8 т.к. половина вызывается,
другая половина — вызвана не будет т.к. persistent handles мешают этому. Кроме того by-design — при разрушении контекста (JS мира),
вся куча дропается и всё. Никаких вызовов финализаторов не будет by-design (V8).
Если речь идёт о объектах CEF — то — как только счетчик ссылок становится равным 0 — пора вычищать связанные с объектом вещи. Это
широко используется в CEF и клиентах (в нэйтиве дёргается деструктор, для CefGlue — я вызываю Dispose(true), в текущих версиях — только
для некоторых объектов — моя старая ошибка что этого не делал изначально).
Если речь идёт о CEF V8Context и связанных объектах — то после OnContextDestroy никаких ссылок не должно оставаться на V8Context.
Это значит что ты должен здесь всё связанное с V8 высвободить. (Контекст принадлежит фрейму, но контекст != фрейм — один и тот же фрейм
может иметь несколько конекстов, ну не одновременно, а просто перезагрузка контента и т.п. Но вообще about:blank — затем URL — вот один
фрейм но два контекста, при этом первый контекст высвобождается почти сразу, поэтому желательно так же минимизировать работу
в OnContextCreated — хотя это и не всегда возможно — ведь в динамических фреймах — URL будет как раз тот самый about:blank).
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
F>>> Не многие юзеры начинаю с вики, и в итоге задают глупые вопросы. S>> Ну в Вики мало чего. Нужно реальный код смотреть. А его не так и много. Но разобрался. F> Если есть чёткое понимание, что нужно сделать — но не знаешь куда копать — всегда можно спросить на CEF forum. Там ткнут быстро в нужные точки. В вики покрыты основные/частые вопросы. Безусловно в отсутствии серьезных врайтеров вики — информации может и маловато. Но потом понимаешь что там в общем-то всё было, просто информация часто оказывается недопонятной. Так же абсолютно нельзя игнорировать документацию к API.
Эх проблемы у меня с англицким 1С ник я. F>>> PS: Потом, когда получится что-то интересное (если это публично доступно), не забудь похвастаться в соответствующим разделе форума по CEF. S>> Спасибо! А то руки опускаются, что никому это не интересно F> Я не очень понимаю что именно ты делаешь на самом деле.
Я делаю порт для использования .Net классов в Angular 2/
Например есть приложение на WPF или UWP, но его нужно перенести на линукс.
Мы можем взять компоненты Ангулар например Компоненты от PrimeNG
и использовать .Net Core классы
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Эх проблемы у меня с англицким 1С ник я.
У меня тоже проблемы с английским. Нужно просто пытаться. Кроме того там половина интернета пишет как попало, причём нэйтивы тоже. Так шо забей. Если не пытаться — то точно не получится.
S> Я делаю порт для использования .Net классов в Angular 2/ S>Например есть приложение на WPF или UWP, но его нужно перенести на линукс. S>Мы можем взять компоненты Ангулар например Компоненты от PrimeNG S> и использовать .Net Core классы
Для этого нужно такое сильно взаимодействие? Не лучше ли выставить некий "сервисный" слой в браузер? Не, то что ты делаешь — я приблизительно понимаю. Выглядит круто — но именно есть ли в этом необходимость — я немного сомневаюсь. Ну т.е. на примере: использовать StringBuilder — явно оверкил, массив и push в него — гораздо лучше для JS. Не, с другой стороны я понимаю, что если мы позволяем работать с любым C# объектом — то в общем-то уже принципиальных ограничений нет.
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
S>>Эх проблемы у меня с англицким 1С ник я. F> У меня тоже проблемы с английским. Нужно просто пытаться. Кроме того там половина интернета пишет как попало, причём нэйтивы тоже. Так шо забей. Если не пытаться — то точно не получится.
S>> Я делаю порт для использования .Net классов в Angular 2/ S>>Например есть приложение на WPF или UWP, но его нужно перенести на линукс. S>>Мы можем взять компоненты Ангулар например Компоненты от PrimeNG S>> и использовать .Net Core классы F> Для этого нужно такое сильно взаимодействие? Не лучше ли выставить некий "сервисный" слой в браузер? Не, то что ты делаешь — я приблизительно понимаю. Выглядит круто — но именно есть ли в этом необходимость — я немного сомневаюсь. Ну т.е. на примере: использовать StringBuilder — явно оверкил, массив и push в него — гораздо лучше для JS. Не, с другой стороны я понимаю, что если мы позволяем работать с любым C# объектом — то в общем-то уже принципиальных ограничений нет.
Ну это просто для примера.
Я в 1С использую для доступа к торговому оборудованию, к Вэб сервисам по ws протоколам,
котовым компонентам. AngleSharp конечно бессмысленно, но например работа с Linux, Excel, Word, OpenXML,ADO и Net
Кроме того сделаю передачу JS объектов на сторону .Net с использованием через DynamicObject.
На самом деле можно сделать определенную конфигурацию CEF для всех платформ и можно делать кроспалатформенные декстопные приложения.
Намного легче чем на том же Electron. Или по крайней мере её альтернатива.
.Net Core развивается.
Ну и жалко, что идея для 1С пропадает. Покажу реализацию, может кого и заинтересует.
Там так или иначе большой пласт всего поднят.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, fddima, Вы писали:
F> Если речь идёт о объектах CEF — то — как только счетчик ссылок становится равным 0 — пора вычищать связанные с объектом вещи. Это F>широко используется в CEF и клиентах (в нэйтиве дёргается деструктор, для CefGlue — я вызываю Dispose(true), в текущих версиях — только F>для некоторых объектов — моя старая ошибка что этого не делал изначально).
Я поясню тут момент:
В CEF некоторые API могут показаться незавершенными. Примером может быть:
bool CefCookieManager::VisitAllCookies(CefRefPtr<CefCookieVisitor> visitor);
///
// Interface to implement for visiting cookie values. The methods of this class
// will always be called on the IO thread.
///
/*--cef(source=client)--*/class CefCookieVisitor : public virtual CefBase {
public:
///
// Method that will be called once for each cookie. |count| is the 0-based
// index for the current cookie. |total| is the total number of cookies.
// Set |deleteCookie| to true to delete the cookie currently being visited.
// Return false to stop visiting cookies. This method may never be called if
// no cookies are found.
///
/*--cef()--*/virtual bool Visit(const CefCookie& cookie, int count, int total,
bool& deleteCookie) =0;
};
Жирным я выделил именно это место. Как же нам быть, если мы хотим посетить куки, дождаться когда это произойдет и пойти дальше? Так вот "хитрость" (для меня это именно хитрость — я долгое время в упор этого не замечал), в том, что когда объект CefCookieVisitor уничтожается — означает что VisitAllCookies завершил свою работу (по той или иной причине — всё отвизитили, или решили прервать в Visit).
Теперь развернуто:
S> Ну получается около 15 000 вызовов в секунду. Правда в отладчике. Плюс еще накладные на Proxy появятся. Но приемлемо.
Для того что бы взять хоть какой-то baseline — возьми cefclient -> Tests -> Other Tests -> Javascript Performance Tests первый и второй. Один из них (второй) делал я, потом его допилили и втащили в cefclient, — что именно он меряет теперь я уже не очень уверен — да и вообще забыл уже всё. Откусили от него как минимум .NET GC memory.
Сейчас у меня тесты эти показывают 150K-250K вызовов (от железа естественно зависит). Ну судя и комментариев — очень похоже на то что делаешь ты (посмотри там есть int -> int функция).
Но, самый быстрый биндинг был в cefglue-v1. Даже быстрее чем в родном C++, потому, что накладные расходы на маршалинг в C++ типы я убрал, оставшись в этой части на C API. И если я не ошибаюсь, он делал 500K вызовов, правда полагаясь на сборку мусора (ненужных CefV8Value), и затем я переделал так, что бы мусора не создавалось — немного медленне... 350K, — но в целом, это лучше чем жрать память, да и выхватывать косяки с временем жизни объектов тоже неинтересно. Но правда точные цифры я забыл. Но то что в cefclient — это не максимум который можно выжать — это точно.
К сожалению обёртка вокруг V8 выполненная в стиле CEF — не самая быстрая по определению. Ну, а передача ещё и сложных аргументов — совсем ховайся. Приблизительно поэтому и следует избегать активного использования простых акцессоров. Хотя для большинства задач и 1K хватит с головой.
У тебя наверное действительно из-за дебага такая беда (CEF релизный или дебажный? если дебажный — то точно из-за этого). Думаю даже с прокси нужно стремиться к 100К вызовов должно быть стабильно.
Так же отмечу: asynchronous vs synchornous режимы — делая всю работу синхронно — ты создашь кучу мусора в V8, поэтому наиболее оптимально и более близко к реальности — всё же хоть иногда делать yield (setTimeout(0)), да бы другим задачам (в том числе и рендер) на основном потоке осталось время поразмышлять. И мусор раньше собирался только в это время. Сейчас возможно он собирается и без этого.
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
F> Я как доберусь до ответа — ты сообщения сносишь.
Да я задам вопрос, потом его решу. F> TL;DR: F> — даешь 100К вызовов; F> — меряй память;
Без JS даже ~500К.
F> Теперь развернуто:
S>> Ну получается около 15 000 вызовов в секунду. Правда в отладчике. Плюс еще накладные на Proxy появятся. Но приемлемо. F> Для того что бы взять хоть какой-то baseline — возьми cefclient -> Tests -> Other Tests -> Javascript Performance Tests первый и второй. Один из них (второй) делал я, потом его допилили и втащили в cefclient, — что именно он меряет теперь я уже не очень уверен — да и вообще забыл уже всё. Откусили от него как минимум .NET GC memory. F> Сейчас у меня тесты эти показывают 150K-250K вызовов (от железа естественно зависит). Ну судя и комментариев — очень похоже на то что делаешь ты (посмотри там есть int -> int функция). F> Но, самый быстрый биндинг был в cefglue-v1. Даже быстрее чем в родном C++, потому, что накладные расходы на маршалинг в C++ типы я убрал, оставшись в этой части на C API. И если я не ошибаюсь, он делал 500K вызовов, правда полагаясь на сборку мусора (ненужных CefV8Value), и затем я переделал так, что бы мусора не создавалось — немного медленне... 350K, — но в целом, это лучше чем жрать память, да и выхватывать косяки с временем жизни объектов тоже неинтересно. Но правда точные цифры я забыл. Но то что в cefclient — это не максимум который можно выжать — это точно. F> К сожалению обёртка вокруг V8 выполненная в стиле CEF — не самая быстрая по определению. Ну, а передача ещё и сложных аргументов — совсем ховайся. Приблизительно поэтому и следует избегать активного использования простых акцессоров. Хотя для большинства задач и 1K хватит с головой.
F> У тебя наверное действительно из-за дебага такая беда (CEF релизный или дебажный? если дебажный — то точно из-за этого). Думаю даже с прокси нужно стремиться к 100К вызовов должно быть стабильно.
F> Так же отмечу: asynchronous vs synchornous режимы — делая всю работу синхронно — ты создашь кучу мусора в V8, поэтому наиболее оптимально и более близко к реальности — всё же хоть иногда делать yield (setTimeout(0)), да бы другим задачам (в том числе и рендер) на основном потоке осталось время поразмышлять. И мусор раньше собирался только в это время. Сейчас возможно он собирается и без этого.
Да. Дебажный. Но по моему JS тормозит.
Сейчас проверю вызов без Net
и солнце б утром не вставало, когда бы не было меня
Передать в Net ссылки на методы объекта,А из Net вызвать метод об
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int МетодОбъекта2Delegate(IntPtr self, int Число1, int Число2);
public static void CallCppMethodObject(IntPtr Self, IntPtr АдресМетода)
{
var execute = Marshal.GetDelegateForFunctionPointer<МетодОбъекта2Delegate>(АдресМетода);
var res = execute(Self, Self.ToInt32(), 777);
execute(Self, Self.ToInt32(), res);
}
Можно передавать ссылку на объект обертку с CefRefPtr<CefV8Value> и контекстом.
Только на время вызова функции из JS
Может есть другие возможности получить ссылку на CefRefPtr<CefV8Value>.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> Можно передавать ссылку на объект обертку с CefRefPtr<CefV8Value> и контекстом. S>Только на время вызова функции из JS S>Может есть другие возможности получить ссылку на CefRefPtr<CefV8Value>.
Это наверное наиболее простой и универсальный способ как минимум для начала. Второе, если ещё не делаешь — избавляйся от них как можно раньше — т.е. нужен инт — получай инт. Тогда на долю V8Value останутсятолько сложные типы и нужно решить что тут делать. POD-ы и функции. Функции проблемнее всего — т.к. мы обычно хотим сохранить на них ссылку и дернуть когданибудь, но их нужно дропнуть в OnContextReleased.
Насчет как легче обернуть C++ объекты — не скажу. CefGlue потребляет C API и создает свои врапперы вокруг этого. Клиентская C++ библиотека делает тоже самое. Поэтому пробрасывать С++ объекты неэффективно ну и геморно.
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
S>> Можно передавать ссылку на объект обертку с CefRefPtr<CefV8Value> и контекстом. S>>Только на время вызова функции из JS S>>Может есть другие возможности получить ссылку на CefRefPtr<CefV8Value>.
F> Это наверное наиболее простой и универсальный способ как минимум для начала. Второе, если ещё не делаешь — избавляйся от них как можно раньше — т.е. нужен инт — получай инт. Тогда на долю V8Value останутсятолько сложные типы и нужно решить что тут делать. POD-ы и функции. Функции проблемнее всего — т.к. мы обычно хотим сохранить на них ссылку и дернуть когданибудь, но их нужно дропнуть в OnContextReleased.
F> Насчет как легче обернуть C++ объекты — не скажу. CefGlue потребляет C API и создает свои врапперы вокруг этого. Клиентская C++ библиотека делает тоже самое. Поэтому пробрасывать С++ объекты неэффективно ну и геморно.
Попробую Объяснить
Создадим класс
Class JSObjectProxy
{
//
CefRefPtr<CefV8Value> JSObject;
vector<JSObjectProxy*> listProxy;
CefRefPtr<CefV8Context> context; // Или один класс содержащий контекст
// Этот метод дергаем из .Net Corebool CallJsMethod(wchar_t* method,tvarstruct* params, int argCount,wchar_t** Error)
{
здесь вызываем
JSObject_->ExecuteFunctionWithContext(callback_context_, NULL, args, retval, exception, false))
и если функция возвращает в retval JSObject то создаем JSObjectProxy
и добавляем его в listProxy, что бы ссылка не обнулилась.
}
}
vector<JSObjectProxy*> это вектор который создается в Методе Exexute
члены которого будут уничтожаться при выходе из метода.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Есть идея иметь доступ из TypeScript к методам и свойствам классов .Net. S>У меня есть опыт вызова методов .Net объектов из натива https://habrahabr.ru/users/serginio1/topics/
Если нужна кросс-платформенность возможно будет вариантом собрать всё через Mono с ключом --enable-nacl:
--enable-nacl
This configures the Mono compiler to generate code suitable to be used by Google's Native Client: http://code.google.com/p/nativeclient/
Currently this is used with Mono's AOT engine as Native Client does not support JIT engines yet.
@Serginio1 — я не очень думаю что тут это вообще кому-то реально интересно. Т.е. я на самом деле предлагаю перенести обсуждения или вопросы на http://magpcss.org/ceforum. Более того — если форум поддерживает русский язык — просто пиши по русски в CefGlue разделе. Я там отвечу. Вообще должен. Возможно тебе прийдется пройти валидацию сначала, но это дело поправимое.
Да, там не очень неудобно. Однако, — здесь — это оффтопик, а у нас идут совершенно специфичные разговоры.
Кроме того, можешь вместо этого, попросить у меня через PM скайп — и скорее всего мы будем общаться гораздо быстрее минуя эту байду. Тут (в этой целевой аудитории) — нет смысла спрашивать. На magpcss.org — лучше на инглише. Более того — тебе и не надо дёргать меня то в плане дурных вопросов — это и так ясно. Поэтому я сразу сказал — что я на такое пойду. В общем — это из разряда "последнего китайского" — в том смысле — что, не стесняйся. Я давно тебя заочно знаю, — я не очень разделяю твоего фанатизма в плане того, что ты делаешь, (возможно всё ещё не понял что именно) — но поверь — лучше сделай, а потом выложи код или сделай серию блог-постов которые толком всё объяснят — толка от текущих наших и твоих самим с собой переговоров ИМХО всё равно никаких нет.
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Serginio1, Вы писали:
S>>Есть идея иметь доступ из TypeScript к методам и свойствам классов .Net. S>>У меня есть опыт вызова методов .Net объектов из натива https://habrahabr.ru/users/serginio1/topics/
_NN>Если нужна кросс-платформенность возможно будет вариантом собрать всё через Mono с ключом --enable-nacl: _NN>
_NN>--enable-nacl
_NN> This configures the Mono compiler to generate code suitable to be used by Google's Native Client: http://code.google.com/p/nativeclient/
_NN> Currently this is used with Mono's AOT engine as Native Client does not support JIT engines yet.
_NN>
Спасибо. До этого еще дойти надо. Но у меня работает через .Net Core и через рефлексию, но из натива.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, bnk, Вы писали:
bnk>Здравствуйте, Serginio1, Вы писали:
S>>Есть идея иметь доступ из TypeScript к методам и свойствам классов .Net.
bnk>Для построения UI есть sciter и обертка для него scitersharp например. bnk>Здесь даже отдельный под-форум есть
bnk>Или тебе это надо не для построения UI?
Мне интересно реализовать универсальную обертку классов Net для использования из натива.
Мы просто можем расширить JS через typescript с использованием классов .Net Core.
Можно достаточно легко перевести приложения на Wpf,UWP и WinForms на Angular 2 и мою прослойку
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
F>@Serginio1 — я не очень думаю что тут это вообще кому-то реально интересно. Т.е. я на самом деле предлагаю перенести обсуждения или вопросы на http://magpcss.org/ceforum. Более того — если форум поддерживает русский язык — просто пиши по русски в CefGlue разделе. Я там отвечу. Вообще должен. Возможно тебе прийдется пройти валидацию сначала, но это дело поправимое.
F>Да, там не очень неудобно. Однако, — здесь — это оффтопик, а у нас идут совершенно специфичные разговоры.
F>Кроме того, можешь вместо этого, попросить у меня через PM скайп — и скорее всего мы будем общаться гораздо быстрее минуя эту байду. Тут (в этой целевой аудитории) — нет смысла спрашивать. На magpcss.org — лучше на инглише. Более того — тебе и не надо дёргать меня то в плане дурных вопросов — это и так ясно. Поэтому я сразу сказал — что я на такое пойду. В общем — это из разряда "последнего китайского" — в том смысле — что, не стесняйся. Я давно тебя заочно знаю, — я не очень разделяю твоего фанатизма в плане того, что ты делаешь, (возможно всё ещё не понял что именно) — но поверь — лучше сделай, а потом выложи код или сделай серию блог-постов которые толком всё объяснят — толка от текущих наших и твоих самим с собой переговоров ИМХО всё равно никаких нет.
Огромное спасибо! Сейчас сделаю вызовм методов, свойств, делегатов, дженерик методов и асинхронных методов. Выложу, напишу статью.
Потом доделаю поддержку внешних событий, использование объектов и функций JS. Покажу использование динамической компиляции итд
А смысл, это использовать библиотеки .Net Core в сочетании с тем же Angular 2.
Например для торгового оборудования тебе нужно писать отдельный плагин и на C++.
Здесь ты можешь использовать готовые библиотеки под Net Core или написать свои. Итд.
Здравствуйте, Serginio1, Вы писали:
S> Мне интересно реализовать универсальную обертку классов Net для использования из натива.
По идее есть такой способ. Поставить на сборке галочку Com visible (в самом фреймворке вроде все сборки такие), поместить ее в gac, сделать regasm. .Net классы становятся юзабельными в любом нативном приложении (нужна поддержка COM)
опа опа мы воюем с нато
любит хавать этот кал
путинская вата
Здравствуйте, sr_dev, Вы писали:
_>Здравствуйте, Serginio1, Вы писали:
S>> Мне интересно реализовать универсальную обертку классов Net для использования из натива.
_>По идее есть такой способ. Поставить на сборке галочку Com visible (в самом фреймворке вроде все сборки такие), поместить ее в gac, сделать regasm. .Net классы становятся юзабельными в любом нативном приложении (нужна поддержка COM)
Здравствуйте, fddima, Вы писали:
F> Не хочу говорить за ТС — но он весьма грамотен. Уверен что и о скайтере он в курсе. Кроме того, афаик, есть ситуации где скайтер сольет. F> Спрашиваю ради интереса — как с accesibility у скайтера? Ну т.е. хотя бы виндовс нарратор — работает?
Честно говоря, я не знаю, не заморачивался на accesibility. Можно у автора спросить.
Здравствуйте, Serginio1, Вы писали:
S>Прошу прощения. Зарегистрировался на форуме. Но что то не могу отправить сообщение.
Первое сообщение нового юзера проходит премодерацию. Подожди денек.
S>Задам и здесь. Суть такая
...
S> В вики вроде написано, что CefPostTask рабочий. Сейчас посмотри Id потоков S>Да потоки разные. Сейчас буду рыть с очередью сообщений
По коду несовсем понятно, увы. Это всё где происходит? В рендерере нет UI потока — есть TID_RENDERER, он же главный. Ну это навскидку.
S>> В вики вроде написано, что CefPostTask рабочий. Сейчас посмотри Id потоков S>>Да потоки разные. Сейчас буду рыть с очередью сообщений F> По коду несовсем понятно, увы. Это всё где происходит? В рендерере нет UI потока — есть TID_RENDERER, он же главный. Ну это навскидку.
Огромное спасибо!!!
Ну скоро выложу рабочий вариант с async и прочими плюшками. Уже виден свет в конце туннеля
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
Сделал и работает на Angular 2 и TypeScript
let Net = NetObject.NetWrapper;
let del = NetObject.FlagDeleteObject;
//let SB = Net.Новый("System.Text.StringBuilder");let StringBuilder = Net.Тип("System.Text.StringBuilder");
let SB = new StringBuilder();
this.ResultExecute.push(new ResTest("SB", SB));
var res = SB.Append("Первая строка");
res(del);
var ToStr = SB.ToString();
this.ResultExecute.push(new ResTest("Tostring=", ToStr));
SB.Capacity=40;
res = SB._Capacity;
this.ResultExecute.push(new ResTest("_Capacity=", res));
SB(del);
let Тестовый = Net.Тип("TestDllForCoreClr.Тестовый", "TestDllForCoreClr");
var TO = new Тестовый("Свойство из Конструктора");
var resAsync = await TO.async.GetStringAsync();
this.ResultExecute.push(new ResTest("GetStringAsync", resAsync));
res = TO.ДженерикМетод(1, 2, 5);
this.ResultExecute.push(new ResTest("ДженерикМетод с выводом типов ", res));
let Int32 = Net.GetType("System.Int32");
res = TO.ДженерикМетод([Int32, Int32], 1, 2, 5);
this.ResultExecute.push(new ResTest("ДженерикМетод с аргeментами типов ", res));
var Dictionary2 = Net.GetType("System.Collections.Generic.Dictionary`2", "System.Collections");
var DyctionaryIS = Net.GetGenericType(Dictionary2, "System.Int32", "System.String");
var D = new DyctionaryIS();
res = TO.ДженерикМетод2(D, 99, "Hello");
this.ResultExecute.push(new ResTest("ДженерикМетод2 с выводом типов ", res));
res = TO.ДженерикМетод2([Int32, "System.String"],D, 99, "Hello");
this.ResultExecute.push(new ResTest("ДженерикМетод2 с аргументами ", res));
var task = TO.GetStringAsync();
res = await Net.async.ReturnParam(task);
this.ResultExecute.push(new ResTest("выполнение полученной задачи асинхронно ", res));
var array = Net.GetNetArray(1, "Привет", 4.4);
for (let item of array)
this.ResultExecute.push(new ResTest("Элемент ", item));
NetObject.DeleteNetObjets(Int32, task, D, DyctionaryIS, Dictionary2, TO, Тестовый, StringBuilder, array);
this.ResultExecute.push(new ResTest("Количество элементов в хранилище ", Net.КоличествоЭлементовВХранилище()));
this.ResultExecute.push(new ResTest("Первый удаленный ", Net.FirstDeleted()));
Отличие от C# это
Для получения свойства нужно добавить "_"
res = SB._Capacity;
Для вызова асинхронного метода нужно добавить ключевое слово async
var resAsync = await TO.async.GetStringAsync();
Для вызова дженерик метода, если нельзя вывести типы по параметрам то параметры указываем в массиве
res = TO.ДженерикМетод2([Int32, "System.String"],D, 99, "Hello");
Буду рад критике и предложениям.
Какие примеры лучше дать для статьи.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
F>@Serginio1 — я не очень думаю что тут это вообще кому-то реально интересно. Т.е. я на самом деле предлагаю перенести обсуждения или вопросы на http://magpcss.org/ceforum. Более того — если форум поддерживает русский язык — просто пиши по русски в CefGlue разделе. Я там отвечу. Вообще должен. Возможно тебе прийдется пройти валидацию сначала, но это дело поправимое.
F>Да, там не очень неудобно. Однако, — здесь — это оффтопик, а у нас идут совершенно специфичные разговоры.
F>Кроме того, можешь вместо этого, попросить у меня через PM скайп — и скорее всего мы будем общаться гораздо быстрее минуя эту байду. Тут (в этой целевой аудитории) — нет смысла спрашивать. На magpcss.org — лучше на инглише. Более того — тебе и не надо дёргать меня то в плане дурных вопросов — это и так ясно. Поэтому я сразу сказал — что я на такое пойду. В общем — это из разряда "последнего китайского" — в том смысле — что, не стесняйся. Я давно тебя заочно знаю, — я не очень разделяю твоего фанатизма в плане того, что ты делаешь, (возможно всё ещё не понял что именно) — но поверь — лучше сделай, а потом выложи код или сделай серию блог-постов которые толком всё объяснят — толка от текущих наших и твоих самим с собой переговоров ИМХО всё равно никаких нет.
Написал поддержку событий. Но вот, что мне подумалось. CEF мощная штука. Браузер с неограниченными возможностями расширения за счет использования нативного кода.
Но вот активность на том же форуме не нулевая, но единичная. Статей толковых мало. Можешь посоветуешь русскоязычные ссылки, я их в статью включу.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>Десктоп как рынок жив только для профессионального софта. S>> Корпоративный софт является профессиональным? Я в нем работаю. S>Да.
S>>Поэтому я и вышел с опросом идеи. Но кстати есть кроссплатформенная замена COM XPCOM S>Которая просто костыль для портирования аддонов? У меня плохие новости.
S>>Но я не пойму в чем идея то плоха? S>Сама идея — замечательная, но только пока мы обсуждаем в духе "за всё хорошее и против всего плохого". Как только переходим к реальной жизни, так сразу начинаются реальные проблемы. Поддержка там, перфоманс, простота использования, наличие специалистов на рынке, вот это всё.
Саму разработку можно разделить на 3 части
1. Хранение и вызов методов объектов на стороне .Net через отображение и нативных методов для вызовов событий, асинхронных методо и выделения памяти
2. Маршалинг между JS и NET на стороне CEF на С++
3. Proxy на стороне JS
По первому пункту. То это по сути реализация метода Type InvokeMember
Просто я добавил поддержку методов с параметрами массивами, параметрами по умолчанию, авто вывод типа для дженерик методов,
поиск методов расширений.
В этом заинтересована сама MS. У них есть нехилая библиотека для использования классов .Net из натива основанная правда на COM.
Я думаю, что это в их интересах развить эту идею.
Что касается CEF то тут мне пришлось вспоминать и учить C++. Но даже моих навыков оказалось достаточно, что бы сделать обертку. Кода там не много.
Покси на стороне JS составляет всего 500 строчек.
Но другое дело, что я никто и звать меня никак. За неделю разработку скачали всего 5 человек.
и солнце б утром не вставало, когда бы не было меня