Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Как правильно “разделять” приложение на слои?

#c_sharp #архитектура #проектирование #инспекция_кода


В своих приложениях я использую подход разделение функционала на слои, слои я стараюсь
группировать на основании функциональности


Data Access Object
Data Access Layer
Business Logic Layer
GUI


Для работы с бд я использую entity-framework. Довольно часто я стал ловить себя на
мысли что я не понимаю/не уверен на каком из слоев должна быть реализована та или иная
функциональность.

Например: необходимо получить список объектов из бд, пусть класс будет называться
Request, тогда я делаю так:

В DAL у меня есть generic репозиторий() c реализацией CRUD и торчащим наружу свойством
protected virtual IQueryable Entities{...}

В BLL я обращаюсь к свойству Entities вытягиваю из бд необходимые мне данные, материализую(ToList(),Single()),
преобразую в DTO объекты которые отдаю в GUI.

В целом меня такой подход устраивал всем, базовые методы(CRUD) реализованы в одном
классе реализующим generic репозиторий, методы для работы с конкретными сущностями
реализованы в слое BLL.

Но тут наступил переломный момент: мне понадобилось использовать хранимые процедуры/функции.

Для поддержания чистоты приложения доступ к хранимкам хочу(мне так кажется правильней)
сделать в слое DAL я это представляю как то так :

Сделать IRequestRepository в который в качестве зависимости передать generic репозиторий
плюс реализовать необходимые методы/функции, например такие как обращение и получения
результата хранимой функции, так же мне будет необходимо выставить наружу в generic
репозитории DbContext что бы можно было обращаться к хранимкам(_context.Database.SqlQuery(...)),
дальше же все остается так как и было.

как альтернатива отказаться от generic репозитория и для каждого необходимого класса
из DAO реализовать класс репозитория, но в этом случае в каждом классе будет дублирование
CRUD методов, что мне кажется не правильным.



Пример реализации:

public interface IRepository where T: class
{

    T GetById(object id);
    void Insert(T entity);
    void Insert(IEnumerable entities);
    void Update(T entity);
    void Update(IEnumerable entities);
    void Delete(T entity);
    void Delete(IEnumerable entities);
    IQueryable Table { get; }
    DbContext Context { get; }
}

public interface IRequestRepository
{
    Request GetById(object id);

    void Insert(Request request);
    void Insert(IEnumerable requests);
    void Update(Request request);
    void Update(IEnumerable requests);
    void Delete(Request request);
    void Delete(IEnumerable requests);
}

public class RequestRepository: IRequestRepository
{
    private readonly IRepository _requestRepository;

    public RequestRepository(IRepository requestRepository)
    {
        this._requestRepository = requestRepository;
    }

    #region Методы обертки над методами из IRepository
    Request GetById(object id) {...}
    void Insert(Request request) {...}
    void Insert(IEnumerable requests) {...}
    void Update(Request request) {...}
    void Update(IEnumerable requests) {...}
    void Delete(Request request) {...}
    void Delete(IEnumerable requests) {...}
    #endregion

    #region Методы обертки над хранимыми функциями/процедурами
    IEnumerable GetListOfActiveRequests()
    {
        var _requestList = _requestRepository.Context.Database
            .SqlQuery("Select * from dbo.GetListOfActiveRequests()");
        //...
    }
    #endregion

    //Прочие методы

}


Как все таки лучше/правильней поступить в описанном мной случае?



UPD:

исходя из ответа @PavelMayorov, если я правильно понял структура классов у меня вырисовывается
следующим образом

public interface IRequestRepository
{
    #region CRUD методы
    Request GetById(object id);
    void Insert(Request request);
    void Insert(IEnumerable requests);
    void Update(Request request);
    void Update(IEnumerable requests);
    void Delete(Request request);
    void Delete(IEnumerable requests);
    #endregion  
}

public interface IRequestHistoryRepository
{
    #region CRUD методы
    #endregion
}

public class RequestRepository: IRequestRepository
{
    private readonly DbContext _context;

    public RequestRepository(DbContext context)
    {
        this._context=context;
    }

    // Реализация необходимых методов
    // для работы с "сущностью"
}

public class RequestHistoryRepository: IRequestHistoryRepository
{
    private readonly DbContext _context;

    public RequestHistoryRepository(DbContext context)
    {
        this._context=context;
    }

    // Реализация необходимых методов
    // для работы с "сущностью"
}


вот только в этом случае будет дублирование в каждом таком репозитории кода CRUD 

try
{
    if (request == null)
        throw new ArgumentNullException("entity");

    //Создание/Чтение/Редактирование/Удаление
}
catch (DbEntityValidationException dbEx)
{
    var msg = string.Empty;

    foreach (var validationErrors in dbEx.EntityValidationErrors)
        foreach (var validationError in validationErrors.ValidationErrors)
            msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName,
validationError.ErrorMessage) + Environment.NewLine;

    var fail = new Exception(msg, dbEx);
    //Debug.WriteLine(fail.Message, fail);
    throw fail;
}

    


Ответы

Ответ 1



Репозиторий со свойством IQueryable Entities - это безусловно лишняя сущность - EF уже сама по себе предоставляет неплохую реализацию репозитория (DbSet). Особенно лишними являются generic-репозитории. Вообще говоря, в простых программах слоем DAL можно считать саму библиотеку EF; делать поверх новый слой - странное занятие. Если ваша программа достаточно простая - то просто выкиньте из нее все репозитории и увидите как все стало проще :) Основная проблема слоев DAL в типичных программах - потеря инкапсуляции, схема хранения напрямую связана с внешним интерфейсом из-за использования типов модели хранения в интерфейсах. Если вам действительно нужен отдельный слой DAL - предлагаю вам выкинуть из его интерфейсов все упоминания моделей EF. Хранение данных должно быть полностью скрыто в слое DAL, никаких классов-сущностей или IQueryable<> наружу торчать не должно. Код уровня бизнес-логики не должен знать, простой ли запрос идет к базе - или же вызывается хранимая процедура. Что же до повторяющегося кода - есть стандартные способы борьбы с ним: методы-хелперы, базовые классы, AOP.

Ответ 2



Не существует идеального готового архитектурного решения на все случаи жизни. Мне неизвестны программисты, которые с самого начала своей карьеры, реализовывали бы хорошие и удобные слоения приложений. Поскольку речь идет об объектно-ориентированном языке программирования C#, то я бы предложил автору вопроса хотя бы в первом приближении ознакомиться с соответствующими приемами и шаблонами проектирования и разработки и попытаться взять их на вооружение. Классический учебник на эту тему в русском переводе называется "Приемы объектно-ориентированного проектирования. Паттерны проектирования", авторы Э.Гамма, Р.Хелм, Р.Джонсон, Д.Влиссидес. На самом деле на эту же тему и именно о C# есть более читабельная книга. Авторы Р.Мартин и М.Мартин, а называется она "Принципы, паттерны и методики гибкой разработки на языке C#". Сразу скажу что прочтение вышеозначенной литературы не сделает никого мгновенно готовым архитектором.

Высокая загрузка процессора системой в Linux. Как узнать почему?

#linux #производительность #cpu



(источник: joxi.ru)

(источник: joxi.ru)    

Иногда имеем высокую загрузку процессора некими системными задачами.
Не процессами из userland, а именно "система" грузит.
Т.е. явно выполняются какие-то системные вызовы (выделение памяти, переключения контекста),
или работают драйверы (обрабатывают прерывания или что-то еще), идёт активный ввод-вывод. 

Это всё я всегда предполагаю, 
Но как узнать ТОЧНО, почему высокая загрузка - не представляю. Поэтому прошу помощи.  

Сейчас я использую несколько косвенных методов, но они не всегда подходят: глянуть
в iotop, прибвать процессы по одному, и смотреть не спала ли нагрузка.  

Но иногда просто нельзя останавливать сервисы. А иногда и процессов работающих уже
почти не осталось, а нагрузка всё равно есть. 

Вот хочется найти какое-нибудь средство быстро и точно узнавать что же грузит процессор.
    


Ответы

Ответ 1



Вам надо сходить сюда, можете найти русский перевод или похожие статьи. Поможет вам ограничить выборочно потребление CPU процессами почитать про strace и подобные ему sudo strace -t -e trace=open,connect,accept unity сможете увидеть много интересного для ядра - ftrace или поищите еще kernel tracer-ов утилиты, которая дает понять это с одного взгляда я не знаю, если вы не нагуглите, я бы пошел следующим способом: настроить мониторинг процессов так, чтобы в случае возникновения нагрузки на K% на N секунд каким-либо процессом, он давал алерт. Можно наскриптовать так, чтобы при возникновении алерта, мониторинг натравливал trace на этот процесс, на секунду, допустим, и сохранял бы список самых часто выполняемых / долгих функций. Но тут нужно быть осторожным, чтобы не повалить систему и не заполнить hdd. Т.е. скриптинг должен учитывать, что необязательно ставить trace на процесс, который уже был под трейсом (т.е. для которого уже сохранен tracefile), иначе процессы начнут тормозить еще больше, к примеру. Нельзя трейсить слишком долго - гигабайтные дампы вам не нужны. Если вы решаете конкретную задачу борьбы с DDoS - ну, или очень много денег и очень много дц (повезло, если у вас есть), или cloudflare - я бы так пошел для начала. Т.е. тут все от задач зависит, дебажить драйвер ядра - один подход, защищаться от ddos - другой.

Ответ 2



Красная полоска говорит о io-wait или о прерываниях. Это работа драйверов и ядра. Откройте обычный top. Там будет строчка: %Cpu(s): 15,3 us, 1,4 sy, 0,0 ni, 78,8 id, 3,4 wa, 0,8 hi, 0,3 si, 0,0 st Если большое число перед wa - устройство ввода вывода работает медленней чем планировщик. Чаще всего это запись на мертвый жесткий диск, но также может проявляться при работе видеокарты (майнинг на gpu). Запись на сетевой диск. hi и si это прерывания. Часто встречал при сгоревшей сетевой карте. Дальше смотрим процессы в топе. Там или в квадратных скобках указан драйвер или без них. Что-то вроде kworker/u8:7+events_unbound, loop8 и прочее. Название указывает на что это такое и номер экземпляра. Покопавшись в /proc/, /sys/ можно сопоставить процесс с устройством. Стоит обратить внимания ещё и на обычные службы, которые могут хотеть от ядра слишком многого. Например, ускорение шифрования на урезанном процессоре. Но там обычно полоски наполовину зеленые, наполовину красные. Ещё такие фокусы могут быть на виртуалках, когда другая виртуалка кушает ресурсы.

Как получить два набора данных (Таблицы или Reader) из двух выборок SQL?

#c_sharp #cpp #sql #sql_server #oledb


В Microsoft SQL Server Managment Studio большой запрос возвращает все выборки. Как
сделать что б после Command.Execute получить програмно все выбранные таблицы. Т.е. 

