Страницы

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

Показаны сообщения с ярлыком исключения. Показать все сообщения
Показаны сообщения с ярлыком исключения. Показать все сообщения

среда, 15 апреля 2020 г.

java.lang.InstantiationException

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

                    
Здравствуйте!
В данный момент пишу такое учебное задание:
На нашем компьютере есть файл (путь C:\User), в котором храниться такая информация:
int id 1
String name Vasya
int age 25
long salary 2500
Цель, написать такой вот метод,
public Object load (Class cl, int id), который бы сканировал нужный нам файл User,
и возвращал конкретный объект с указанным в параметрах id.

Написал такой код:
public class Main {
public static void main(String[] args) {

    ObjectMapper om = new ObjectMapper();

    try{
    System.out.println((om.load(User.class,1)).toString());
    } catch (Exception e){
        e.printStackTrace();
    }
}

}
public class User {
private int id;
private String name;
private int age;
private long salary;

public User(String name, int age, long salary) {
    this.id=0;
    this.name = name;
    this.age = age;
    this.salary = salary;
}

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public long getSalary() {
    return salary;
}

public void setSalary(long salary) {
    this.salary = salary;
}

@Override
public String toString() {
    return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            ", salary=" + salary +
            '}';
}

}
public class ObjectMapper {

public Object load(Class cl, int id) throws IllegalAccessException, InstantiationException,
IOException, NoSuchMethodException, InvocationTargetException {
    ArrayList lines = new ArrayList();   //Сроки файла
    Object obj = cl.newInstance(); //Создаем пустой объект класса User
    Field[] fields = cl.getDeclaredFields(); //Считываем поля

    Scanner sc = new Scanner(new File("C:\\" + cl.getSimpleName())); //Скан. файл
    String line;
    String[] setterNames = new String[fields.length];  //Создаем массив названий
"методов-сеттеров"
    String[] fieldParameters = new String[3]; //Массив слов, которые нах. в одной
строке (мы точно знаем, что слов в каждой строке 3: тип поля - имя поля - значение поля)
    String[] fieldValue = new String[fields.length]; //Массив значений каждого поля
(например, [0]1,[1]Vasya,[2]25,[3]2500)
    String[] fieldType = new String[fields.length];  //Массив типов каждого поля
(например, [0]"int",[1]"String",[2]"int",[3]"long")

    //Построчно сканируем содержимое файла и заносим в наш ArrayList
    while (sc.hasNextLine()) {
        line = sc.nextLine();
        lines.add(line);
    }

    for (int i = 0; i < lines.size(); i++) {    // Считываем каждую строку ArrayList-a и
        String idLine =  "int id " + id;
        if (lines.get(i).equals(idLine)); {  // сравниваем строки из ArrayList-a
со строкой, например "int id 1"
            int temp;
            for (temp = i; temp < i + fields.length; temp++) {
                fieldParameters = lines.get(temp).split(" "); // Дробим строку на 3 слова
                String methName = "set" + fieldParameters[1].toUpperCase().charAt(0)
+ fieldParameters[1].substring(1); // Создаем имя метода (например, setId)
                setterNames[temp - i] = methName; // Заносим имя метода в массив
                fieldType[temp - i] = fieldParameters[0]; // Заносим тип поля в массив
                fieldValue[temp - i] = fieldParameters[2];  // Заносим значение поля
в массив
            }
        }
    }

    //Заполняем наш, пока еще пустой Object obj значениями
    for (int i = 0; i < fields.length; i++) {
        // Значение нужно записать в соотв. ему типе, если поле "int", значит в поле
заносим значение типа int
        if (fieldType[i].equals("int")) {
            cl.getDeclaredMethod(setterNames[i]).invoke(obj, Integer.parseInt(fieldValue[i]));
        }
        if (fieldType[i].equals("long")) {
            cl.getDeclaredMethod(setterNames[i]).invoke(obj, Long.parseLong(fieldValue[i]));
        }
        if (fieldType[i].equals("String")) {
            cl.getDeclaredMethod(setterNames[i]).invoke(obj, fieldValue[i]);
        }
    }
    return obj;  // Возвращаем заполненный объект
}
}

К сожалению данный метод не работает, пока не могу понять почему.
Вылетает такая ошибка:
java.lang.InstantiationException: User
at java.lang.Class.newInstance0(Class.java:357)
at java.lang.Class.newInstance(Class.java:325)
at ObjectMapper.load(ObjectMapper.java:13)
    .......

Кто-то может объяснить, что делаю не так, и как надо сделать так, чтобы заработало ?    


Ответы

Ответ 1



Object obj = cl.newInstance(); //Создаем пустой объект класса User это значит что ожидается конструктор по умолчанию, которого у вас нет. Заведите конструктор класса User без параметров.

воскресенье, 29 марта 2020 г.

Как работают исключения?

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


public class DogIsNotReadyException extends Exception {

    public DogIsNotReadyException(String message) {
        super(message);
    }
}

public class Dog {

    String name;
    boolean isCollarPutOn;
    boolean isLeashPutOn;
    boolean isMuzzlePutOn;

    public Dog(String name) {
        this.name = name;
    }

    public void putCollar() {

        System.out.println("Ошейник надет!");
        this.isCollarPutOn = true;
    }

    public void putLeash() {

        System.out.println("Поводок надет!");
        this.isLeashPutOn = true;
    }

    public void putMuzzle() {
        System.out.println("Намордник надет!");
        this.isMuzzlePutOn = true;
    }

