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

Как отмечает Питер Дунихо в своем комментарии, я был зациклен на красной сельди, когда мне следовало сосредоточиться на чем-то другом.

Когда я использую Symbol.Barcode.Reader и Symbol.Barcode.ReaderData в одной форме, они работают нормально. Я использую их, поскольку я документирую здесь.

Однако, когда я перехожу из одной формы, которая использует код сканирования штрих-кода, к другой, которая также делает, все Даллас освобождается. Я получаю следующее исключение во второй форме при запуске:

Symbol.Exceptions.OperationFailureException: SCAN_GetInterfaceParams
   at Symbol.Barcode.InterfaceParams.GetInterfaceParams()
   at Symbol.Barcode.InterfaceParams..ctor(Reader reader)
   at Symbol.Barcode.Actions.Enable()
   at HHS.frmFind.InitReader()
   at HHS.frmFind.textBoxScan_GotFocus(Object sender, EventArgs e)
   at System.Windows.Forms.Control.OnGotFocus(EventArgs e)
   at System.Windows.Forms.Control.WnProc(WM wm, Int32 wParam, Int32 lParam)
   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)
   at Microsoft.AGL.Forms.WL.SetVis(IntPtr hwnThis, BOOL fVis)
   at System.Windows.Forms.Control.set_Visible(Boolean value)
   at System.Windows.Forms.Form.ShowDialog()

Код сканирования штрих-кода между двумя формами идентичен, так что это не сам код (он отлично работает в первый раз, в первой форме).

Исключение возникает сразу во второй форме, когда вводится текстовое поле, настроенное для сканирования. Это событие GotFocus (), потому что текстовое поле получает фокус при отображении формы; OnGotFocus () вызывает InitReader (), который затем завершается ошибкой. InitReader () - это:

private bool InitReader()
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader");
    // If reader is already present then retreat
    if (this.barcodeReader != null)
    {
        return false;
    }

    // Create new reader, first available reader will be used.
    this.barcodeReader = new Symbol.Barcode.Reader();

    // Create reader data
    this.barcodeReaderData = new Symbol.Barcode.ReaderData(
        Symbol.Barcode.ReaderDataTypes.Text,
        Symbol.Barcode.ReaderDataLengths.MaximumLabel);

    // Create event handler delegate
    this.barcodeEventHandler = this.BarcodeReader_ReadNotify;

    // Enable reader, with wait cursor
    this.barcodeReader.Actions.Enable();

    this.barcodeReader.Parameters.Feedback.Success.BeepTime = 0;
    this.barcodeReader.Parameters.Feedback.Success.WaveFile = "\\windows\\alarm3.wav";

    // Attach to activate and deactivate events
    this.Activated += ReaderForm_Activated;
    this.Deactivate += ReaderForm_Deactivate;

    return true;
}

Обсуждаемые объекты находятся в Symbol.Barcode.dll от Motorola. Они объявлены в виде примерно так:

private Symbol.Barcode.Reader barcodeReader;
private Symbol.Barcode.ReaderData barcodeReaderData;

Если я обойду первую форму, которая имеет тот же тип кода сканирования штрих-кода, и сразу перейду к этой форме, она не вылетит.

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

ОБНОВИТЬ

С этой регистрацией, добавленной в InitReader:

private bool InitReader()
{
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader");
    // If reader is already present then retreat
    if (this.barcodeReader != null)
    {
        return false;
    }

    // Create new reader, first available reader will be used.
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #2");
    this.barcodeReader = new Symbol.Barcode.Reader();

    // Create reader data
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #3");
    this.barcodeReaderData = new Symbol.Barcode.ReaderData(
        Symbol.Barcode.ReaderDataTypes.Text,
        Symbol.Barcode.ReaderDataLengths.MaximumLabel);

    // Create event handler delegate
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #4");
    this.barcodeEventHandler = this.BarcodeReader_ReadNotify;

    // Enable reader, with wait cursor
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #5");
    this.barcodeReader.Actions.Enable();
    this.barcodeReader.Parameters.Feedback.Success.BeepTime = 0;
    this.barcodeReader.Parameters.Feedback.Success.WaveFile = "\\windows\\alarm3.wav";

    // Attach to activate and deactivate events
    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #6");
    this.Activated += ReaderForm_Activated;
    this.Deactivate += ReaderForm_Deactivate;

    ExceptionLoggingService.Instance.WriteLog("Reached frmFind.InitReader #7");
    return true;
}

... Я вижу это в файле журнала после открытия формы Find и сбоя приложения (оно зависает примерно на 20 секунд перед исчезновением):

Date: 3/19/2009 11:43:38 PM
Message: Reached frmFind.InitReader

Date: 3/19/2009 11:43:38 PM
Message: Reached frmFind.I

... так что он почти мгновенно падает после этого блока кода:

if (this.barcodeReader != null)
{
    return false;
}

... так как он проходит только половину следующей строки регистрации, прежде чем грубо прервать себя.

ОБНОВЛЕНИЕ 2

Fejesjoco все еще может быть прав (в своем комментарии ниже), но я отправляю этот код регистрации как выставку "A":