create PROCEDURE [dbo].p1  begin
   select 1 select 2,3   -- выборка
end
/*results:
1

2  3*/


Что б дало две таблицы а не одну. Да запрос можно "разделить" на два, но до тех пор
пока нет связи между ними через локальные переменные.  Можно конечно и их передать,
но Studio ловит две три таблицы из выполняемой хранимой  процедуры.

1) Пробовал OleDb - там дополнительных курсоров не получилось получить. Думаю что
поддержка нескольких таблиц заложена в sqlncli.dll. 2) Возможно как-то можно средствами
sql сделать такую выборку, что б сложить таблицы в таблицу (курсорное поле), или например
с помощью sp_msforeachtable.
    


Ответы

Ответ 1



Попробуйте поработать с объектом ADO Recordset для С++. Пример работы с Recordset в VBA: Dim rsTree As ADODB.Recordset Dim rsTreeGrp As ADODB.Recordset Dim rsGrp As ADODB.Recordset Set rsTree = ExecuteRS("usp_pct_CatList", 2, 0, "") Set rsTreeGrp = rsTree.NextRecordset() Set rsGrp = rsTreeGrp.NextRecordset() Процедура usp_pct_CatList возвращает три выборки.

В чём заключается отличие таких способов взаимодействия приложений, как шлюз и шина?

#архитектура #интеграция


Читала статью про способы взаимодействия приложений. И что мне осталось совершенно
непонятным, это как различаются топологии интеграционного решения "шлюз" и "шина":



Буду благодарна, если кто-нибудь пояснит разницу между ними.
    


Ответы

Ответ 1



В статье "немного" ошиблись с определениями, отсюда и непонятки. Дело в том, что та статья написана, по сути, про одно конкретное решение - и все остальное приведено только для общего сведения. Интеграционная шина - это прежде всего среда передачи сообщений, позволяющая доставлять сообщения на основе логических признаков. "На пальцах", это означает, что в самих сообщениях не содержатся никакие физические адреса. Простейшие варианты интеграционных шин держат у себя справочник соответствия логических и физических адресов. Логические адреса формируются исходя из назначения приложения, его названия или оргструктуры. Этого достаточно чтобы называться шиной :) Более сложные варианты дают возможность доставки сообщений по системе pub/sub (публикатор - подписчик). Интеграционная шина может быть построена вокруг центрального узла (или нескольких узлов), где "крутится" маршрутизатор (см. далее) - или же быть распределенной, существуя в виде кучи адаптеров (см. далее) на стороне каждого приложения с общей базой адресов. Маршрутизатор сообщений - это приложение, смысл существования которого - передавать сообщения от одного приложения к другому. Обычно маршрутизатор сообщений можно найти внутри интеграционный шины - но не каждый маршрутизатор образует вокруг себя шину. Как минимум, нужна работа с логическими адресами. Также маршрутизаторы сообщений иногда реализуют другие инфраструктурные сервисы, вроде гарантированной доставки, ведения журнала сообщений или преобразования форматов. Интеграционный шлюз - это маршрутизатор сообщений, связи которого можно поделить на "внешние" и "внутренние". Эти связи могут работать по одному и тому же протоколу - в таком случае смысл шлюза в разбиении общей шины на сегменты с целью добавления отказоустойчивости или защищенности. К примеру, при наличии филиалов в разных городах, в каждый можно "поставить" шлюз, который будет передавать сообщения в головной офис по шифрованному каналу. В таком случае общая интеграционная шина окажется разбита на сегменты, которые могут продолжать ограниченно функционировать при падении интернет-канала. Кроме того, интеграционный шлюз может соединять приложения, работающие по разным протоколам - в таком случае он будет еще и адаптером. Адаптер - это маршрутизатор сообщений, разные связи которого работают по разным протоколам. Брокер - это маршрутизатор сообщений повышенной крутизны сложности. К примеру, он может работать с децентрализованной шиной. Или выполнять хитрые трансформации, выступая универсальным центральным адаптером. Как можно заметить, ни один терминов не исключает другие. Вполне можно себе представить счастье архитектора-перфекциониста в виде мега-интеграционной шины, построенной на маршрутизаторе сообщений, который является брокером, шлюзом и адаптером. Он будет уметь работать по любому протоколу, делать SQL-запросы, проводить XSLT-трансформации - и станет кошмарным сном любого разработчика :) Однажды я написал свой интеграционный шлюз просто потому что КРОК 2 месяца отказывался предоставлять нашей подсистеме второй адрес на своей интеграционной шине... А отсюда есть еще один вывод: любую достаточно сложную программу можно обозвать любым словом. Поэтому, чтобы коллеги вас понимали - старайтесь называть ее теми словами, которые используются в официальном названии программы. К примеру, WebSphere Message Broker имеет в своем названии слово "Брокер" (и заслуженно носит его за свою непомерную сложность крутизну) - поэтому и называть его надо брокером, а не маршрутизатором сообщений, адаптером, шлюзом или шиной. С другой сторону, если на основе WebSphere Message Broker был сделан свой проект по работе со СМЭВ, который был назван "шлюз для работы со СМЭВ" - то и называть его надо шлюзом, несмотря на то, что он является еще и адаптером и маршрутизатором сообщений. Или, к примеру, сама СМЭВ по сути является простейшей интеграционной шиной, а внутрях крутится на WebSphere Message Broker (я видел типичные для него сообщения об ошибках) - но называть ее надо СМЭВ, а то не поймут.

Как сделать только один коммит из локальной ветки в remote

#git #git_rebase


Есть удаленный репозиторий

x1->x2->x3
          \master


Я сделал его форк и локально новую ветку

          /master
x1->x2->x3
          \y1->y2->y3
                     \local-develop


Как мне теперь сделать push-request с последним коммитом так, что бы не были видно
всей истории коммитов?

              /master
x1->x2->x3->y3
          \y1->y2->y3
                     \local-develop


Цель в том, что бы добавить результат работы в удаленный репозиторий в отдельную
ветку, при этом не захламляя удаленный репозиторий лишними кометами с историей разработки.

Я пробовал сделать так

git checkout local-develop
git rebase -i master
*squash all commit*


Но получается что они сбиваются в одни в local-develop ветке.

Заранее спасибо.
    


Ответы

Ответ 1



Это так не работает. Проблема в том, что коммит, семантически, это набор изменений, а не состояние. Поэтому чтобы отправить последнее состояние, нужно отправить набор изменений от начального (x3, не включительно) до конечного (y3). И раз вы хотите представить этот набор одним коммитом, то вам придётся сделать один коммит, содержащий в себе изменения y1..y3. С помощью rebase -i вы это и сделали. Если при этом вы не хотите терять исходные коммиты, можно "сплющивание" (squash) сделать в отдельной ветке (просто перед началом ребейза сделав git checkout -b local-develop-squashed, находясь в ветке local-develop). После ребейза получится такое состояние: x1->x2->x3-\ {master} | |->y1->y2->y3 {local-develop} | \->z1 {local-develop-squashed} В ветке local-develop-squashed по сравнению с master выходит один красивый коммит. Из этой ветки и нужно делать pull request. У этого, конечно, есть проблемы. Если вы захотите этот pull request доделать, вам придётся делать push --force каждый раз, что в общем случае очень опасно. Поэтому "на посмотреть" можно загрузить и исходную историю из кучи коммитов, и только когда дадут "добро", сделать из кучи коммитов один "прямо на месте" и сделать один раз push --force. А ещё некоторые пользуются при мердже PR опцией --no-ff, а историю смотрят с помощью git log --first-parent master, в результате чего детальная история фича-веток не показывается, а PR выглядят, как отдельные коммиты. И для этого не нужно никакое шаманство с перезаписью коммитов и пушем "силой".

Что делать, когда при обработке события WinForms контрола нужные данные еще не обновились?

#c_sharp #winforms #datagridview #события


Конечно, напрашивается ответ: "используй другое событие", но, к сожалению, я не нашёл
подходящий event.
Живой пример из живой программы: есть таблица настроек, у которой есть колонка типа
checkbox. Требуется, чтобы при изменении состояния чекбокса, кнопка Save на форме Enabled
= true;
Проблема заключается в том, что когда вы нажимаете мышкой на checkbox, событие DataGridView.CellValueChanged
не вызывается, пока ячейка не потеряет фокус.  Cell.Value ещё хранит прежнее значение
и никаких событий обновления данных не возникает. Новое значение хранится в свойстве
Cell.EditedFormattedValue, но оно появляется там только после выполнения всех событий,
которые я тестировал.
Напросилось такое решение:

private void gridSettings_CellClick(object sender, DataGridViewCellEventArgs e)
{
    var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(() =>
    {
        Thread.Sleep(200);
    })
    .ContinueWith(result =>
    {
        if (gridSettings.Columns[e.ColumnIndex].Name ==
            "ColumnIsMandatory")
        {
            var clickedCell = gridSettings.Rows[e.RowIndex].Cells[e.ColumnIndex];
            object editedValue = clickedCell.EditedFormattedValue;
            object oldValue = clickedCell.Value;
            if (oldValue != null && editedValue != null && (bool) editedValue !=
(bool) oldValue)
            {
                clickedCell.Value = (bool) editedValue;
                // Событие изменения данных вызвано принудительно
            }
        }
    }, uiScheduler);
}


Но оно обладает недостатками в виде запуска Task и отфонарного Thread.Sleep(200);
который не гарантирует, что данные уже обновились. Да и решение топорное, совсем не
изящное.

Как бы вы решали такую проблему?

UPDATE:
Работающий пример:

public partial class Form1 : Form
{
    private DataGridView gridSettings;
    private Button buttonSave;

    public Form1()
    {
        InitializeComponent();

        gridSettings = new DataGridView()
        {
            Location = new Point(0,0),
            Size = new Size(200, 200)
        };
        var column = new DataGridViewCheckBoxColumn()
        {
            Name = "ColumnIsMandatory"
        };
        gridSettings.Columns.Add(column);
        gridSettings.Rows.Add(1);
        buttonSave = new Button {Text = "Save", Location = new Point(210, 210), Enabled
= false};
        gridSettings.CellValueChanged += (sender, args) => buttonSave.Enabled = true;
        gridSettings.CellClick += gridSettings_CellClick;
        this.Controls.AddRange(new Control[]{gridSettings, buttonSave});
    }

    private void gridSettings_CellClick(object sender, DataGridViewCellEventArgs e)
    {
        var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        Task.Factory.StartNew(() =>
        {
            Thread.Sleep(200);
        })
        .ContinueWith(result =>
        {
            if (gridSettings.Columns[e.ColumnIndex].Name ==
                "ColumnIsMandatory")
            {
                var clickedCell = gridSettings.Rows[e.RowIndex].Cells[e.ColumnIndex];
                object editedValue = clickedCell.EditedFormattedValue;
                object oldValue = clickedCell.Value;
                if (oldValue != null && editedValue != null && (bool)editedValue
!= (bool)oldValue)
                {
                    clickedCell.Value = (bool)editedValue;
                    // Событие изменения данных вызвано принудительно
                }
                else if (oldValue == null && editedValue != null)
                {
                    clickedCell.Value = (bool)editedValue;
                }
            }
        }, uiScheduler);
    }
}

    