    public void walk() throws DogIsNotReadyException {

        System.out.println("Собираемся на прогулку!");
        if (isCollarPutOn && isLeashPutOn && isMuzzlePutOn) {
            System.out.println("Ура, идем гулять! " + name + " очень рад!");
        } else {
            throw new DogIsNotReadyException("Собака " + name + " не готова к прогулке!
Проверьте экипировку!");
        }
    }
}


public class Test {
    public static void main(String[] args) throws DogIsNotReadyException {
        Dog dog = new Dog("Rex");
        dog.putCollar();
        dog.putMuzzle();
        dog.walk();
    }
}


Почему вот этот код выводит в консоль: 

Ошейник надет!
Намордник надет!
Собираемся на прогулку!
Exception in thread "main" DogIsNotReadyException: Собака Rex не готова к прогулке!
Проверьте экипировку!
    at Dog.walk(Dog.java:35)
at Test.main(Test.java:6) А вот ниже пример, выводит вот это: Ошейник надет! Намордник надет! Собираемся на прогулку! Собака Мухтар не готова к прогулке! Проверьте экипировку! Проверяем снаряжение! Ошейник надет? true Поводок надет? false Намордник надет? true Вот пример: public class DogIsNotReadyException extends Exception { public DogIsNotReadyException(String message) { super(message); } } public class Dog { String name; boolean isCollarPutOn; boolean isLeashPutOn; boolean isMuzzlePutOn; public Dog(String name) { this.name = name; } public void putCollar() { System.out.println("Ошейник надет!"); this.isCollarPutOn = true; } public void putLeash() { System.out.println("Поводок надет!"); this.isLeashPutOn = true; } public void putMuzzle() { System.out.println("Намордник надет!"); this.isMuzzlePutOn = true; } public void walk() throws DogIsNotReadyException { System.out.println("Собираемся на прогулку!"); if (isCollarPutOn && isLeashPutOn && isMuzzlePutOn) { System.out.println("Ура, идем гулять! " + name + " очень рад!"); } else { throw new DogIsNotReadyException("Собака " + name + " не готова к прогулке! Проверьте экипировку!"); } } } public class Test { public static void main(String[] args) { Dog dog = new Dog("Rex"); dog.putCollar(); dog.putMuzzle(); try{ dog.walk(); } catch (DogIsNotReadyException e) { System.out.println(e.getMessage()); System.out.println("Проверяем снаряжение! \n Ошейник надет? " + dog.isCollarPutOn + "\r\n Поводок надет? " + dog.isLeashPutOn + "\r\n Намордник надет? " + dog.isMuzzlePutOn); } } } Вопросы: Почему первый пример не работает если мы метод main помечаем throws? Должно же одинаково отрабатывать как с первым примером так и со вторым. Ну либо throws либо try catch должно быть. Не понимаю как отрабатывает метод getMessage(), залез в исходники и увидел вот это: public String getMessage() { return detailMessage; } Посмотрел что такое detailMessage это просто переменная, не очень всё равно понял как это отрабатывает. Для проверки сделал вот так: public class Test { public static void main(String[] args) { Dog dog = new Dog("Rex"); try{ dog.walk(); } catch (DogIsNotReadyException e) { System.out.println(e.getMessage()); } } } Вывелось в консоль: Собираемся на прогулку! Собака Rex не готова к прогулке! Проверьте экипировку! В вызове метода walk отрабатывает System.out.println("Собираемся на прогулку!"); А вот потом отрабатывает throw new DogIsNotReadyException("Собака " + name + " не готова к прогулке! Проверьте экипировку!"); но только когда System.out.println(e.getMessage()); мы написали, не понимаю как отрабатывает код.


Ответы

Ответ 1



Примеры работают. Исключение необходимо ловить, иначе оно выбрасывается выше, на уровень управляющего кода. Это справедливо и для функции main В первом примере вы его пробрасываете наружу public static void main(String[] args) throws DogIsNotReadyException { тем самым указывая что не хотите заботится об обработке этого исключения и возлагаете эту ответственность на того, кто запускает функцию main. результат выброса - завершение программы с выводом этого исключения, чисто технически это механизм отработает так же как и внутри программы. ОС узнает об исключении и может предпринять действия по его обработке. Представьте что Ваша программа по выгулу собак часть чего-то большего. Управляющий код так же должен узнать о нештатных ситуациях и уметь например перезапустить Вашу программу. PS: getMessage это метод акцессор для поля message у исключения, оно выводит строку, которая была передана в конструктор исключения и там запомнена в переменную message

воскресенье, 15 марта 2020 г.

Транзакции в коде

#алгоритм #разработка_игр #исключения #best_practice


Есть примерно такой код:

ПриВыходеИзВарпРежима()
{
  ДобавитьКораблиВКосмос();
  УстановитьУНихДефолтныеКоординаты();
  ...
  ВключитьЩиты();
  ОбнаружитьПротивников();
}


В методе ВключитьЩиты произошла ошибка, а значит, я хочу всё откатить назад.
С базами данных всё легко, там есть транзакции. А как быть с кодом? 
Может придумали что-то? Чтобы не писать кучу обратных шагов.
    


Ответы

Ответ 1



Попробуйте написать ваш код в стиле «опасные изменения — безопасный коммит» + иммутабельность. // изменения локальные корабли' = создать корабли с дефолтными координатами и включённым щитом(); локальный космос' = космос.ДобавитьКораблиИВернутьНовыйКосмос(корабли'); ... локальные противники' = обнаружить противников в (космос'); // безопасный коммит космос = космос' противники = противники' Если какая-то часть из изменений вылетит — она затрагивает лишь локальные объекты, которые съест garbage collector или RAII. Если все объекты у вас иммутабельны, то новый локальный космос — не расходная штука: он делит большую часть своих объектов со старым космосом.

Ответ 2



Для решения такой задачи используется паттерн Memento (Хранитель). С его помощью Вы сохраняете состояние объекта перед началом операций. А в случае ошибки - записываете сохраненное состояние обратно. Осталось только отловить наличие ошибки. Беспроигрышный вариант - через возвращаемые значения. Еще можно воспользоваться исключениями. Но в данном случае они не очень к месту, так как это вовсе даже не исключительная ситуация, а вполне себе игровая логика. Они только добавят лишние тормоза. И размажут логику по коду не хуже, чем это делает goto.

Ответ 3



Не совсем понятно, что именно вы хотите откатить назад. Состояние переменных, измененных в методе, в котором произошла ошибка? Если так, то самый простой путь - это сохранить состояние всех нужных вам переменных перед вызовом метода ВключитьЩиты(). После чего, в случае ошибки, использовать вернуться к сохраненному состоянию.

Ответ 4



Я видел такую методику. Перед выполнением критичного кода, делаем копию всех переменных/объектов. Потом выполняем код. Если произошла ошибка, то просто восстанавливаем все назад. В С++ это можно сделать удобно, если написать правильные конструкторы копирования. В чистом Си я видел другой прием. Хитрая череда goto. Где то так. ПриВыходеИзВарпРежима() { if (!ДобавитьКораблиВКосмос()) { goto space; } if (!УстановитьУНихДефолтныеКоординаты()) { goto coord; } ... if (!ВключитьЩиты()) { goto guard; } if (!ОбнаружитьПротивников()) {goto anemi;} return ok; anemi: ПровестиДиагностикуВторогоУровня(); guard: ПерекалиброватьЩиты(); coord: УбратьДефолтныеКоординаты(); space: УбратьКораблиСКосмоса(); return bad; } То есть, если происходит ошибка - переходим по goto на обработчики "откатки в к нормальному состоянию". Да, я вижу, что Вы не хотите писать "обратные шаги", но иногда без них просто невозможно.

воскресенье, 8 марта 2020 г.

Как зациклить функцию, чтобы ее выполнение являлось гарантированным

#python #python_3x #циклы #исключения


Программа обращается к сети интернет для получения данных из списка исходных ID.
Ночью интернет частенько отваливается, что приводит к ошибке программы.
Как добиться безаварийной работы программы, чтобы элемент списка был обработан гарантированно?
Что нужно написать после except:, чтобы программа повторно обращалась к блоку try:,
покуда его не обработает с корректным получением результата y.

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def Scan_data(x):
    try:
        y = <..Обращение к интернету..>
    except:
        time.timesleep(60)
        # <..Тут не знаю что написать..>
    return y

for i in data:
    Scan_data(i)

    


Ответы

Ответ 1



Вот где то так. Я также добавил счетчик неуспешних попыток. Если было больше 1000 попыток, то не ведомо, если ли смысл продолжать. data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] def Scan_data(x): y = -1 happy = false tr = 0 # сколько раз пробовали while (!happy and tr < 1000): try: y = <..Обращение к интернету..> happy = true # как только поняли, что все ок except: time.timesleep(60) tr = tr + 1 # кол-во попыток print("попытка номер ", tr) return y for i in data: Scan_data(i)

Ответ 2



Я бы написал так def Scan_data(x): try: y = ... return y except: return None for i in data: while Scan_data(i) is None: time.timesleep(60)

Ответ 3



Примените рекурсию: def Scan_data(x): try: y = <..Обращение к интернету..> except: time.timesleep(60) Scan_data(x) return y

суббота, 7 марта 2020 г.

блоки try-catch. Обработка исключений

#java #исключения #try_catch


правильно ли я понимаю, что если первое закрытие выбросит ошибку, то остальные даже
и не вызовутся? подскажите как ПРАВИЛЬНО исправить, а главное почему именно так!

private void closeStatement() throws DaoException, SQLException {
    try {
        getByIdStmt.close();
    } catch (Exception e) {
        throw new DaoException("Error! getByIdStmt is not closed");
    }
    try {
        updateStmt.close();
    } catch (Exception e) {
        throw new DaoException("Error! updateStmt is not closed");
    }
    try {
        addStmt.close();
    } catch (Exception e) {
        throw new DaoException("Error! addStmt is not closed");
    }
    try {
        deleteStmt.close();
    } catch (Exception e) {
        throw new DaoException("Error! deleteStmt is not closed");
    }
    System.out.println("Statement close");
}

    


Ответы

Ответ 1



Возможно, так: private void closeStatement() throws DaoException, SQLException { ArrayList err = new ArrayList(); try { getByIdStmt.close(); } catch (Exception e) { err.add("getByIdStmt"); } try { updateStmt.close(); } catch (Exception e) { err.add("updateStmt"); } try { addStmt.close(); } catch (Exception e) { err.add("addStmt"); } try { deleteStmt.close(); } catch (Exception e) { err.add("deleteStmt"); } if (!err.isEmpty()) { throw new DaoException("Error! "+String.join(", ", err)+" not closed"); } System.out.println("Statement close"); } Поочерёдно пытаемся закрыть все statement, неудачи собираем. Потом выбрасываем общее для всех ошибок исключение.

Почему исключения в асинхронном коде считаются необработанными?

#c_sharp #исключения #async_await


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

private static bool Failed = false;

static void Main(string[] args)
{
  TaskScheduler.UnobservedTaskException += (o, a) => { throw a.Exception; };
  ChapterDownload().Wait();
}

public static async Task ChapterDownload()
{
  try
  {
    var pages = Enumerable.Range(0, 20);
    var pTasks = pages.Select(page =>
    {
      return PageDownload()
      .ContinueWith(t => Console.WriteLine("{0}{1}", t.Status, t.IsFaulted));
    });
    await Task.WhenAll(pTasks.ToArray());
  }
  catch (Exception ex)
  {
    //
  }
}

public static async Task PageDownload()
{
  if (Failed)
    throw new Exception("boom");

  try
  {
    // var file = await new WebClient().DownloadDataTaskAsync(new Uri(@"http://example.com"));
    var file = await DownloadFile(new Uri(@"http://example.com"));
    if (file != null)
      throw new Exception("Restart download, downloaded file is corrupted");
  }
  catch (Exception ex)
  {
    Failed = true;
    await PageDownload();
  }
}

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