public void WriteLog(string message)
{
    if (!HHSConsts.Logging) return;
    StringBuilder formattedMessage = new StringBuilder();
    formattedMessage.AppendLine("Date: " + DateTime.Now.ToString());
    formattedMessage.AppendLine("Message: " + message);
    _streamWriter.WriteLine(formattedMessage.ToString());
    _streamWriter.Flush();
}

Поток должен очищаться после записи каждой строки в журнал.

ОБНОВЛЕНИЕ 3

Отказ всегда очень последовательный относительно того, как долго он "зависает", прежде чем он падает; Я вижу, как курсор мигает в текстовом поле штрих-кода формы поиска, и он пульсирует около 20 раз (на самом деле я посчитал его в прошлый раз: 24).

Это также немного странно; в обновлении 2 я показал содержимое файла журнала, в котором все записи журнала были добавлены в метод InitReader. С закомментированными (кроме первого) файл журнала заканчивается:

Date: 3/20/2009 12:01:22 AM
Message: Reached frmFind.InitReader

Date: 3/20/2009 12:01:22 AM
Message: From application-wide exception handler: Symbol.Exceptions.OperationFailureException: SCAN_GetInterfaceParams
   at Symbol.Barcode.InterfaceParams.GetInterfaceParams()
   at Symbol.Barcode.InterfaceParams..ctor(Reader reader)
   at Symbol.Barcode.Actions.Enable()
   at HHS.frmFind.InitReader()

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

ОБНОВЛЕНИЕ 4

Actions.Enable?

Я был незнаком с этим, и когда я добавил "Действия". У меня есть выбор между Symbol.Barcode.Actions и Symbol.Generic.Actions.

Сначала я выбрал первый, но у него нет метода "Включить". Добавив его, я отругал его: "Ссылка на объект требуется для нестатического поля, метода или свойства Symbol.Generic.Actions.Enable()".

Затем я прокомментировал добавленное использование и ввел "Действия". снова, и на этот раз выбрал Symbol.Generic.Actions (и получил то же сообщение об ошибке для моих проблем).

Как я могу использовать Actions.Enable()?

ОБНОВЛЕНИЕ 5

C.Evenhuis: "некоторые события требуют повторного присоединения каждый раз, когда они происходят", вы подразумеваете это:

private void StartRead()
{
    // If we have both a reader and a reader data
    if ((this.barcodeReader != null) && (this.barcodeReaderData != null))
    {
        // Submit a read
        this.barcodeReader.ReadNotify += this.barcodeEventHandler;
        this.barcodeReader.Actions.Read(this.barcodeReaderData);
    }
}

private void StopRead()
{
    // If we have a reader
    if (this.barcodeReader != null)
    {
        // Flush (Cancel all pending reads)
        this.barcodeReader.ReadNotify -= this.barcodeEventHandler;
        this.barcodeReader.Actions.Flush();
    }
}

... (barcodeReader.ReadNotify прикреплен в StartRead и отсоединен в StopRead) или более необходим?

У меня также есть, в InitReader ():

this.Activated += ReaderForm_Activated;
this.Deactivate += ReaderForm_Deactivate;

... которые реализованы так:

private void ReaderForm_Activated(object sender, EventArgs e)
{
    // If there are no reads pending on barcodeReader start a new read
    if (!this.barcodeReaderData.IsPending)
    {
        this.StartRead();
    }
}

private void ReaderForm_Deactivate(object sender, EventArgs e)
{
    this.StopRead();
}

InitReader () вызывается из textBoxScan_GotFocus(); textBoxScan имеет фокус при отображении формы.

ОБНОВЛЕНИЕ 6

Что касается "явного закрытия классов Close() перед их Dispose()", я вызываю Dispose для двух вещей, Symbol.Barcode.Reader и Symbol.Barcode.ReaderData, и ни один из них не допускает вызова Close().

ОБНОВЛЕНИЕ 7

Это утверждение С. Эвенхейса: "нельзя включить двух (на переднем плане) читателей" заставило меня попробовать следующее:

private void FrmDelivery_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    this.barcodeReader.Actions.Disable();
}

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

2 ответа

Решение

Symbol Motorola Zebra использует устаревшую библиотеку C++ с устаревшей оболочкой.NET. Есть довольно много подводных камней (некоторые события требуют повторного присоединения каждый раз, когда они происходят, некоторые классы требуют явного Close() до тебя Dispose() их и т. д.).

Как вы, возможно, узнали, вы можете вызывать члены классов Symbol из нескольких потоков / форм, но вы не можете включить два (передних) считывателя, и некоторые свойства можно установить, только если не включены фоновые считыватели, и невозможно определить, включены ли фоновые программы чтения (например, DataWedge).

В нашей компании мы решили инициализировать сканер в Form.Activated событие и деинициализировать его в Deactivated событие. Затем, когда требуется сканирование, вызов Actions.Enable() сделал бы трюк. Настройка свойств читателя выполняется в try catch блоки:(

Довольно странно, что ваше приложение вылетает при Get InterfaceParams, я ожидаю, что установка их вызовет проблемы. Попробуйте ваше приложение с минимумом; не устанавливайте BeepTime или же WaveFile свойства.

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

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

Это будет намного более "твердым" и, следовательно, более легким в обслуживании.

Другие вопросы по тегам