Ответы

Ответ 1



Если я не ошибаюсь, вам способ прекрасно работает, даже если из обработчика CellClick выкинуть создание тасков. То есть достаточно оставить оператор if. Тем не менее, событие, которое вам нужно - CurrentCellDirtyStateChanged. gridSettings.CurrentCellDirtyStateChanged += GridSettings_CurrentCellDirtyStateChanged; private void GridSettings_CurrentCellDirtyStateChanged(object sender, EventArgs e) { buttonSave.Enabled = true; }

Максимально быстрое сравнение 2х скриншотов

#c_sharp #vbnet


Есть три скриншота. на них короткий текст с кодом типа X123XX123.


белый текст на темном фоне (без искажений)
красный текст на темном фоне (без искажений, текст совпадает с 1.)
белый текст на том же фоне что и всегда, но код другой. (без
искажений)


Какой способ сравнения выбрать для быстрого сравнения вариантов 1-2 и 2-3?


1-2 Необходимо удостовериться что текст тот же но он стал красным.
2-3 Удостовериться что текст стал другим.



  Скриншоты размером около 150х80px.

    


Ответы

Ответ 1



Можно попробовать так static bool Equality(Bitmap Bmp1, Bitmap Bmp2) { var pixelTrue = 0.0; var pixelFalse = 0.0; if (Bmp1.Size == Bmp2.Size) { for (int i = 0; i < Bmp1.Width; i++) for (int j = 0; j < Bmp1.Height; j++) { var pixel1 = Bmp1.GetPixel(i, j); var pixel2 = Bmp2.GetPixel(i, j); if (pixel1 != pixel2) pixelFalse++; else pixelTrue++; } } else return false; var percentResult = (pixelTrue/(pixelTrue + pixelFalse))*100; return percentResult >= 97; } Хотя шаг можно и увеличить!

var React = require(“react”); Почему не работает?

#reactjs


Бьюсь над проблемой уже третий день, перерыл весь интернет на русском и на английском
(как смог). Ответа не нашёл. Прошу вашей помощи.

Суть проблемы: установил через npm React и ReactDOM. В node_modules они добавились.
Подключаю. Не видит. Пишет в консоли "React is undefined". А если подключаю через cdn,
то все работает.

Что в коде:

var React = require("react");
var ReactDOM = require("react-dom");

var Note = React.createClass({
    render() {
        return 
Me
} }); ReactDOM.render( , document.getElementById("app") );


Ответы

Ответ 1



Возможно вы используете ECMAScript6. Попробуйте вместо: var React = require("react"); var ReactDOM = require("react-dom"); Написать import React from 'react'; import ReactDOM from 'react-dom';

Ответ 2



СreateClass это устаревший способ объявления компонентов. В вашем случае объявить компонент можно так import React from 'react'; import ReactDOM from 'react-dom'; class Note extends Component { render() { return ( Hello! ) } } Чтобы создать структуру проекта на Реакт рекомендую воспользоваться модулем create-react-app, очень удобная штука.

Ответ 3



Вот пример реализации стандартного компонента в React. App.js import React, { Component } from 'react'; export default class App extends Component { render() { return (

Hello world!

); } } Index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; // Указываете путь до компонента App ReactDOM.render(, document.getElementById('root')); // 1) Указываем какой компонент будет стартовым 2) Контейнер

Ответ 4



еще проблема "React is undefined" возникает тогда, когда библиотека установлена, но не добавлена в package.json проверьте в этом файле "dependencies:" если нет, то устанавливайте модули командой: npm i react --save

Ответ 5



еще так можно import * as React from 'react';

Как отловить событие запуска любого приложения?

#java #android #android_service


Задача: нужно при запуске любого приложения выводить Toast с текстом "такое-то приложение
запустилось", а когда приложение закрывается (через finish(), или кнопкой Назад — без
разницы), вне зависимости от того, работают ли Service этого приложения, или нет, —
опять же, показать Toast с текстом "такое-то приложение завершилось". 

Например, запустил Play Market — получил Toast: "Play маркет запутился".

Как это реализовать? Кажется нужно использовать Service, а дальше никаких мыслей.
    


Ответы

Ответ 1



Можно попробовать использовать AccessibilityService и отлавливать событие TYPE_WINDOW_STATE_CHANGED public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { switch (accessibilityEvent.getEventType()) { case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: if (accessibilityEvent.getPackageName() != null) { showToast("App "+accessibilityEvent.getPackageName()+" window state changed"); } break; } }

Ответ 2



По хорошему это не очень то и возможно, правда есть определенные хаки, с помощью которых можно отловить событие запуска приложения. В частности, в момент запуска приложения ОС выставляет логи, примерно следующего содержания: 05-17 13:55:30.586: I/ActivityManager(526): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.rovio.angrybirds/com.rovio.fusion.App} from pid 818 То есть вам надо ловить в логах запуск Intent'а android.intent.action.MAIN и извлечь пакет (как в данном случае com.rovio.angrybirds) Для того, чтобы читать логи вам нужно иметь пермишен и в сервисе читать логи LogCat: Process logcatProc = Runtime.getRuntime().exec(new String[]{"logcat", "-d"}); reader = new BufferedReader(new InputStreamReader(logcatProc.getInputStream())); Дальше уже парсить полученный StreamReader Как только вы отловите название стартовавшего пакета, то по имени пакета получить имя проги уже совсем просто: final String packageName = "my.application.package" PackageManager packageManager= getApplicationContext().getPackageManager(); String appName = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA));

Ответ 3



Нужно переопределить метод finish(), и в нем вызывать Toast и вызывать finish() суперкласса. Либо в методе onFinish вывести Toast.

Запуск с правами администратора программы написанной с помощью Code::Blocks

#windows #mingw #codeblocks


Для разработки использую Code::Blocks MinGW под Windows. Моей программе требуются
права администратора. 

Как программно запросить эти права у пользователя? 

Прочитал что для этого нужен манифест, куда можно прописать обязательный запуск с
правами администратора. Куда нужно его добавить и как использовать?
    


Ответы

Ответ 1



xXx а в код добавлять как-то так: С++IDR_XPMANIFEST MANIFEST "xpmanifest.xml" Для семёрки манифест следующий:

Ответ 2



Code::Block тут абсолютно непричём. Можете писать программу хоть в блокноте. ПОЛЬЗОВАТЕЛЬ никогда, ни при каких обстоятельствах, не может кому-то дать права администратора. Вы не указали самое главное обстоятельство - в какой именно ОС собираетесь это сделать? Ведь решение проблемы целиком определяется используемой ОС Судя по тому, что Вы упомянули MinGW, то речь идёт о Windows. Cразу скажу, что для этого варианта я не знаю решения. Если же речь идёт о *NIX системах, то там нужно действовать следующим образом: Обычный пользователь создаёт приложение Root объявляет испольнимый файл этого приложения своей собственностью. Root взводит бит смены ID пользователя у этого файла. Тогда, при запуске этого приложения рядовым пользователем, произойдёт установка действующего ID по ID хозяина файла. А хозяином файла является root. Таким образом, в рамках данного приложения, рядовой пользователь получает права root. Более подробно можете почитать тут: http://citforum.ru/operating_systems/unix/glava_15.shtml

Можно ли в Java сделать метод внутри метода?

#java


Как сделать чтобы в методе был ещё один метод?
    


Ответы

Ответ 1



Можно: Сделать внутри метода безымянную реализацию интерфейса и использовать её метод. Как в прошлом пункте, только воспользоваться для этого лямбда-выражением.

Как интерпретировать понятие “высоконагруженное Android приложение”?

#android #highload #задание_на_собеседовании


На одном собеседовании на позицию Android developer мне был задан вопрос: "Имею ли
я опыт создания высоконагруженных Android приложений." (Жаль, что мысль уточнить определение
этого понятия мне пришла уже после собеседования)

Разработкой приложений для Android я занимаюсь уже несколько лет. Так получилось,
что проекты с которыми я работаю(л) не имели большого успеха в плане количества пользователей.
Исходя из этого, на вышеупомянутый вопрос я ответил, что такого опыта не имел, все
проекты доростали в лучшем случае до нескольки тысяч пользователей. (В данном случае,
я интерпретировал highload как количество активных пользователей)

Ранее я не встречал применение понятия "высоконагруженный" (highload) в контексте
Android приложений. И сейчас, задавшись вопросом я вижу, что это понятие применяется
по отношению к бекенду и сайтам.

После проведения аналогий с highload в вебе, я бы сказал, что highload Android app
- это приложение архитектура которого будет легко поддерживать внедрение новых функций,
оптимально расходовать ресурсы Android девайса. Но разве не каждое приложение должно
разрабатываться с учетом этих требований?

Хочу поинтересоваться у других специалистов, как вы понимаете "высоконагруженный"
(hihgload) по отношению к Android приложению?
    


Ответы

Ответ 1



Сложно сказать, что именно имели в виду на собеседовании, но я могу предположить следующие ситуации: Собеседование проводил нетехнический специалист, у которого был опросник (или опыт) для собеседования бекенд-специалистов, который на лету как-то адаптировал вопросы, а в ответах надеялся услышать уверенный ответ (не важно какой) Собеседование проводил грамотный интервьюер, вопрос задавался с целью проверить уверенность знаний или ввести в некий стресс. Ожидаемым ответом на такие вопросы являются уточняющие вопросы или рассказ о том, что вопрошающий спрашивает чушь. Собеседник разговаривает на каком-то внутреннем диалекте, известном только ему или его окружению. В этом случае он хотел узнать что-то конкретное, но вот что именно - остается только гадать. И если с первыми 2 случаями все относительно понятно, то в последнем варианте можно подумать о: Отзывчивости и плавности приложения, особенно когда оно обвешано сторонними модулями и библиотеками. Мне порой попадались приложения, которые на топовом железе прошлого года выдавали ну просто сверх-тормозящие списочки. Впрочем, гигагерцы и ядра только растут, а списочки все равно всегда тормозят. Оптимизации сетевого обмена с сервером. Когда сервер задыхается от миллионов запросов, то тут надо бы подумать, как сделать работу сервера попроще. К примеру, отказаться от XML/JSON и других человекочитаемых протоколов, как бы ввести несколько серверов, как бы снизить число запросов так, чтобы и функциональность не пострадала, и при этом сервер без лишней надобности не трогать. Надо сказать, что это в общем-то не работа Андроид-девелопера, но порой больше этим некому заниматься. Вопрос мог означать, умеете ли вы работать с байтиками или ума хватает только на парсинг XML и голые запросы по http. Иногда надо работать с большими объемами данных. К примеру, отображать карту с миллионами объектов на ней одновременно. Да, это возможно, но если все приложение написать на сишечке, а не использовать гугломапсы, как это делают все. Есть ряд приложений, где отзывчивость критически важна и которые потребляют максимум ресурсов. К примеру, Angry Birds делает внутри себя какую-то магию, что мой планшет становится горячим примерно за 10 минут игры. Я не знаю как они этого добились, но я верю, что это пример высоконагруженного приложения Майнинг биткоинов, подсчет прогноза погоды, рисование чего-то поверх фотографий на основе нейросетей. Возможно, в скрытом режиме. Наверное злые птицы чем-то таким в фоне и занимаются. Думаю, список можно продолжать бесконечно. Если кто-то напишет другие варианты, то будет круто. А вообще, порог входа в профессию "погромизд на андроид" настолько низок, что достаточно понимать как правильно копипастить код со стековерфлоу и клеить свои приложения из копипасты. АПИ меняется постоянно, равно как и практики, равно как и требования. Я совершенно не понимаю, зачем проводят такие собеседования.