  try
  {
    response = await request.GetResponseAsync();
    using (var memory = new MemoryStream())
    {
      await response.GetResponseStream().CopyToAsync(memory);
      result = memory.ToArray();
    }
  }
  catch (System.Exception ex)
  {
    return null;
  }
  if (response.ContentLength == result.LongLength)
    return result;
  return null;
}


Проблема в чём - в текущем виде, происходит UnobservedTaskException, с текстовкой
boom, т.е. явно моё. При этом, стоит мне заменить скачивание с самопала на webclient
- UnobservedTaskException больше не возникает. В чём разница?
    


Ответы

Ответ 1



Чтобы исключения задачи (Task) считались обработанными и не вызывали событие TaskScheduler.UnobservedTaskException необходимо их перевыбросить, например t.Result, t.Wait() или t.GetAwaiter().GetResult(), или просмотреть, обратившись с свойству Exception: t.Exception. В Вашем коде проблема возникает потому, что вы ждёте завершение не самих исходных задач, а их продолжений. В то время как продолжения не делают ничего, чтобы обработать исключения исходных задач. В результате исключения, возникшие в исходных задачах, остаются необработанными.

пятница, 6 марта 2020 г.

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

#ассемблер #исключения


Мне нужно написать программу на ассемблере х64 в VS 2017, Windows 10.
Нужно ввести в окошко строку, а программа должна заменить * на пробелы и вывести
в строку результат.

Пример нашей преподавательницы запускается, но при нажатии на кнопку RUN происходит
исключение по адресу.

Не знаете, в чем проблема? Сама она тоже не знает. Код ниже. 
Заранее спасибо.

ExitProcess PROTO 
DlgProc PROTO :QWORD,:QWORD, :QWORD, :QWORD
PostQuitMessage PROTO
GetModuleHandleA PROTO
DialogBoxParamA PROTO
GetDlgItemTextA PROTO
SendDlgItemMessageA PROTO

include windows.inc

.data
DName db "dial",0
hinst dq ?
result_buffer db 20 dup ("0")

hwnd dq 0
uMsg dq 0
wParam dq 0
lParam dq 0

.code
main proc
 sub rsp, 56

 mov rcx,0
 call GetModuleHandleA 
 mov hinst,rax

 mov rcx,hinst
 lea rdx, DName
 mov r8,0
 lea r9,DlgProc
 mov     dword ptr [rsp+32], 0
 call DialogBoxParamA
 mov rcx,0
 call ExitProcess 
 main endp

DlgProc proc q:QWORD,w:QWORD, e:QWORD, r:QWORD
  cmp edx, WM_CLOSE ;10h   
  je Exit_Button

  cmp edx, WM_COMMAND   ;111h   
  je go_button
  jne End_it 

 go_button:
 cmp r8,4
 je Exit_Button
 cmp r8,3
 je next
 jne End_it

 next:
    mov hwnd,rcx
    mov uMsg,rdx
    mov wParam,r8
    mov lParam,r9

    mov rdx,1
    lea r8,result_buffer
    mov r9,20
    call GetDlgItemTextA

    mov rcx,  hwnd
    mov rdx,  uMsg
    mov r8,   wParam
    mov r9,   lParam

        mov hwnd,rcx

    mov rcx,rax
    lea rsi,result_buffer
    m1:
     cmp byte ptr [rsi],'*'
     je m2
     jne m3
     m2:
      mov byte ptr [rsi],' '
     m3:
      inc rsi
     loop m1    

        mov rcx,hwnd

    mov rdx,2
    mov r8,LB_DELETESTRING  ;  182h - LB_DELETESTRING
    mov r9,0
    lea rsi,result_buffer
    mov qword ptr [rsp+32],rsi 
    call SendDlgItemMessageA

        mov rcx,  hwnd
        mov rdx,  uMsg
        mov r8,   wParam
        mov r9,   lParam

    mov rdx,2
    mov r8,LB_ADDSTRING  ;  182h - LB_DELETESTRING
    mov r9,0
    lea rsi,result_buffer
    mov qword ptr [rsp+32],rsi 
    call SendDlgItemMessageA
    jmp End_it