Как указать пространство имен по-умолчанию в шаблоне проекта Visual Studio?

#c_sharp #visual_studio #шаблоны


Имеется проект с пространством имен по умолчанию Root.ProjectName. Впоследствии проект
экспортируется как шаблон с предварительным переименованием пространства имен ProjectName
во всех файлах проекта в $safeprojectname$. Проблема в том, что после создания проекта,
основанного на этом шаблоне, выкидывается исключение MissingManifestResourceException,
а пространство имен по умолчанию меняется с Root.ProjectName на ProjectName, где ProjectName
- это указанное пользователем имя проекта при создании.

Конечно, после ручного переименования все работает, но возникает вопрос: как принудительно
указать в шаблоне, что пространство имен по умолчанию состоит не только из имени проекта?
    


Ответы

Ответ 1



Когда Вы создаёте (экспортируете) шаблон, имя, которое пользователь введёт при создании проекта с использованием Вашего шаблона подставляется вместо переменной $safeprojectname$ во всех файлах проекта, в соответствии с содержимым файла TemplateName.csproj (где TemplateName это имя Вашего шаблона). Вот часть конфигурационного файла: Debug AnyCPU {$guid1$} Library Properties $safeprojectname$ $safeprojectname$ v4.5.2 512 Обратите внимание на содержимое тегов и . Если внутри вашего проекта будут вложенные директории, например, если корневое пространство имён шаблона - Root, а пространство имён внутри шаблона - Foo, соответственно, у Вас имеется класс с именем Root.Foo.SomeClass, то при создании проекта по шаблону, если проект будет назван пользователем MyRootSampleProject, то полное квалифицированное имя класса SomeClass - MyRootSampleProject.Foo.SomeClass, в результате никаких исключений не будет и всё будет компилироваться корректно, ведь это вполне ожидаемое поведение, поскольку создание шаблона предполагает переименование КОРНЕВОГО пространства имён проекта, а за формирование этого имени отвечает сам пользователь, использующий Ваш шаблон. Если же Вы, как пользователь хотите, чтобы перед всеми именами классов в созданном по шаблону проекте был какой-либо префикс, то нужно ввести вместо, скажем, имени MyRootSampleProject что-то вроде SolutionPrefix.MyRootSampleProject. Принудительно указать префикс в шаблоне не представляется возможным, да и трудно представить ситуацию, в которой это действительно будет нужно.

Ответ 2



При создании шаблона проекта, как правило, пространство имен в файлах переименовываются на $safeprojectname$. Но не всё можно заменить непосредственно в IDE. Если используется составное пространство имен (например: Root.ProjectName), то после экспорта шаблона в файле ProjectName.csproj необходимо вручную переименовывать параметр /Project/PropertyGroup/RootNamespace (например на Root.$safeprojectname$). В этом же файле можно переименовать параметры /Project/PropertyGroup/AssemblyName и /Project/PropertyGroup/DocumentationFile. Полный список поддерживаемых параметров шаблона можно посмотреть здесь.

Как определить скорость скачивания в многопоточной программе?

#c_sharp #download #benchmark


Был простой код скачивания:

internal static async Task DownloadFile(Uri uri)
{
  byte[] result;
  WebResponse response;
  var file = new ImageFile();
  var request = WebRequest.Create(uri);

  try
  {
    response = await request.GetResponseAsync();
    using (var ms = new MemoryStream())
    {
      response.GetResponseStream().CopyTo(ms);
      result = ms.ToArray();
    }
  }
  catch (System.Exception ex) { }
  if (response.ContentLength == result.LongLength)
    file.Body = result;
  return file;
}


Захотелось добавить показатель скорости скачивания. Гугл подсказал, что можно ориентироваться
на скорость чтения потока:

result = await CopyTo(response.GetResponseStream(), response.ContentLength, progressChanged);

private static async Task CopyTo(Stream from, long totalBytes, Action
loadedEvent)
{
  var sw = new Stopwatch();
  sw.Start();
  var data = new byte[totalBytes];
  byte[] buffer = new byte[81920];
  int currentIndex = 0;
  while (true)
  {
    int num = await from.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
    if (num != 0)
    {
      Array.Copy(buffer, 0, data, currentIndex, num);
      currentIndex += num;
      loadedEvent?.Invoke(new DownloadProgress(currentIndex, totalBytes, sw.ElapsedMilliseconds));
    }
    else
      break;
  }
  sw.Stop();
  return data;
}


Для хранения информации (и прокидывания наверх), завел простую структуру:

public struct DownloadProgress
{
  public readonly long BytesReceived;

  public readonly long TotalBytesToReceive;

  public readonly long TimeMs;

  public double GetSpeed()
  {
    var seconds = TimeMs / 1000.0;
    if (seconds > 0)
      return BytesReceived / seconds;
    return 0;
  }

  public DownloadProgress(long received, long total, long time)
  {
    this.BytesReceived = received;
    this.TotalBytesToReceive = total;
    this.TimeMs = time;
  }
}


Итого, если качать в один поток, то GetSpeed в любой момент времени (кроме первой
секунды где то) показывает реальную скорость.

Класс, который качает общую цифру в итоге хранит у себя:

    this.Speed = 0;
    var file = await ImageFile.DownloadFile(this.ImageLink, dp => this.Speed = dp.GetSpeed());
    this.Speed = 0;


Дальше я думал осталось самое легкое - просто на верхнем уровне сложил все скорости
и всё:

return this.ActivePages != null && this.ActivePages.Any() ? 
  this.ActivePages.Sum(p => p.Speed) : 0;


На деле, получилось очень неприятное поведение:


Большую часть времени скорость действительно отображается корректная.
Скорость часто скачет, причем разброс иногда превосходит ширину канала. При канале
в 650кб\сек цифры скачут от 300кб\сек до 5-8мб\сек.


Если скорость ниже вполне может быть при окончании одной загрузки и начале следующей,
то вот превышение ширины канала очевидно невозможно технически.
Возможно я где то округление пропустил, или Stopwatch недостаточно точен для таких
расчетов?

UPD: таки первое подозрение оправдалось - начальный скачок скорости портил общую
статистику. Если метод подсчета скорости сделать вот таким:

  public double GetSpeed()
  {
    var seconds = TimeMs / 1000.0;
    if (seconds > 0.1)
      return BytesReceived / seconds;
    return 0;
  }


То скорость уже в целом намного адекватнее, выше ширины канала изредка скачки ещё
бывают, но уже не больше 5%. Можно увеличить игнорируемое время, тогда скачков не будет
совсем.

Осталась проблема с общим показателем. Если для одного потока цифра была достоверной,
то с многопоточным скачиванием цифра часто врёт, средняя от показателя получается ниже
реальной (общее время на общий объем).

UPD2: минимизировал все расчеты, убрал структуру, вынес логику в статический класс:

  public class NetworkSpeed
  {
    public static double TotalSpeed { get { return totalSpeed; } }

    private static double totalSpeed = 0;

    private const uint Seconds = 3;

    private const uint TimerInterval = 1000;

    private static Timer speedTimer = new Timer(state =>
    {
      var now = 0L;
      while (receivedStorage.Value.Any())
      {
        long added;
        if (receivedStorage.Value.TryDequeue(out added))
        {
          now += added;
        }
      }
      lastSpeeds.Value.Enqueue(now);
      totalSpeed = lastSpeeds.Value.Average();
    }, null, 0, TimerInterval);

    private static Lazy> lastSpeeds = new Lazy>(()
=> new LimitedConcurrentQueue(Seconds));

    private static Lazy> receivedStorage = new Lazy>();

    public static void Clear()
    {
      while (receivedStorage.Value.Count > 0)
      {
        long dd;
        receivedStorage.Value.TryDequeue(out dd);
      }
      while (lastSpeeds.Value.Count > 0)
      {
        double dd;
        lastSpeeds.Value.TryDequeue(out dd);
      }
    }

    public static void AddInfo(long received)
    {
      receivedStorage.Value.Enqueue(received);
    }

    private class LimitedConcurrentQueue : ConcurrentQueue
    {
      public uint Limit { get; }

      public new void Enqueue(T item)
      {
        while (Count >= Limit)
        {
          T deleted;
          TryDequeue(out deleted);
        }
        base.Enqueue(item);
      }

      public LimitedConcurrentQueue(uint limit)
      {
        Limit = limit;
      }
    }
  }


В итоге, при скачивании достаточно сообщать, сколько байт было скачано в очередной
момент:

      NetworkSpeed.AddInfo(num);


И всё, показатель NetworkSpeed.TotalSpeed будет отображать среднюю за последние 3
секунды скорость. Средний показатель в целом стал более-менее стабильным, правда немного
завышает показатели на моих данных. Ну и очевидно, что если тредпул будет перегружен,
то таймер вовремя не отработает и скорость начнёт "скакать".
    


Ответы

Ответ 1



Публикую решение, которое меня устроило. Точность итоговой цифры 95-99%. public class NetworkSpeed { public static double TotalSpeed { get { return totalSpeed; } } private static double totalSpeed = 0; private const uint Seconds = 3; private const uint TimerInterval = 1000; private static Timer speedTimer = new Timer(state => { var now = 0L; while (receivedStorage.Value.Any()) { long added; if (receivedStorage.Value.TryDequeue(out added)) { now += added; } } lastSpeeds.Value.Enqueue(now); totalSpeed = lastSpeeds.Value.Average(); }, null, 0, TimerInterval); private static Lazy> lastSpeeds = new Lazy>(() => new LimitedConcurrentQueue(Seconds)); private static Lazy> receivedStorage = new Lazy>(); public static void Clear() { while (receivedStorage.Value.Count > 0) { long dd; receivedStorage.Value.TryDequeue(out dd); } while (lastSpeeds.Value.Count > 0) { double dd; lastSpeeds.Value.TryDequeue(out dd); } } public static void AddInfo(long received) { receivedStorage.Value.Enqueue(received); } private class LimitedConcurrentQueue : ConcurrentQueue { public uint Limit { get; } public new void Enqueue(T item) { while (Count >= Limit) { T deleted; TryDequeue(out deleted); } base.Enqueue(item); } public LimitedConcurrentQueue(uint limit) { Limit = limit; } } } Как этим пользоваться - на верхнем уровне вызываем Clear() когда начинаем или заканчиваем скачивание, чтобы результаты были независимы от других загрузок. В месте, где происходит реальное скачивание - вызываем метод AddInfo, указав сколько байт нам пришло в очередной цикл скачивания. Можно использовать CopyTo из шапки или DownloadProgressChanged у WebClient. Главное передавать именно разницу между предыдущим показателем и текущим. Точность измерений обеспечивается таймером (System.Threading.Timer), а потому, чтобы точность показаний была достоверной, тредпул должен быть свободен для вызова callback. Ну и, понятное дело, результат всех измерений находится в свойстве TotalSpeed. Если хочется, можно добавить событие о его изменении, для своевременного отображения в UI. Частота его обновления специально синхронизирована с таймером - иначе цифра меняется слишком часто и пользователь не понимает, какова скорость.

Что лучше: метод класса или метод расширения

#aspnet_mvc #архитектура


Добрый день.
Я начинающий программист, поэтому могут возникать глупые вопросы.
Делаю проект на ASP.MVC, возник вопрос связанный с архитектурой проекта.
У меня есть класс ViewModel, в котором содержатся данные, с которыми будет работать
контроллер и которые будут выводиться на интернет-страницей. Есть класс ViewModelBuilder,
который преобразует объект базы данных во ViewModel и наоборот.
Мне нужно  принять строку, введенную пользователем, обработать ее, вытянуть недостающие
данные из интернета и сформировать ViewModel.
Возник вопрос, в какой класс написать методы, обрабатывающие входные данные (это
не валидация), вытягивающие их сети недостающие данные  и формирующие ViewModel.

Варианта у меня 3:
- В контроллер
- В ViewModel
- В отдельный класс расширения к ViewModel.
Идея с контроллером мне не нравится, так как эти методы все-таки BusinessLayer и
в контроллере им делать особо нечего.
ViewModel  почему-то тоже не хочется грузить этими методами. Вроде бы как они будут
логично смотреться в этом классе, но почему-то на уровне интуиции, мне эта идея не
нравится.
Вынести их в класс расширения я боюсь за производительность.  

Вопрос: где правильно с точки зрения архитектуры и следования принципам MVC разместить
методы, которые обрабатывают строку, введенную  пользователем, преобразуют ее, вытягивают
из сети данные и  преобразуют их для записи в свойства ViewModel.
    


Ответы

Ответ 1



Вижу в вашем вопросе два слоя. Первый -- про производительность методов расширения. По факту, это всего лишь синтаксический сахар для вызова статического метода. Когда компилятор встречает инструкцию вызыва экстеншена -- он генерирует IL-код вызывающий статический класс: void Main() { var message = "Hello Extension Methods"; int counter1 = message.WordCount(); int counter2 = MyExtensions.WordCount(message); } // Define other methods and classes here public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } IL-код одинаковый в обоих случаях: Вы переживаете о возможном падении производительности. Но у вас в приложении есть чтение из БД и парсинг интернета -- намного более долгие операции, в десятки и сотни раз. Вы боитесь потерять, но там экономия на спичках. Второй слой вопроса -- где размещать правильно. Я бы хотел ответить вам в терминах DDD (Domain-driven design): там где у вас находится слой, который называется предметная область. Вот у вас собственно описание этого класса: Мне нужно принять строку, введенную пользователем, обработать ее (и это не валидация), вытянуть недостающие данные из интернета и сформировать ViewModel. Я не знаю вашу предметную область, поэтому могу предложить назвать этот класс *Manager. Это не очень правильно с точки DDD и подход можно критиковать, но архитектурно -- движение в более правильную сторону, всяко лучше чем размещать метод на контроллере (архитектура ТТУК (Толстый тупой уродливый контроллер)) и тем более выносить [бизнес-логику] в слой представления.

Как вычислить равномерное увеличение сложной 3D фигуры

#3d #геометрия #объекты #threejs #формулы


Добрый день, есть некий абстрактный 3D объект сложной геометрии.
Скажем его объем V₁ кубических метра. 

Задача расчитать величину (на иллюстрации выделена желтым) на которую надо сместить
каждую вершину объекта, чтобы в результате получился объект с новым объемом V₂.

Есть ли какая-нибудь формула для нахождения этой величины? 
В английском сегменте данная операция называется offset.


    


Ответы

Ответ 1



Disclaimer: с three.js не работал. Для каждой вершины новое положение: (kx, ky, kz). где k = кубический_корень_из(V2/V1) То есть координаты каждой вершины надо изменить на ((k - 1)x, (k - 1)y, (k - 1)z) А потом сдвиньте все координаты, чтобы, например, центры тяжести старой и новой фигуры совпадали, или описывающий параллелепипед равномерно расширился/сжался в направлениях координатных осей. Но это уже зависит от дополнительных условий задачи, не включенных в вопрос. Такое не должно пропасть. Сохраним для истории. Если для 2D такая же формула, но только с квадратным корнем, то она не работает. Если конечно правильно считал. Тот же прямоугольник 10x20. Площадь его 200 единиц. Хотим увеличить до 300. корень квадратный (300 / 200 ) = 1.224745... Если его применяем к сторонам, то у полученной фигуры площадь 279.5 Для 10х20 до 300, величина должна быть что-то около 1.51 А разве корень квадратный из 1.5 (300/200) не 1.224745? Остальное я не считал, а строил в Rhinoceros3D.

Достать все сообщения с публичного чата Telegram

#php #laravel #telegram_bot #telegram_api #telegram


Как спарсить заданное пользователем количество сообщений с публичного чата Telegram?

На входе пользователь вводит url чата и количество сообщений которое нужно спарсить.
Раньше никогда не работал ни с ботами Телеграм, ни с его АПИ. Как лучше это реализовать?
Возможно есть готовые библиотеки для данной задачи? 
    


Ответы

Ответ 1



Если нужно парсить с любого рандомного публичного чата, то через Telegram-Bot-API этого не сделать. Потому что бот читает только из чата, куда он добавлен сам. Для ботов есть только один способ получать сообщения - метод getUpdates получает все сообщения за определенный период, но надо понимать, что в ответ приходит данные по всем чатам и определять нужный придется самостоятельно. Для понимания как писать именно ботов для Telegram можно почитать вот эту статью - https://netology.ru/blog/bot-php ну или любую другую по запросу telegram бот на php их достаточно много=) Для того что бы парсить из любого чата, нужно писать свой клиент для Telegram, тогда можно будет использовать метод messages.getHistory() Нашел реализацию клиента Telegram на PHP - MadelineProto, можно попробовать использовать ее. Дополнительно можно почитать: https://stackoverflow.com/questions/34687435/telegram-api-with-php-not-bot - в ответах есть краткое описание шагов по созданию клиента для Telegram https://core.telegram.org/#getting-started - дока по TelegramAPI

Не могу программно убить ранее запущенный процесс

#c_sharp #процесс


В программе я открываю новый системный процесс, который проигрывает видео в дефолтном
плеере. Стартует и играет без проблем. Потом я пытаюсь закрыть (убить) процесс и получаю
ошибку: System.InvalidOperationException: No process is associated with this object. 
Кусок кода:

    string filename = "747225775.mp4";
    var myProc = new Process()
    {
        StartInfo = new ProcessStartInfo(filename)
    };
    myProc.Start();
    Thread.Sleep(5000);
    try
    {
       myProc.Kill(); //Error is here
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
        Debugger.Break();
    }


Вопрос как закрыть процесс.
PS. Задаю имя файла, а не программы, чтобы использовать программу по умолчанию.
    


Ответы

Ответ 1