Exit_Button:
    mov rcx,0
    call PostQuitMessage

End_it:    
    mov rax,0
    ret
DlgProc endp 
end


Файл RSRC.RC

dial DIALOG 0, 0, 309, 95
CAPTION "mY dialog"
BEGIN
    EDITTEXT 1, 48,7,222,14
    ListBox 2,48,24,222,14
    PUSHBUTTON "Run", 3, 167,41,50,14  
    PUSHBUTTON "Exit",4, 220,41,50,12
    LTEXT "Input string:", -1, 7,10,40,8    
END 


Ошибка в отладчике:


    


Ответы

Ответ 1



В коде две проблемы, приводящих к вылетам: Не проверяется что введена пустая строка (значение, возвращенное из GetDlgItemTextA, равно нулю), из-за чего при первом попадании на инструкцию loop 0 в регистре rcx превращается в -1, получаем "бесконечный" цикл и попытку записи за пределы секции данных. В начале процедуры DlgProc нужно выделить на стеке место под параметры процедуры SendDlgItemMessageA, при выходе из DlgProc стек восстановить. Из-за того что это не выполнено, скорее всего при выполнении SendDlgItemMessageA (которое происходит только при нажатии кнопки "Run") происходит затирание каких-то данных вызывающего кода, при выходе из процедуры происходит вылет. Вот рабочий код, адаптированный под fasm, с моими замечаниями: include 'win64a.inc' format MS64 COFF public main extrn PostQuitMessage extrn SendDlgItemMessageA extrn GetModuleHandleA extrn GetDlgItemTextA extrn DialogBoxParamA extrn ExitProcess section '.data' readable writeable DName db "dial",0 result_buffer db 20 dup ("0") section '.code' code readable executable main: sub rsp, 5*8 ; место под 5 параметров для вызова DialogBoxParamA mov rcx,0 call GetModuleHandleA mov rcx, rax ; [hinst] mov rdx, DName mov r8, 0 mov r9, DlgProc mov dword [rsp+32], 0 call DialogBoxParamA mov rcx,0 call ExitProcess proc DlgProc hwnd, uMsg, wParam, lParam ; место под параметры на стеке выделено как раз для того, ; чтобы можно было сохранить там значения тех же параметров из регистров mov [hwnd], rcx mov [uMsg], rdx mov [wParam], r8 mov [lParam], r9 sub rsp, 5*8 ; резервируем на стеке место под 5 параметров для вызова SendDlgItemMessageA cmp edx, WM_CLOSE ; 10h je Exit_Button cmp edx, WM_COMMAND ; 111h je go_button jne End_it go_button: cmp r8,4 je Exit_Button cmp r8,3 ; je next ; лишняя проверка jne End_it ; next: mov rdx,1 mov r8, result_buffer mov r9,20 call GetDlgItemTextA ; Не нужно постоянно гонять параметры между памятью и регистрами ; mov rcx, hwnd ; mov rdx, uMsg ; mov r8, wParam ; mov r9, lParam ; mov hwnd,rcx test rax, rax ; не забываем проверить, что введена не пустая строка jz End_it mov rcx, rax mov rsi, result_buffer m1: cmp byte [rsi], '*' ; je m2 ; лишняя проверка jne m3 ; m2: mov byte [rsi], '+' ; заменяю на +, чтобы была заметнее разница m3: inc rsi loop m1 mov rcx, [hwnd] mov rdx, 2 ; ListBox mov r8, LB_DELETESTRING ; 182h - LB_DELETESTRING mov r9, 0 mov qword [rsp+32], result_buffer ; mov qword ptr [rsp+32], offset result_buffer для masm call SendDlgItemMessageA ; mov rcx, hwnd ; mov rdx, uMsg ; mov r8, wParam ; mov r9, lParam mov rcx, [hwnd] mov rdx,2 mov r8, LB_ADDSTRING mov r9,0 mov qword [rsp+32], result_buffer call SendDlgItemMessageA jmp End_it Exit_Button: mov rcx,0 call PostQuitMessage End_it: mov rax,0 add rsp, 5*8 ; восстанавливаем стек ret endp Для компиляции нужен fasm, а также GoRC и GoLink (см. The Go tools for Windows + Assembler, к языку Go не имеют никакого отношения). Для отладки использовал x64dbg. Собственно, компиляция: fasm dialog.asm gorc /machine X64 rsrc.rc golink dialog.obj rsrc.res user32.dll kernel32.dll /entry main Ну и наконец скриншот (состояние после ввода текста и нажатия на кнопку Run): P.S. "криво расставленные котлы" авторские, rsrc.rc я в неизмененном виде взял из вопроса.

четверг, 5 марта 2020 г.

перехват исключения производным классом

#cpp #ооп #классы #исключения


Объясните почему при вводе 0  и выбросе исключения Base, оно не отлавливается блоком
Derived1?

int main()
{
    int number = 0;

    for (;;)
    {
        try
        {
            cin >> number;
            cout << number << " ";

            switch (number)
            {
                case 0:
                    throw Base();

                case 1:
                    throw Derived1();

                case 2:
                    throw Derived2();
            }
        }
        catch (Derived1 /*exception*/)
        {
            cout << "Exception of derived class" << endl;
        }
        catch (Base /*exception*/)
        {
            cout << "Exception of Base class" << endl;
        }
    }

    return 0;
}

    


Ответы

Ответ 1



Потому что Base не является Derived, а вот Derived является Base. Вот если бы вы написали (кстати, перехватывайте исключения по ссылке, иначе столкнетесь со срезкой) catch (Base& /*exception*/) { cout << "Exception of Base class" << endl; } catch (Derived1& /*exception*/) { cout << "Exception of derived class" << endl; } то тут и Base, и Derived ловились бы первым блоком - Base.

среда, 4 марта 2020 г.

С++ аварийный выход из конструктора

#cpp #исключения #конструктор


Можно ли во время создания объекта, при сбое в конструкторе завершить его без создания
объекта и оповестить об этом программу?  

serialPort::serialPort(LPSTR serialName)
{
    HANDLE  serialDesc;

    // COM openning
        serialDesc = CreateFile(serialName, GENERIC_READ | GENERIC_WRITE,
                                0, NULL, OPEN_EXISTING, 0, NULL);
        if (serialDesc == INVALID_HANDLE_VALUE)
        {
             // сделать что-то, что бы объект не создавался и оповестить программу
        }
}


Мне в голову приходят только передача указателя на внешнюю структуру с последующей
проверкой, или содержать дополнительную переменную в классе, которую так же придется
проверять извне.
Еще прочитал про try-throw-catch, но не могу найти явную информацию как он работает
в конструкторе.
    


Ответы

Ответ 1



Вообще-то генерация исключения в конструкторе и означает, что объект не создан... Само собой, все действия по очистке уже сделанного в конструкторе вы должны выполнить самостоятельно.