Метод Process.Start связывает объект Process с дескриптором процесса только когда его вызов непосредственно порождает новый процесс. При передаче имени файла вместо имени программы класс Process обращается к функциям из shell32.dll и пытается определить, какие ассоциации в реестре установлены для данного расширения. Если используется "традиционный" способ ассоциации, с вызовом командной строки и передачей имени файла первым параметром (такой используется, например, Блокнотом), вызов Process.Start самостоятельно создает новый процесс, и связь с дескриптором устанавливается нормально. Если же для расширения установлен более новый способ ассоциации, с вызовом специального COM-объекта (который используется многими новыми приложениями, например Windows Media Player), вызов Process.Start лишь отправляет через RPC запрос на вызов метода COM-объекта и завершается, не устанавливая связь с процессом. (Как показали исследования, непосредственное создание процесса в этом случае происходит в контексте svchost.exe) Для решения этой проблемы можно использовать метод создания процесса, модифицированный следующим образом: using System; using System.ComponentModel; using System.Text; using System.Windows.Forms; using System.Diagnostics; using System.Threading; using System.Runtime.InteropServices; namespace ProcessTest { public partial class Form1 : Form { [DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, ref uint pcchOut); /*Модифицированный метод создания процесса*/ public static Process TrueProcessStart(string filename) { ProcessStartInfo psi; string ext = System.IO.Path.GetExtension(filename);//получаем расширение var sb = new StringBuilder(500);//буфер для пути к exe-файлу uint size = 500;//размер буфера /*Получаем приложение, ассоциированное с файлом*/ uint res = AssocQueryString(AssocF.None, AssocStr.Executable, ext,null, sb, ref size); if (res != 0) { Debug.WriteLine("AssocQueryString returned error: " + res.ToString("X")); psi = new ProcessStartInfo(filename);//не удалось получить приложение, используем стандартный метод } else { psi = new ProcessStartInfo(sb.ToString(), filename); } return Process.Start(psi);//запуск процесса } public Form1() { InitializeComponent(); } private void button2_Click(object sender, EventArgs e) { string filename = "c:\\images\\clip.wmv"; var myProc = TrueProcessStart(filename); if (myProc == null) { MessageBox.Show("Process can't be killed"); return; } Thread.Sleep(5000); try { myProc.Kill(); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } } [Flags] enum AssocF : uint { None = 0, Init_NoRemapCLSID = 0x1, Init_ByExeName = 0x2, Open_ByExeName = 0x2, Init_DefaultToStar = 0x4, Init_DefaultToFolder = 0x8, NoUserSettings = 0x10, NoTruncate = 0x20, Verify = 0x40, RemapRunDll = 0x80, NoFixUps = 0x100, IgnoreBaseClass = 0x200, Init_IgnoreUnknown = 0x400, Init_FixedProgId = 0x800, IsProtocol = 0x1000, InitForFile = 0x2000, } enum AssocStr { Command = 1, Executable, FriendlyDocName, FriendlyAppName, NoOpen, ShellNewValue, DDECommand, DDEIfExec, DDEApplication, DDETopic, InfoTip, QuickTip, TileInfo, ContentType, DefaultIcon, ShellExtension, DropTarget, DelegateExecute, SupportedUriProtocols, Max, } } Здесь для получения программы, ассоциированной с файлом, используется функция AssocQueryString. Потом полученное значение передается в ProcessStartInfo. Однако этот метод не всегда работает, в этом случае вызывается стандартный метод. Например, для файлов изображений нет определенной программы, просто dll грузится в процесс проводника. В этом случае убить процесс просто так не получится.

Ответ 2



знаешь почему у тебя производится такая ошибка. потому что когда ты этот файл стартуешь,как процесс но файл не может стартовать ся как процесс , просто у него определенно по умолчанию (Process File exe)'Video-Media Player' то есть Windows сохраняет эти расширение в регистре поэтому когда автоматически Кликаешь или стартуешь он автоматически стартует процесс который совпадает с этим расширением когда стартует ся файл тогда стартует ся (Process File exe)'Video-Media Player' а это новый Другой процесс. а у тебя находится пустой объект и поэтому ты не можешь убивать этот процесс. вот пример. string filename = @"C:\Test\Wildlife.wmv"; Process p = new Process(); p.StartInfo = new ProcessStartInfo("wmplayer.exe", filename); p.Start(); Thread.Sleep(2000); try { p.Kill(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.Read();

Ответ 3



Я с таким столкнулся первый раз, когда писал приложение для контроля за RDP сессиями на ферме серверов. mstsc.exe создает головной процесс, запускает дочерний процесс с окном RDP подключения и убивает головной процесс. Таким образом, PID не может быть использован для контроля за mstsc.exe Также точно работает 1С 8. Выход для себя я нашел в получении всех процессов по имени exe файла, у которых командная строка соответствует определенному критерию. Вам нужно работать именно в этом направлении

Игнорирование ненужного пакета-зависимости при обновлении

#linux #archlinux #обновление #зависимости


TL;DR Arch Linux, pacman, два пакета конфликтуют, первый нужен, а второй является
зависимостью нужного meta-пакета. Удалил второй пакет, но при обновлении pacman выдаёт
ошибку, как убрать её?



В arch linux есть пакеты openbabel и babel-cli, которые конфликтуют. Пакет openbabel
является зависимостью пакета kalzium, тот является зависимостью пакета kde-meta-kdeedu,
который является зависимостью пакета kde-applications-meta. Вот схема зависимости:

kde-applications-meta
|
v
kde-meta-kdeedu
|
v
kalzium
|
v
openbabel


Пакет kde-applications-meta скорее всего полезный, так что его удалять нельзя. Пакет
openbabel мне не нужен (там что-то, связанное с химией), поэтому при установки пакета
babel-cli я удалил пакет openbabel, выполнив pacman -Rdd openbabel. Однако теперь при
обновлении системы (pacman -Syu) pacman пытается заново установить openbabel (как зависимость
kalzium), и у него, конечно, не получается:

разрешение зависимостей...
проверка конфликтов...
ошибка: обнаружен неразрешимый конфликт пакетов
ошибка: не удалось подготовить транзакцию (конфликтующие зависимости)
:: 'babel-cli' и 'openbabel' конфликтуют


Какой наиболее правильный способ сказать pacman'у, чтобы он при обновлении kalzium
не пытался установить openbabel?



В комментариях советуют добавить пакет openbabel к игнорируем с помощью строки IgnorePkg
в файле /etc/pacman.conf. Совет хороший, но тогда каждый раз при обновлении pacman
будет спрашивать, примерно так:

разрешение зависимостей...
предупреждение: пропуск пакета openbabel-2.4.1-4
предупреждение: не удалось разрешить "openbabel", зависимость "kalzium"
:: Этот пакет не может быть обновлен из-за неразрешимых зависимостей:
      kalzium

:: Вы уверены что хотите пропустить этот пакет при обновлении? [y/N] 


Как тогда убрать этот вопрос?
    


Ответы

Ответ 1



Создай виртуальный пакет openbabel-fake, который будет предоставлять альтернативу openbabel. Не прокатило из-за явного конфликта. Когда-нибудь мейнтейнеры должны с этим разобраться. А пока: extra/kalzium 17.08.1-1 (kde-applications kdeedu) Periodic Table of Elements Кажется не очень нужным пакетом. Заменим его, тогда мета не будет ругаться. Создай виртуальный пакет kalzium-fake, который будет предоставлять альтернативу kalzium Пример aur/akonadi-fake. Если только один раз поставить, то можно прямо на месте исправить PKGBUILD. Создавай свой пакет если ставить не один раз. А полный текст пакета будет такой: # Maintainer: Your Name pkgname=kalzium-fake pkgver=1000.0.0 pkgrel=3 pkgdesc="A fake empty kalzium package for those who don't want openbabel installed on their system" arch=('any') url="https://aur.archlinux.org/packages/${pkgname}/" license=('GPL') provides=('kalzium') replaces=('kalzium') package() { true }

Почему нельзя использовать в аргументах шаблонов сущности с внутренней компоновкой?

#cpp


Почему нельзя использовать в аргументах шаблонов сущности с внутренней компоновкой?

template
class Template { ... };
Template<"literal"> T;            // Ошибка! 


Нельзя использовать и глобальный указатель:

template
class Template { ... };
char const *s = "Literal";
Template T;                    // Ошибка!


Интересуют физические причины этих ограничений, а не правила, как можно а как нельзя.
    


Ответы

Ответ 1



В первом случае использование невозможно, так как даже одинаковые строковые литералы не обязаны иметь одинаковые адреса в программе. Т.е. Template<"literal"> мог бы получать разный аргумент шаблона в зависимости от контекста (если бы это было разрешено). Более того, в черновике стандарта N4687 использование строковых литералов явно запрещено в шаблонах 17.3.2/2: For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of): a subobject (4.5), a temporary object (15.2), a string literal (5.13.5), Это касается как непосредственного указания литерала, так и переменной типа char const * (см. далее). Во втором случае ошибка из-за использования переменной, а не адреса, фиксированного на момент компиляции. Например, gcc даёт следующую ошибку: error: 's' is not a valid template argument because 's' is a variable, not the address of a variable Чтобы всё же использовать шаблон, можно заменить указатель массивом: const char s[] = "literal"; Template T; Внутренняя или внешняя компоновка (linkage) будет - не важно по современным меркам, главное, чтобы она была, т.е. нельзя, например, использовать локальные объекты: template class Template { }; const char i[] = "literal"; // internal linkage char e[] = "literal"; // external linkage int main() { char n1[] = "literal"; // no linkage const char n2[] = "literal"; // no linkage constexpr char n3[] = "literal"; // no linkage Template Ti; // ok Template Te; // ok Template Tn1; // error Template Tn2; // error Template Tn3; // error }

почему Java stack memory быстрее heap

#java


почему доступ к объектам в стэке быстрее чем к объектам вне стэка ?
    


Ответы

Ответ 1



Управление памятью для стека тривиально: машина просто увеличивает или уменьшает одно значение, так называемый «указатель стека» (stack pointer). Управление памятью для кучи сложнее: память, выделенная в куче, освобождается в произвольные моменты, а каждая область выделенной в куче памяти может быть произвольного размера. Распределителю памяти, как правило, требуется приложить гораздо больше усилий для определения областей, которые можно использовать заново.

Задача про паркет

#алгоритм #комбинаторика #динамическое_программирование


Есть поле из клеток, размеры которого [n * m]. Необходимо покрыть это поле плитками
размера [1 * 2] (их можно поворачивать) таким образом, чтоб все клетки поля были накрыты,
и чтоб плитки не вылазили за границы поля. Задача состоит в том, чтоб найти количество
таких покрытий.

Данную задачу я решил с помощью динамики по профилю. Профиль - состояние одного столбца
поля. Подразумевается, что слева от данного профиля уже все клетки заполнены, а справа
ещё нет. Переход из профиля в профиль существует, если можно корректно положить некоторое
количество плиток (переходы хранятся в матрице dp).

Затем я считаю матрицу ответов:

if (dp[j][i]) ans[k][i] += ans[k - 1][j];


И после посчитанной матрицы ответов считаю ответ на задачу

if (isFinishing(i)) res += ans[m - 1][i];

Эта строчка означает, что если профиль может являться завершающим, то добавить это
число в ответу.

Данное решение вполне себе работает. Но затем я нашел модификацию данной задачи.
Модификация состоит в том, чтобы найти количество таких покрытий, в которых не будет
сплошных линий из плиток [1 * 2].

Пример: 

Первая картинка - это пример того, какими должны быть покрытия, вторая - какими не
должны. 

У меня был вариант хранить в динамике ещё состояние для каждой строки, была ли она
перекрыта хоть раз вертикальной плиткой, и добавлять к ответу только эти варианты.
Возможно, это бы и работало, но я не особо понимаю, как это реализовать. 

Подскажите, как реализовать, или предложите свои варианты решения.
    


Ответы

Ответ 1



Можно сделать динамически по рядам длины n. Для каждого промежуточного решения хранить состояние "дырок", куда можно подставить вертикальные плитки. Установим, что мы всегда движемся сверху вниз, значит дырки будут внизу. Динамический массив будет иметь вид: [int ряды, bool[] дырки] = кол-во решений Например, все возможные решения для 1 ряда длиной 4: [1, [0, 0, 1, 1]] = 1 // [xx] . . [1, [1, 0, 0, 1]] = 1 // . [xx] . [1, [1, 1, 0, 0]] = 1 // . . [xx] [1, [1, 1, 1, 1]] = 1 // . . . . 0 - горизонтальная плитка, 1 - пустое место для будущей вертикальной плитки. Количество нулей идущих подряд всегда должно быть парным, все нули запрещены (что бы удовлетворить ваше условие). Дальше мы пытаемся соединить решение для x-1 рядов с решением для 1 ряда, что бы получить решение для x рядов: for (prev in solutions[x-1]) for (single in solutions[1]) { if (дырки prev совместимы c дырками single) { holes = считаем как будут выглядеть дырки после совмещения 2 решений solutions[x][holes] += prev; } } Конечным решением будет значение solutions[m, [0, 0, 0, 0, ... 0]], где все нули - это полностью заполненный последний ряд. Возможно существует более элегантное решение.

Как отключить переключение полей tab'ом

#javascript #html


Делаю онлайн-редактор кода, соответственно, куда же без старого доброго TAB, но при
нажатии, конечно же, фокус уходи с поля ввода.

Подскажите, есть ли какие-либо решения данной проблемы. 

P.S. если это важно, то поле у меня- textarea
    


Ответы

Ответ 1



Взято отсюда

Двойной static_cast через void*, вместо reinterpret_cast

#cpp


Некоторые авторы используют двойное преобразование через void* с помощью static_cast,
вида:

long* px;
char* p = static_cast(static_cast(px));


вместо reinterpret_cast:

long* px;
char* p = reinterpret_cast(px);


В чем преимущество/недостатки первого варианта?
    


Ответы

Ответ 1



Двойное преобразование static_cast с преобразованием в void* на первом шаге ограничивает возможный тип аргумента указателем, и не позволяет использовать в качестве него целый тип. Пример: int i = 42; int* p = nullptr; // Различное поведение при использовании целого аргумента static_cast(static_cast(i)); // ошибка компиляции reinterpret_cast(i); // ok // Эквивалентны при использовании аргумента указателя static_cast(static_cast(p)); // ok reinterpret_cast(p); // ok

Ответ 2



Вообще-то один static_cast даже лишний, можно определить шаблонную функцию специально для указателей, нельзя записать в строчку, как вариант с двумя static_cast, зато понятно, что имеется в виду. template inline U* pointer_cast(T* p) { void* t = p; return static_cast(t); }

Лицензия Windows разрешает использование предустановленных шрифтов в картинках на сайте?

#windows #шрифты #лицензирование


Например, написать что-то шрифтом Arial, сохранить как image.jpg и опубликовать на сайте.
    


Ответы

Ответ 1



Тёма Лебедев рассказывал о претензиях, предъявляемых его студией именно из-за шрифтов, которые использовались в ряде работ. Так что стоит внимательнее читать лицензионные соглашения. Что касается Windows, в своё время пришлось столкнуться с тем, что MS продвигает политику типа "шрифты лицензируются у их создателей или правообладателей" и могут свободно использоваться лишь внутри системы и её приложений. На практике это означает, что вы смело можете использовать шрифты, скажем, для печати презентации и ехать с ней на конференцию, но гарантированно не имеете права использовать в коммерческих целях (нам три года назад пришлось менять шрифт в товарном знаке на OFL). Опять же, вероятность претензии к частному пользователю значительно ниже, нежели к компании. Исходя из этого, формально, вы нарушаете правила лицензирования. поступая так, как вы описали. Но если сайт некоммерческий, посещаемость его не чрезмерно велика, то вероятность претензии низка. А лучше - пользоваться OFL шрифтами

Ответ 2



Конкретно для Arial, насколько я понял, можно, в том числе и для коммерческих целей, но нельзя распечатать шрифт в виде картинок и использовать их как замену шрифту. Правообладателя и лицензию можно посмотреть в самом файле шрифта в windows/fonts, в частности для Arial там указано: Авторские права: The Monotype Corporation. Описание лицензии: "...You may only (i) embed this font in content as permitted by the embedding restrictions included in this font;". Не понятно что за ограничения включённые в этот шрифт. Вероятно это отсылка к полной лицензии на шрифт, которую можно найти на официальном сайте компании Monotype: http://www.monotype.com/legal/eula/ FONT SOFTWARE END USER LICENSE AGREEMENT You may embed static graphic images into an electronic document, including a Commercial Product, (for example, a “gif”) with a representation of a typeface and typographic design or ornament created with the Font Software as long as such images are not used as a replacement for Font Software...

MinGW - SEH и SJLJ. Странное поведение

#cpp #исключения


Имеется в наличии невероятно простой код:

#include 

class C {
    public :
        C() try {
            throw std::runtime_error("C::C");
        } 
        catch (...) { }
};

int main() {
    C c;
}


Компилируем этот код компилятором MinGW-builds 8.1.0 x86_64 SJLJ и, запустив полученное
приложение, получаем следующий вывод:

terminate called after throwing an instance of 'std::runtime_error'
  what():  C::C

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.


А теперь сделаем то же самое, заменив компилятор на MinGW-builds 8.1.0 x86_64 SEH.
С удивлением наблюдаем, что строка с сообщением о типе исключения исчезла:

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.


Собственно, вопрос. Почему? Что происходит? Так и должно быть или я чего-то не понимаю?

P.S. Интересно, что если у конструктора убрать function-try-block, т.е. привести
его к виду

C() {
    throw std::runtime_error("C::C");
}


то вывод при обоих способах обработки исключений идентичный.



ДОПОЛНЕНИЕ

Если добавить конструктору спецификатор noexcept, то поведение вновь становится одинаковым:

C() noexcept try {
    throw std::runtime_error("C::C");
}
catch (...) { }

    


Ответы

Ответ 1



Формально говоря, у вас в коде нет "обработки" исключений. Исключение, пойманное function-try-block конструктора, невозможно "подавить" - оно все равно будет перевыброшено автоматически, если вы этого не сделаете сами. То есть в любом из ваших вариантов исключение остается необработанным. Если исключение не обработано, то вызывается std::terminate, которая вызовет текущий обработчик terminate (terminate handler). А что делает установленный по умолчанию обработчик terminate - определяется реализацией. Вот эти различия между реализациями вы и наблюдаете. Например, если в GCC при помощи std::set_terminate задать свой пустой обработчик terminate, то вывод о типе исключения и вывод what() исчезнет.

В чем разница между обычным программированием и программированием с использованием Spring?

#java #spring


Я понимаю как можно использовать Spring, но я до сих пор не могу понять то, о чём
везде так пишут: "Мы разрываем жёсткую связку между классами" и т.д., и т.п.

Но ведь мы и так жёстко всё прописываем либо в аннотациях, либо в *.xml, да и к тому
же в Main, когда пишем что-то вида:

Cat cat = context.getBean(Cat.class);
Dog dog = (Dog) context.getBean("dog");
Parrot parrot = context.getBean("parrot-kesha", Parrot.class);


Мы ведь и так тут всё жёстко прописали.

Также, Spring использует интерфейсы для ухода от жёсткой зависимости между классами.
Тогда что мне мешает без Spring сделать интерфейс IAnimals и в нём метод void voice();,

а потом, просто сделать:

class Cat implements IAnimals {
    publice void voice() {
        System.out.print("myau");
}}

class Dog implements IAnimals {
    publice void voice() {
        System.out.print("gav");
}}

class Parrot implements IAnimals {
    publice void voice() {
        System.out.print("kesha");
}}


???

Вот эту разницу между обычным программированием и программированием с использованием
Spring я не могу понять.
    


Ответы

Ответ 1



Вот вам реальный пример. Отправка sms на тестовом сервере и на рабочем. В коде который отправляет sms работаем с интерфейсом, а реализацию spring подставляет в зависимости от сервера. Если понадобится отправлять sms сообщение через другой сервис просто добавим новую реализацию, и не будем переписывать весь код, откуда отправляется sms. interface SmsService { void send(String to, String message); } @Service @Profile("development") class DevelopmentSmsService implements SmsService { public void send(String to, String message) { // печать сообщения в лог } } @Service @Profile("production") class ProductionSmsService implements SmsService { public void send(String to, String message) { // отправка сообщения через API нужного сервиса } } @Service class AnotherService { @Autowired SmsService smsService; public test() { // что-то делаем // отправляем sms через интерфейс, не заботясь о подробностях smsService.send(to, message); } } Вы рассматриваете слишком простые пример, где можно спокойно обойтись и без внедрения зависимостей. Но если бы у вас был проект на сотню тысяч классов, где один какой-нибудь UserRepository используется тысячу раз по всему проекту, вы бы поняли как это круто что spring подставил везде его реализацию вместо вас.

Ответ 2



Жесткая или не жесткая определяется тем, как вы будете использовать IAnimals. Для чего вы создали интерфейс? Для того чтобы иметь классы, которые его имплементируют. Если у вас несколько имплементаций, а вам нужна конкретная имплементация, то вы используете паттерн метода фактора или абстрактного фактора для создания экземпляра класса, который имплементирует этот интерфейс. Таким образом вы можете внедрять нежесткую связь в другие классы без использования Spring. У вас нет зависимостей между классами. Проблема появляется если вы будете устанавливать связи между объектами, и если эти связи должны быть нежесткие то вам всегда нужно четко знать где и когда, использовать и какую имплементацию. В общем если таких связей очень много, то вы будете морочить себе голову решая подобные проблемы. В конце концов вы поймете, что вам нужен Spring или какой нибудь другой контейнер, где вы будете определять зависимости с помощью конфигурации, независимо от кода их используемого. Более того, поскольку Spring берет на себя проблему создания бинов, то он еще и занимается менежментом. Что дает возможность интегрироваться в другие фреймворки.

Ответ 3



Основная фича Spring это его DI контейнер. Мы можем внедрять зависимости в бины, не задавая жестко тип. Вот Вы привели пример с интерфейсом Animal. Давайте разовьем немного тему. Допустим мы имеем вет. клинику и можем заставлять "говорить" любое животное. При этом нам абсолютно не важно какое это будет животное. Таким образом мы можем написать логику извлечения звука из животных один раз, а самих животных придумывать по ходу пьесы. И не придется править код вет. клиники, а лишь добавлять новых и новых Animal. @Component public class Clinic { private final List animals; @Autowired //ЕМНИП, в крайних версиях, даже этого делать уже не нужно public Clinic(List animals) { this.animals = animal; } public void doVoice() { for (Animal animal : animals) { animal.voice(); } } } public interface Animal { void voice(); } @Component public Cat implements Animal { @Override public void voice() { System.out.print("myau"); } } @Component public Dog implements Animal { @Override public void voice() { System.out.print("gav"); } } По поводу того, что мы в Spring все-равно жестко все задаем. Посмею с Вами не согласится. Да, в xml мы описывали бины, рассказывая Spring-у кто они, и что они. Теперь же Spring, при помощи аннотаций, сам может разобраться кто есть кто. Spring это уже не просто очередной фреймворк, это, можно сказать, промышленный стандарт. Советую к прочтению "Spring In Action" Крэйга Уолша, очень доступно товарищ объясняет.

Как загрузить ядро системы загрузчиком на fasm?

#c #ассемблер #kernel #os #fasm


Доброе время суток! Недавно начал писать свою ос на FASM и C и застрял на создании
ядра. (пишу в Windows, mingw) 

Вкратце, мне нужно залинковать загрузчик и ядро, но пока ничего не выходит.

Код загрузчика (bootloader.asm):

format binary
; preparation
org 0x7c00
use16

begin:
xor ax, ax
mov ds, ax
mov es, ax

mov [disk], dl ; save disk number for later

mov si, loading16 ; print welcome string
pchar:
 .lp:
  mov al, [si]
  cmp al, 0
  je .exit
 .pch:
  mov ah, 0eh
  int 10h
  add si, 1
  jmp .lp
 .exit:


mov ah, 0  ;reset disk
int 13h

; load kernel_entry and kernel from disk
mov ah, 2h     ;read sectors
mov al, 1      ;sectors to read
mov ch, 0      ;cylinder idx
mov dh, 0      ;head idx
mov cl, 2      ;sector idx
mov dl, [disk] ;disk idx
mov bx, 0x1000 ;target pointer
int 13h


; get into pm and jump to KERNEL label
jmp switch_to_pm

jmp $ ; never executed

use32
KERNEL:

jmp 0x1000 ; jump to the actual kernel

jmp $ ; never executed


include './32/gdt.asm'        ; gdt for protected mode
include './32/switch32.asm'   ; code for entering protected mode
include './32/strout32.asm'   ; protected mode print

;; data

disk: rb 1

use16
wc32: db "Welcome to x32!", 0
loading16: db "Loading OS"
osver: db "v.0.0", 0

;bootloader fill
times 510-($-$$) db 0
;magic number
dw 0xaa55

;;;;;;;;;;;;;


./32/gdt.asm:

use16
gdt_start:
    dq 0x0
gdt_code:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:

; GDT descriptor
gdt_descriptor:
    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
    dd gdt_start ; address (32 bit)

; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start


./32/strout32.asm:

use32

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f ; the color byte for each character

__print_string_label:
    mov edx, VIDEO_MEMORY

.print_string_pm_loop:
    mov al, [ebx] ; [ebx] is the address of our character
    mov ah, WHITE_ON_BLACK

    cmp al, 0 ; check if end of string
    je .print_string_pm_done

    mov [edx], ax ; store character + attribute in video memory
    add ebx, 1 ; next char
    add edx, 2 ; next video memory position

    jmp .print_string_pm_loop

.print_string_pm_done:
    ret

;PRINT MACRO
macro print _str* {
      mov ebx, _str
      call __print_string_label
}


./32/switch32.asm:

use16
switch_to_pm:
    mov ax, 0x2401
    int 15h ;A20

    mov ax, 3h
    int 10h

    cli ; 1. disable interrupts
    lgdt [gdt_descriptor] ; 2. load the GDT descriptor
    mov eax, cr0
    or eax, 0x1 ; 3. set 32-bit mode bit in cr0
    mov cr0, eax
    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment

use32
init_pm: ; we are now using 32-bit instructions
    mov ax, DATA_SEG ; 5. update the segment registers
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
    mov esp, ebp

    call KERNEL ; 7. Call a well-known label with useful code
use16 ;     


Код загрузчика ядра (kernelentr.asm):

format elf
use32
extrn _start
call _start

jmp $        


Ядро (kernel.c):

void start (void) 
{
   unsigned char* vga = (unsigned char*) 0xb8000;
   vga[0] = 'X'; //need to make sure that this is a character
   vga[1] = 0x09; //append the attribute byte
   for(;;); //make sure our kernel never stops, with an infinite loop
}


Мой порядок действий:


Компиляция ядра

gcc -m32 -ffreestanding -nostdlib -c kernel.c -o kernel.o

Линковка получившегося обьекта и загрузчика ядра(?)

ld -T NUL -o kernel.tmp -Ttext 0x1000 kernelentr.o kernel.o

Извлечение бинарного кода из ядра с загрузчиком

objcopy -O binary -j .text  kernel.tmp kernel.bin

Совмещение загрузчика и получившегося бинарника

copy /b bootloader.bin+kernel.bin os.bin



EDIT: Команды брал отсюда - https://stackoverflow.com/questions/25128579/ld-cannot-perform-pe-operations-on-non-pe-output-file-error

После всей процедуры получается файл который, увы, не запускается и вылетает из QEMU
с ошибкой 'Trying to execute code outside RAM or ROM at 0xc6fc458b'

Укажите пожалуйста на ошибку и и скажите как все таки заставить это штуку работать.

UPDATE: Вдруг бинарник таки заработал, но криво - 
Теперь при запуске и правда высвечиваеся синяя буква Х, но экран постоянно меняется
на до х32 (с надписью Loading OS v0.0) и обратно.
При этом эмулятор не крашится...
    


Ответы

Ответ 1



На шаге ld -T NUL -o kernel.tmp -Ttext 0x1000 kernelentr.o kernel.o получается файл с двумя исполняемыми секциями: .flat ("загрузчик ядра") и .text (сишный код): .flat:00000000 ; Segment type: Pure code .flat:00000000 ; Segment permissions: Read/Write/Execute .flat:00000000 _flat segment dword public 'CODE' use32 .flat:00000000 assume cs:_flat .flat:00000000 assume es:nothing, ss:nothing, ds:_flat, fs:nothing, gs:nothing .flat:00000000 call _start .flat:00000005 .flat:00000005 loc_5: ; CODE XREF: .flat:loc_5j .flat:00000005 jmp short loc_5 .flat:00000005 ; --------------------------------------------------------------------------- .flat:00000007 align 200h .flat:00000200 dd 380h dup(?) .flat:00000200 _flat ends .flat:00000200 .text:00001000 ; Section 2. (virtual address FFC01000) .text:00001000 ; Virtual size : 00000020 ( 32.) .text:00001000 ; Section size in file : 00000200 ( 512.) .text:00001000 ; Offset to raw data for section: 00000600 .text:00001000 ; Flags 60300020: Text Executable Readable .text:00001000 ; Alignment : 4 bytes .text:00001000 ; =========================================================================== .text:00001000 .text:00001000 ; Segment type: Pure code .text:00001000 ; Segment permissions: Read/Execute .text:00001000 _text segment dword public 'CODE' use32 .text:00001000 assume cs:_text .text:00001000 ;org 1000h .text:00001000 assume es:nothing, ss:nothing, ds:_flat, fs:nothing, gs:nothing .text:00001000 .text:00001000 ; =============== S U B R O U T I N E ======================================= .text:00001000 .text:00001000 ; Attributes: bp-based frame .text:00001000 .text:00001000 public _start .text:00001000 _start proc near ; CODE XREF: .flat:00000000p .text:00001000 .text:00001000 var_4 = dword ptr -4 .text:00001000 .text:00001000 push ebp .text:00001001 mov ebp, esp .text:00001003 sub esp, 10h .text:00001006 mov [ebp+var_4], 0B8000h .text:0000100D mov eax, [ebp+var_4] .text:00001010 mov byte ptr [eax], 58h ; 'X' .text:00001013 mov eax, [ebp+var_4] .text:00001016 add eax, 1 .text:00001019 mov byte ptr [eax], 9 .text:0000101C nop .text:0000101D leave .text:0000101E retn .text:0000101E _start endp .text:0000101E .text:0000101E ; --------------------------------------------------------------------------- .text:0000101F align 200h .text:00001200 dd 380h dup(?) .text:00001200 _text ends На следующем шаге (objcopy) из этого исполняемого файла извлекается содержимое только секции .text, а код загрузчика ядра по факту никак не используется. Можно убедиться в этом, если с помощью дизассемблера посмотреть содержимое kernel.bin, полученного на шаге objcopy: seg000:00000000 ; Segment type: Pure code seg000:00000000 seg000 segment byte public 'CODE' use32 seg000:00000000 assume cs:seg000 seg000:00000000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing seg000:00000000 push ebp seg000:00000001 mov ebp, esp seg000:00000003 sub esp, 10h seg000:00000006 mov dword ptr [ebp-4], 0B8000h seg000:0000000D mov eax, [ebp-4] seg000:00000010 mov byte ptr [eax], 58h seg000:00000013 mov eax, [ebp-4] seg000:00000016 add eax, 1 seg000:00000019 mov byte ptr [eax], 9 seg000:0000001C nop seg000:0000001D leave seg000:0000001E retn seg000:0000001E ; --------------------------------------------------------------------------- seg000:0000001F align 10h seg000:0000001F seg000 ends Видим только функцию, без кода ее вызова и бесконечного цикла после вызова. Нужно сделать, чтобы при линковке все попало в одну секцию. Можно просто явно указать имя секции в kernelentr.asm: format elf use32 extrn _start section '.text' call _start jmp $ Результат линковки: .text:00001000 ; Segment type: Pure code .text:00001000 ; Segment permissions: Read/Execute .text:00001000 _text segment dword public 'CODE' use32 .text:00001000 assume cs:_text .text:00001000 ;org 1000h .text:00001000 assume es:nothing, ss:nothing, ds:_text, fs:nothing, gs:nothing .text:00001000 .text:00001000 ; =============== S U B R O U T I N E ======================================= .text:00001000 .text:00001000 ; Attributes: noreturn .text:00001000 .text:00001000 public start .text:00001000 start proc near .text:00001000 call _start .text:00001005 .text:00001005 loc_1005: ; CODE XREF: start:loc_1005j .text:00001005 jmp short loc_1005 .text:00001005 start endp .text:00001005 .text:00001005 ; --------------------------------------------------------------------------- .text:00001007 align 4 .text:00001008 .text:00001008 ; =============== S U B R O U T I N E ======================================= .text:00001008 .text:00001008 ; Attributes: bp-based frame .text:00001008 .text:00001008 public _start .text:00001008 _start proc near ; CODE XREF: startp .text:00001008 .text:00001008 var_4 = dword ptr -4 .text:00001008 .text:00001008 push ebp .text:00001009 mov ebp, esp .text:0000100B sub esp, 10h .text:0000100E mov [ebp+var_4], 0B8000h .text:00001015 mov eax, [ebp+var_4] .text:00001018 mov byte ptr [eax], 58h .text:0000101B mov eax, [ebp+var_4] .text:0000101E add eax, 1 .text:00001021 mov byte ptr [eax], 9 .text:00001024 nop .text:00001025 leave .text:00001026 retn .text:00001026 _start endp .text:00001026 .text:00001026 ; --------------------------------------------------------------------------- .text:00001027 align 200h .text:00001200 dd 380h dup(?) .text:00001200 _text ends Листинг содержимого kernel.bin: seg000:00000000 call sub_8 seg000:00000005 seg000:00000005 loc_5: ; CODE XREF: seg000:loc_5j seg000:00000005 jmp short loc_5 seg000:00000005 ; --------------------------------------------------------------------------- seg000:00000007 align 4 seg000:00000008 seg000:00000008 ; =============== S U B R O U T I N E ======================================= seg000:00000008 seg000:00000008 ; Attributes: bp-based frame seg000:00000008 seg000:00000008 sub_8 proc near ; CODE XREF: seg000:00000000p seg000:00000008 seg000:00000008 var_4 = dword ptr -4 seg000:00000008 seg000:00000008 push ebp seg000:00000009 mov ebp, esp seg000:0000000B sub esp, 10h seg000:0000000E mov [ebp+var_4], 0B8000h seg000:00000015 mov eax, [ebp+var_4] seg000:00000018 mov byte ptr [eax], 58h seg000:0000001B mov eax, [ebp+var_4] seg000:0000001E add eax, 1 seg000:00000021 mov byte ptr [eax], 9 seg000:00000024 nop seg000:00000025 leave seg000:00000026 retn seg000:00000026 sub_8 endp Дальше проходим по пунктам из вопроса, в конце запускаем os.bin с помощью QEMU: Стабильно показывает X, картинка не меняется (если не считать мигающего курсора под Х).