Ответ 2



я обязан их закрыть в ручную, иначе они будут считаться открытыми, а их дескрипторы утеряны? закрывать обязательно нужно, так как во первых будет потеря ресурсов - файловых дескрипторов + памяти. Если ваша программа работает 24х7х365, то если эти объекты создаются постоянно, то ваша программа может аварийно завершиться. Вопрос как? Если вы сгенерируете исключение в конструкторе, а файл - член этого класса? Стандартный подход 1) в конструкторе не делать инициализую переменных (например просто их нулить) а делать это в отдельном методе, например init, которое может сгенерировать исключение, которое может быть обработано сверху.В этом подходе, у вас будет вызван деструктор, который будет освобождать ресурсы 2) Использовать наследование 3) Использовать умные указатели для членов класса, тут все понятно Пример 1 решения class Foo { FILE* f; public: Foo() { f = 0; } virtual ~Foo() { if ( 0 != f) fclose(f); } bool init() { f = fopen("file.txt", "+rw"); if ( !f ) { throw 1; } fseek(f, 0, SEEK_END); size_t fileSize = ftell(f); if (fileSize < 100) return false; return true; } }; int main() { { Foo f; try { f.init(); } catch (int& e) { // Обработка исключения, о ресурсах не беспокоимся, деструктор // класса Foo все сделает } } } Второй способ основан на том, чтобы поместить f в базовый класс и и там освобождать ресурсы ( при исключении в конструкторе порожденного класса, вызовется и деструктор базового) Пример 2 Решения Перенесем метод инит в конструктор порожденного класса class Foo { FILE* f; public: Foo() { f = 0; } virtual ~Foo() { if ( 0 != f) fclose(f); } }; class Delivery: public Foo { bool fileIsBig; public: Delivery(const char* fileName) { fileIsBig = true; f = fopen(fileName, "+rw"); if ( !f ) { throw 1; } fseek(f, 0, SEEK_END); size_t fileSize = ftell(f); if (fileSize < 100) fileIsBig = false; } virtual ~Delivery() { } }; int main() { try { Delivery d("myfile"); } catch (int& e) { // Обработка исключения из конструктора, ресурсы будут освобождены } }

вторник, 25 февраля 2020 г.

Как узнать об аварийном закрытии программы на c#?

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


Тестирую программу, не могу везде написать try... except. Можно ли как-то при аварийном
закрытии программы вывести сообщение пользователю типа "Аварийное закрытие программы,
запомните, пожалуйста, Ваши последние действия"?    


Ответы

Ответ 1



Если вы хотите перехватывать все необработанные исключения в вашем приложении, подпишитесь на AppDomain.UnhandledException [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlAppDomain)] public static void TestSomeException() { AppDomain domain = AppDomain.CurrentDomain; domain.UnhandledException += new UnhandledExceptionEventHandler(SomeExceptionHandler); throw new Exception("Тестовое исключение"); } static void SomeExceptionHandler(object sender, UnhandledExceptionEventArgs args) { Exception e = (Exception) args.ExceptionObject; // TODO: Обработать исключение } public static void Main() { TestSomeException(); }

Как отловить exception если он обработан внутри и не пробрасывается? (Java)

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


Суть проблемы следующая:

Есть класс, с большим количеством разных методов, с большим количеством вложенностей
(которые тоже полны методов). Многие из них могут вызвать эксепшены. Эксепшены не пробрасываются,
почти всегда обрабатываются на месте (выше обозначенного класса практически ни один
не выбрасывает).

Как с минимальным вмешательством в код выловить информацию о всех исключениях (вместе
со стеком)? 
Возможно обернуть в другой класс? Если да, то как сделать что бы гарантированно я
мог узнать о всех эксепшенах? 
    


Ответы

Ответ 1



Если нет возможности изменить исходный код или это очень сложно, то можно использовать модификацию байткода (bytecode instrumentation). Например с помощью библиотеки ASM и метода MethodVisitor.visitTryCatchBlock найти все блоки try\catch и модифицировать их таким образом, чтобы все исключения перед обработкой сохранялись в некоторую структуру данных или выводились в лог.

понедельник, 24 февраля 2020 г.

Исключение при вызове метода MoveTo у экземпляра DirectoryInfo

#c_sharp #исключения #файловая_система #io




DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory());
string s = @"C:\";
dir.MoveTo(s);


Текст в s для примера. Какой бы путь не вставил, исключение всё равно вылетает.

Исключение:


  "Процесс не может получить доступ к файлу, так как этот файл занят другим процессом."



Что не так? Вроде простейший код.



UPD 24.03.16. 17.45.

Если кому-нибудь надо, могу выложить алгоритм поиска вверх по папкам.
Ищет в текущей папке нужную папку/файл/файлы с заданным расширением. Если не найдено,
поднимается на уровень выше и ищет там, ещё выше и т. д. Опционально - поиск в подпапках,
максимальное количество уровней "подъёма" вверх, название искомой директории, маска
поиска файлов (одного файла или файлов с заданным расширением), папка начала поиска.
На выходе - путь к найденной папке. Поиск ленивый - до первой найденной папки, в
которой есть искомые файлы.
    


Ответы

Ответ 1



Из текущей папки запущенно приложение, вот и не удается переместить её. По крайней мере - это то что приходит в голову в первую очередь.

суббота, 15 февраля 2020 г.

Обработка исключений с++

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


Как обработать исключение, которое возникает при попытке инициализировать значение
за  пределами массива, или при чтении из-за его пределов. Пробовал в catch писать "exception
e", но оно не ловит.
    


Ответы

Ответ 1



если это встроенный c++ массив то можно ставить ассерты перед каждым доступом к его элементу - #define array_size 100 int v[array_size]; int i = 100; assert(i >= 0 && i < array_size); // бросит исключение v[i] = 123; если у вас std::vector то как уже сказали в комментариях - воспользоваться функцией членом std::vector::at которая гарантированно бросит исключение при выходе за пределы вектора. std::vector v(5); v.at(5) = 1.0f; // бросит исключение

Ответ 2



На самом деле в случае выхода за границу массива происходит исключение уровня операционной системы, после чего программе присылается сигнал об этом, и она падает (в комментариях меня поправят, если это не так, но общий смысл такой). Корректная реакция на такие сигналы - завершение программы, поскольку они вызываются недопустимыми операциями и после них программа может не находится в консистентном состоянии. Однако, если очень хочется, то можно такую ошибку отловить и продолжить работу (при этом следует очень хорошо понимать зачем и почему понадобилось так делать, поскольку затея эта целиком и полностью не здоровая). В основе обработки таких ошибок лежат функции setjmp и longjmp, позволяющие сохранить состояние программы и вернутся к сохраненному ранее состоянию. Так же нужно отловить сигнал операционной системы, что делается везде по-своему (Для POSIX-систем достаточно обычных обработчиков сигналов, для windows стоит использовать Structured Exception Handling или Vectored Exception Handling). Итак. Регистрируем обработчик (пример для POSIX). struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = signalHandler; sigaction(SIGSEGV, &act, 0); В обработчике сигнала возвращаемся к сохраненному состоянию и передаем не нулевое значение в качестве возвращаемого для setjmp: jmp_buf env; static void signalHandler(int signum) { longjmp(env, 1); } Перед входом в опасный блок сохраняем состояние программы: try { if (setjmp(env)) throw std::runtime_error("Some shit"); // Действия, которые могут уронить программу Ловим брошенное исключение: } catch (std::runtime_error & e) { // Что-то делаем } Как оно работает. Функция setjmp при сохранении возвращает 0 - исключение не кидается. Если дальше происходит ошибка, то longjmp возвращает выполнение к setjmp, которое в этот момент вернет 1 - бросится исключение, которое можно обработать. Данный пример дает лишь общее представление о логике обработки таких ошибок, для использования его на практике, его стоит доработать. Более полное описание чего куда и зачем можно найти тут: Habrahabr: Обработка многократно возникающих SIGSEGV-подобных ошибок

пятница, 14 февраля 2020 г.

JAVA: Проверка входных данных, Рекурсия, Исключения

#java #исключения #рекурсия #ввод


Такая проблемка возникла:

Нужно чтобы пользователь вводил число.
Если число не int (например буква), то должно выбрасываться исключение. 
А я как программист должен сделать так, чтобы программа не завершалась и не "пропускала"
пользователя дальше, пока он не введёт корректное значение.

Пробовал через рекурсию. Но тк в стеке творится неведомая **** (на скрине), думаю
этот способ не лучший. 

Посоветуйте что сделать

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




    


Ответы

Ответ 1



Одно из решений - цикл while. boolean check = true; int fN; while(check) { try { Scanner sc = new Scanner(System.in); check = false; //ставим false, что бы не вводить больше данные System.out.println(...); fN = sc.nextInt(); } catch(Exception ex) { System.out.println(...); check = true; //если вышло исключение, ставим обратно true, что бы опять вводить данные } }

Ответ 2



Нормальный способ. Просто разделите вывод сообщения об ошибке и рекурсивный вызов функции небольшой задержкой, типа Thread.sleep(20);

Ответ 3



Может как-то так попробовать: public static void main(String[] args) throws Exception { System.out.println(InputFirstNumber()); } static int InputFirstNumber() throws IOException { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) { try { while (true) { System.out.println("Input first number!"); return Integer.parseInt(bufferedReader.readLine()); } } catch (NumberFormatException ex) { System.err.println("\tError!"); System.err.println("\tIncorrect data!"); return InputFirstNumber(); } } }

Ответ 4



Решил таким способом (скрин) В итоге от try и catch пришлось отказаться. Хотя я думаю если добавить Thread.sleep(20); (как советовал тут один человек) То мой изначальный код с рекурсией, будет работать правильно Всем спасибо за помощь) Поставил лайки, но тк у меня рейтинг ниже 15, они кажется не отобразятся......

Зачем нужен флаг /EHsc для MSVC компилятора?

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


Зачем нужен флаг /EHsc для MSVC компилятора? Недавно увидел его в конфиге, почитал
доки, но так и не понял о каких синхронных/асинхронных исключениях идёт речь. Это расширение
компилятора или всё соответствует стандарту C++?
    


Ответы

Ответ 1



Примерно (не в точных деталях, просто для примерного понимания) так - если использовать /EHa, то это и есть расширение VC++, которое всякие неприятности типа деления на ноль и т.п. трансформирует в исключения С++ и перехватывает их как исключения. /EHs - это перехват только стандартных исключений, ну а добавление c - считать, что функции, объявленные как extern "C", исключений не генерируют. Попробуйте скомпилировать что-то вроде int main(int argc, const char * argv[]) { try { int x = 5; int y = 0; x /= y; cout << " x = " << x << endl; } catch(...) { cout << "Exception!\n"; } } с тем и другим ключом и посмотреть на результат выполнения. Мое личное отношение к /EHa - не люблю нестандартные расширения компиляторов в принципе. Тем более сами MS пишут - Specifying /EHa and trying to handle all exceptions by using catch(...) can be dangerous. In most cases, asynchronous exceptions are unrecoverable and should be considered fatal. Catching them and proceeding can cause process corruption and lead to bugs that are hard to find and fix. Еще раз - это не более чем краткое (a la научпоп :)) описание, не претендующее на точность и тем более на полноту.

Ответ 2



В Windows существует механизм структурированных исключений, Structured Exception Handling (SEH). Этот механизм используется при обработке различных исключительных ситуаций, таких как деление на 0 или ошибка доступа. Компилятор VC++ (и другие) реализует исключения C++ поверх этого механизма. То бишь он присутствует всегда, однако это детали реализации. Исключения SEH там называются асинхронными исключениями, а исключения С++ синхронными. Так вот, опция /EH задает, как компилятору работать с этими двумя видами исключений. Она принимает один или более модификаторов: a - перехват синхронных и асинхронных исключений в C++ блоке try{}catch(...){} - это нестандартное поведение s - перехват только синхронных исключений в C++ блоке try{}catch(...){} - такое поведение соответствует стандарту с (используется совместно с s) - это директива оптимизации, указывающая компилятору предположить, что "C" функции никогда не кидают синхронных исключений r - это директива анти-оптимизации, запрещающая компилятору устранять избыточные проверки при работе с функциями, помеченными noexcept (то бишь предположить, что они все равно могут выкинуть исключение) Еще компилятор поддерживает нестандартный синтаксис __try{}__except{} и __try{}__finnaly{} для работы с SEH исключениями, однако это от опции /EH не зависит.

воскресенье, 9 февраля 2020 г.

Tочное значение термина “unhandled exception”

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


Какие исключения относятся к необработанным? Те, которые не были обёрнуты в try-catch?
 Исключение, которое создаётся в методе, объявленном с throws Exception, относится
к обработанным? Т.е. это уже "handled exception"? 

И следующее исключение будет "handled" или "unhandled"?

public static void main(String[] args) throws Exception {
        throw new Exception("ololo");
}

    


Ответы

Ответ 1



Исключения обрабатываются в соответствующем (внешнем, подходящем по типу) блоке catch. Соответственно, обработанное исключение — то, которое отлавливается одним из блоков catch, а необработанное — то, которое не отлавливается. В спецификации Java конкретное определение «необработанное исключение» я не нашел. Вместо этого определяются перехваченные исключения и точно описано, что произойдет с неперехваченными исключениями: Chapter 11. Exceptions. ... During the process of throwing an exception, the Java Virtual Machine abruptly completes, one by one, any expressions, statements, method and constructor invocations, initializers, and field initialization expressions that have begun but not completed execution in the current thread. This process continues until a handler is found that indicates that it handles that particular exception by naming the class of the exception or a superclass of the class of the exception (§11.2). If no such handler is found, then the exception may be handled by one of a hierarchy of uncaught exception handlers (§11.3) - thus every effort is made to avoid letting an exception go unhandled. ... 11.3. Run-Time Handling of an Exception ... If no catch clause that can handle an exception can be found, then the current thread (the thread that encountered the exception) is terminated. Before termination, all finally clauses are executed and the uncaught exception is handled according to the following rules: - If the current thread has an uncaught exception handler set, then that handler is executed. - Otherwise, the method uncaughtException is invoked for the ThreadGroup that is the parent of the current thread. If the ThreadGroup and its parent ThreadGroups do not override uncaughtException, then the default handler's uncaughtException method is invoked. Глава 11. Исключения. ... В процессе генерации исключения JVM немедленно поочередно завершает все выражения, инструкции, вызовы методов и конструкторов, инициализаторы и выражения инициализации полей, которые были начаты, но не завершены в текущем потоке. Этот процесс продолжается до тех пор, пока не будет обнаружен обработчик, который указывает, что он обрабатывает это конкретное исключение, путем именования класса исключения или его суперкласса. Если такой обработчик не найден, то исключение может быть обработано одним из элементов иерархии обработчиков неперехваченных сообщений ($11.3). Таким образом, делается все возможное, чтобы не допустить возможности существования неперехваченного исключения. ... $11.3. Обработка исключений времени выполнения. ... Если конструкция catch, способная обработать сгенерированное исключение, не найдена, то текущий поток выполнения (поток, в котором сгенерировано исключение) завершается. Перед завершением выполняются все конструкции finally, а неперехваченное исключение обрабатывается в соответствии со следующими правилами. - Если текущий поток имеет установленный обработчик неперехваченных исключений, вызывается этот обработчик. - В противном случае для группы ThreadGroup, которая представляет собой родителя текущего потока, вызывается метод uncaughtException. Если ThreadGroup и ее родительские ThreadGroup не перекрывают uncaughtException, то вызывается метод uncaughtException обработчика по умолчанию. На практике фраза «unhandled exception» обычно употребляется в более узком контексте и относится не к JVM, а к какому-либо процессу/классу/методу. В этом случае имеется ввиду неожиданное исключение, которое выбрасывается в вызывающий код. Например: «исключения, не обработанные при разборе файла, должны быть отражены в журнале ошибок». Конструкция throws в объявлении метода не связана напрямую с перехватом/обработкой исключений. Если нужно как-то назвать перечисленные типы исключений, то скорее подойдет «объявленные типы исключений» («declared exception types»).

Ошибка при обращении к элементу массива

#c_sharp #массивы #исключения


При запуске программы

class Program
{
    static void Main()
    {
        string[] listNames = new string[4] { "Личный", "Рабочий", "Семейный", "Список
книг" };
        TodoList[] TodoLists = new TodoList[listNames.Length];
        for (int i = 0; i < TodoLists.Length; i++)
        {
            TodoLists[i].name = listNames[i];
        }
    }
}

class TodoList
{
    public string name;
    public string[] tasks;
}


получаю ошибку


  Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
     at dz.Program.Main(String[] args) in C:\Users\Yuri\source\repos\dz\dz\Program.cs:line 15

    


Ответы

Ответ 1



TodoList[] TodoLists = new TodoList[listNames.Length] лишь создаёт массив на listNames.Length элементов, в каждой ячейке которого лежит null. Вам сначала нужно проинициализировать элементы массива, чтобы с ними работать. TodoList[] TodoLists = new TodoList[listNames.Length]; for (int i = 0; i < TodoLists.Length; i++) { TodoLists[i] = new TodoList(); TodoLists[i].name = listNames[i]; }

Ответ 2



for (int i = 0; i < TodoLists.Length; i++) { TodoLists[i] = new TodoList() { name = listNames[i] }; }

Зачем нужно пробрасывать исключения?

#java #исключения #методы #обработка_ошибок


Зачем нужно пробрасывать исключения? Почему может быть нельзя из сразу обработать?
    


Ответы

Ответ 1



Чтобы обрабатывать ошибки централизованно. Вот допустим у Вас есть процесс, который разбирает определенные файлы и кладет их в базу данных. Файлы большие и сложные, поэтому логика разбита по сотне-другой классов, в каждом из которых по несколько методов. Драйвер базы данных предусматривает, что база может оказаться недоступна и все методы, которые работают с БД выбрасывают DatabaseKaputException. Т.к. разработчики драйвера фанатики, с садисткими наклонностями DatabaseKaputException — проверяемое исключение. Теперь, если все исключения обрабатывать на месте, то каждый метод, работающий с БД будет содержать try/catch: public Result someMethod() { try{ //получаем результат из БД } catch(DatabaseException de) { //а вдруг база упала? } } Проблема с таким подходом в том, что «низовые» классы понятия не имеют, что им делать если база умерла. В этом случае нужно перейти к самому верхнему классу и останавливать весь процесс. Поэтому если исключения не пробрасывать, то каждый метод должен будет определять какой-то код ошибки и передавать его вверх. public ResultWithException someMethod() { ResultWithException result; try{ //нормальный код result.setValue(...); } catch(DatabaseException de) { //Обрабатываем, возвращаем результат с ошибкой result.setError(...); } } И при каждом вызове метода нужно будет проверять не оборвался ли он: ResultWithException result = someObj.someMethod(); //а ты точно выполнился? if(result.getError()==null) { //передаем сообщение об ошибке наверх } В результате получится тот же механизм пробрасывания исключений, только самописный и более многословный. Пробрасывание позволяет обрабатывать исключение в том классе, который может принять адекватное решение по ошибке. При этом такого класса может и не быть. Если приложение работает с БД напрямую, то не всегда оправданно писать для него удобную систему обработки ошибок подключения. В таком случае исключение можно пробросить на самый верх. В принципе есть языки программирования (C, Rust) которые обрабатывают ошибки в основном через возвращаемые значения (коды ошибок, специальные типы). В Java для этого как правило используются исключения. Проверяемые исключения как раз и являются попыткой обязать разработчиков обрабатывать ошибки.

Исключения С++ и критические секции

#многопоточность #cpp #исключения


Корректен ли код?

bool started_cs = false;  
try {  
    // ...  
    EnterCriticalSection(&cs);  
    started_cs = true;  
    // ...  
    LeaveCriticalSection(&cs);  
    started_cs = false;  
} catch(SomeException&) {  
    if (started_cs)  
        LeaveCriticalSection(&cs);  
    // ...  
}
    


Ответы

Ответ 1



Код вполне корректен, но я бы посоветовал отказаться от переменных типа бул для синхронизации. Вы можете сделать, к примеру, так: try { // ... try { EnterCriticalSection(&cs); // ... LeaveCriticalSection(&cs); } catch(...) { // Ловим все исключения... LeaveCriticalSection(&cs); throw; // ...и пробрасываем их дальше. } // ... } catch(SomeException&) { // ... } А вообще, лучше сделать умную критическую секцию. Создать объект, в конструкторе вызвать EnterCriticalSection(&cs), а в деструкторе LeaveCriticalSection(&cs). Тогда при раскрутке стека критическая секция будет освобождена.

суббота, 8 февраля 2020 г.

Цикл внутри try…catch, как написать красивый код?

#c_sharp #исключения #vkontakte_api #captcha


Код примерно следующий:

    public void AntigateAuthorize(int appId, string email, string password, Settings
settings, string antigateKey)
    {
        var authorizationCompleted = false;

        try
        {
            Authorize(appId, email, password, settings);
            authorizationCompleted = true;
        }
        catch (CaptchaNeededException exception)
        {
            var captchaSid = exception.Sid;
            var image = GetImageFromUrl(exception.Img.AbsoluteUri);

            try
            {
                const int numberOfAuthorizationRetries = 5;
                var retryAuthorizationsCount = numberOfAuthorizationRetries;
                do
                {
                    var anticaptcha = new AntiCaptcha(antigateKey);
                    try
                    {
                        var captchaKey = anticaptcha?.GetAnswer(image);

                        if (captchaKey != null)
                        {
                            Authorize(appId, email, password, settings, captchaSid,
captchaKey);
                            authorizationCompleted = true;
                        }
                        else
                        {
                            Logger.Error("Ответ от сервера Antigate не получен.");
                        }
                    }
                    catch (CaptchaNeededException)
                    {
                        Logger.Trace("Капча распознана неверно.");
                        anticaptcha.FalseCaptcha();
                        retryAuthorizationsCount--;
                        if (retryAuthorizationsCount == 0)
                        {
                            Logger.Trace("Заданное количество попыток авторизации
исчерпано.");
                            throw;
                        }
                    }
                } while (!authorizationCompleted && retryAuthorizationsCount > 0);
            }
            catch (AntigateErrorException aee)
            {
                Logger.Error("Ошибка Antigate: {0}", aee.Message);
            }
        }
        catch (VkApiAuthorizationException)
        {
            Logger.Error("Не удалось авторизовать приложение. Неправильный логин
или пароль.");
        }
        catch (VkApiException)
        {
            Logger.Error("В процессе попытки авторизации произошла неизвестная ошибка.");
        }
    }

    public static Image GetImageFromUrl(string url)
    {
        using (var stream = ((HttpWebResponse)((HttpWebRequest)WebRequest.Create(url)).GetResponse()).GetResponseStream())
            return stream == null ? null : Image.FromStream(stream);
    }


Проблема в том, если обрабатывать ошибку CaptchaNeededException однократно, то всё
получается, а если в цикле - получается плохо и некрасиво.
    


Ответы

Ответ 1



Можно попробовать разбить на мелкие функции. Например, так: public void AntigateAuthorize( int appId, string email, string password, Settings settings, string antigateKey) { try { try { Authorize(appId, email, password, settings); } catch (CaptchaNeededException exception) { AuthorizeWithCaptcha(appId, email, password, settings, exception); } } catch (AntigateErrorException) { // ? } catch (VkApiAuthorizationException) { Logger.Error( "Не удалось авторизовать приложение. Неправильный логин или пароль."); } catch (VkApiException) { Logger.Error( "В процессе попытки авторизации произошла неизвестная ошибка."); } } Вспомогательные функции: private void AuthorizeWithCaptchaOnceImpl( int appId, string email, string password, Settings settings, CaptchaNeededException reason, AntiCaptcha anticaptcha) { var captchaSid = reason.Sid; var image = GetImageFromUrl(reason.Img.AbsoluteUri); try { var captchaKey = anticaptcha.GetAnswer(image); if (captchaKey == null) { Logger.Error("Ответ от сервера Antigate не получен."); throw new NoAntigateResponceException(); } Authorize(appId, email, password, settings, captchaSid, captchaKey); } catch (CaptchaNeededException) { Logger.Trace("Капча распознана неверно."); anticaptcha.FalseCaptcha(); throw; } } void AuthorizeWithCaptcha( int appId, string email, string password, Settings settings, CaptchaNeededException exception) { try { var anticaptcha = new AntiCaptcha(antigateKey); const int numberOfAuthorizationRetries = 5; CaptchaNeededException currentException = exception; for (int retry = 0; retry < numberOfAuthorizationRetries; retry++) { try { AuthorizeWithCaptchaOnceImpl( appId, email, password, settings, currentException, anticaptcha); return; } catch (CaptchaNeededException ex) { currentException = ex; } catch (NoAntigateResponceException ex) { // nothing to do, just retry } } throw new RetryNumberExceededException(); } catch (AntigateErrorException aee) { Logger.Error("Ошибка Antigate: {0}", aee.Message); throw; } } Заметьте, что я немного поменял логику. Например, после неудачного распознавания капчи теперь картинка меняется. Также, в результате превышения разрешённого количества попыток распознавания выбрасывается исключение (хотя, возможно, вы захотите вернуть bool из функции).

Ответ 2



Есть такая рекомендация: для упрощения читаемости кода блоки if/try/catch/finally/for/foreach/while должны содержать как можно меньше кода, в идеале -- вызов одного метода. Еще одна рекомендация: соблюдать минимальную вложенность кода. Например, у вас есть код: public void Foo() { try { FooInternal(); } catch (Exception1) { ... } catch (Exception2) { ... } } Цель этого метода: вызов некоторого кода и обработка ошибок. При этом метод не перегружен подробностями о том, какой код вызывается. В свою очередь метод FooInternal не будет перегружен информацией о возможных ошибках и их обработке. К вашему коду. Для внешнего блока try эти рекомендации у вас соблюдены: try { Authorize(appId, email, password, settings); authorizationCompleted = true; } Теперь осталось вынести содержимое catch (CaptchaNeededException exception) в отдельный метод. В нем -- в свою очередь вынести содержимое блоков try/catch в отдельные методы. От развесистой структуры блоков try/catch, к сожалению, совсем избавиться не удастся -- такова логика.

Ответ 3



Я бы вынес логику отдельно, т.к. в коде присутствуют другие методы не отвечающие за авторизацию, нет следования принципу SRP и не будет reuse. К примеру класс class Antigate Service, метод TryAuthenticate который будет возвращать bool и out AuthenticationResult. По крайней мере мне так видится, избавление в будущем от дублирования И вообще следует избегать ветвления кода(даже если это try/catch)

Ответ 4



Переделал сейчас примерно следующим образом (убрал для примера лишние catch): public void AuthorizeEx( int appId, string email, string password, Settings settings, int captchaRecognitionCount = MaxCaptchaRecognitionCount, AntiCaptcha antiCaptcha = null, CaptchaNeededException previousException = null) { if (captchaRecognitionCount < 0) return; var captchaSid = previousException?.Sid; var captchaKey = previousException == null ? null : antiCaptcha?.GetAnswer(GetImageFromUrl(previousException?.Img?.AbsoluteUri)); try { Authorize(appId, email, password, settings, captchaSid, captchaKey); } catch (CaptchaNeededException currentException) { AuthorizeEx(appId, email, password, settings, captchaRecognitionCount - 1, antiCaptcha, currentException); } } private static Image GetImageFromUrl([CanBeNull] string url) { if (url == null) return null; using (var stream = ((WebRequest.Create(url))?.GetResponse())?.GetResponseStream()) { return stream == null ? null : Image.FromStream(stream); } } Итог: код обработки капчи идёт отдельно, красиво. Из минусов - использование рекурсии, но так ли это страшно, если я ограничиваю её уровень? Если нехорошо, то как развернуть?