Гость
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Вызов Callback из C-библиотеки / 9 сообщений из 9, страница 1 из 1
28.01.2017, 05:44
    #39393610
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
Описание из opal.dll

автор...
OpalMessageAvailableFunction m_messageAvailable; /**< If non-null then this function is called before
the message is queued for return in the
GetMessage(). See the
OpalMessageAvailableFunction for more details. */
...

'If non-null then this function is called before
'the message is queued for return in the
'GetMessage(). See the
'OpalMessageAvailableFunction for more details.
'IntPtr
m_messageAvailable As Long

/** Function called when a message event becomes available.
This function is called before the message is queued for the GetMessage()
function.

A return value of zero indicates that the message is not to be passed on
to the GetMessage(). A non-zero value will pass the message on.

Note that this function will be called in the context of different threads
so the user must take care of any mutex and synchonisation issues. If the
user subsequently uses the GetMessage() then the message will have been
serialised so that there are no multi-threading issues.

A simple use case would be for this function to send a signal or message
to the applications main thread and then return a non-zero value. The
main thread would then wake up and get the message using GetMessage.
*/
typedef int (*OpalMessageAvailableFunction)(
const OpalMessage * message /**< Message that has become available. */
);

В VB6 у меня под это дело некогда была вымучена такая хитрая конструкция:

Инициализация Callback-а отправляется в dll в следующем виде:

Код: vbnet
1.
2.
3.
4.
  ' m_messageAvailable As Long (IntPtr то бишь)

    If messageAvailable Then _
     .m_param.m_messageAvailable = FARPROC(AddressOf OpalMessageAvailableFunction)



Сама Callback - ф-ция реализована так:

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
Public Function FARPROC(ByVal pfn As Long) As Long
  
  'A dummy procedure that receives and returns
  'the return value of the AddressOf operator.
 
  'Obtain and set the address of the callback
  'This workaround is needed as you can't assign
  'AddressOf directly to a member of a user-
  'defined type, but you can assign it to another
  'long and use that (as returned here)
  'TextBoxAdd Form1.TextGetMes, CStr(pfn)
 
  FARPROC = pfn

End Function

Public Function OpalMessageAvailableFunction() As Long
  OpalMessageAvailableFunction = 1
  FormMain.TimerGetMes.Enabled = True
End Function



Из самой OpalMessageAvailableFunction насколько я понял сообщение забрать нельзя или очень нежелательно (другой поток?),
поэтому используем тик таймера.
Интервал таймера выставлен =1 миллисекунда
Ну и собственно он забирает сообщение как только так сразу (единственный поток не висит и таймер не долбит)
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
Private Sub TimerGetMes_Timer()
  Dim MesPtr As Long
  TimerGetMes.Enabled = False
  Do
    MesPtr = OpalGetMessage(hOpal, 0)
    If MesPtr <> 0 Then
      HandleMessages MesPtr
    Else
      Exit Do
    End If
  Loop
End Sub



Как это в VB.Net сделать?

Пока туплю, c делегатами вроде чуть игрался.
Задекларировать делегата
Создать New делегата AddressOf OpalMessageAvailableFunction
этого делегата запомнить глобальной переменной (чтоб мусорщик не съел) и бухнуть в m_messageAvailable (IntPtr).
??? путаюсь чего-то

Есть некие исходники-обертка C# под это дело.
Там есть такие конструкции.

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
    /// <summary>
    /// If non-null then this function is called before
    /// the message is queued for return in the
    /// GetMessage(). See the
    /// OpalMessageAvailableFunction for more details.
    /// </summary>
    public OpalMessageAvailableFunction MessageAvailable
    {
      get { return (OpalMessageAvailableFunction)Marshal.GetDelegateForFunctionPointer(m_opalParamGeneral.m_messageAvailable, typeof(OpalMessageAvailableFunction)); }
      set { m_opalParamGeneral.m_messageAvailable = Marshal.GetFunctionPointerForDelegate(value); }
    }

    public delegate int OpalMessageAvailableFunction([MarshalAs(UnmanagedType.LPStruct)] Opal_API.OpalMessage message); /**< Message that has become available. */   



Но чет не очень похоже на мою VB6-конструкцию, которую вымучивал долго но при этом работает как часы.
Сам автор .Net кода в тест-примере это никак не использует, и не факт что у него это вообще работает.
...
Рейтинг: 0 / 0
29.01.2017, 20:35
    #39394191
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
Вернулся к этому вопросу.
В принципе переписал VB6 код.

Код: vbnet
1.
2.
3.
4.
  Public Delegate Function OpalMessageAvailableFunction() As Integer ' Message that has become available.

  Public MessageAvailableFunction As OpalMessageAvailableFunction = _
   New OpalMessageAvailableFunction(AddressOf MyMessageAvailable) 'чтоб мусорщик не убил


Инициализируем CallBack так:
Код: vbnet
1.
        .m_param.m_messageAvailable = Marshal.GetFunctionPointerForDelegate(MessageAvailableFunction)



Ловим вот так:
Код: vbnet
1.
2.
3.
4.
5.
  Public Function MyMessageAvailable() As Integer
    Form1.TimerGetMes.Enabled = True
    Debug.Print("message")
    Return 1
  End Function


Form1 - это конкретная форма
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
  Public Form1 As frm1

  Sub main()
...
    Form1 = New frm1
    Application.Run(Form1)
  End Sub



Код таймера (.Interval=1(миллисекунда)) такой
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
  Private Sub TimerGetMes_Tick(sender As Object, e As EventArgs) Handles TimerGetMes.Tick
    TimerGetMes.Enabled = False
    Dim MesPtr As IntPtr
    Do
      MesPtr = OpalGetMessage(hOpal, 0)
      If MesPtr <> IntPtr.Zero Then
        'HandleMessages MesPtr
        Debug.Print(MesPtr)
        OpalFreeMessage(MesPtr)
      Else
        Exit Do
      End If
    Loop
    Debug.Print("Exit")
End Sub



MyMessageAvailable исправно ловит сообщения (пишет в Debug слово "message")
Но возникла проблема: таймер срабатывает только один раз, потом он выключается(из-под себя)
и больше уже не срабатывает.


Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
message
message
message
message
message
'очевидно истекла 1мс от прихода первого "message" и сработал тик таймера, выключаем таймер из-под себя и им же читаем накопленные сообщения
130428280
130468296
130470496
130527000
130469496
Exit 'сообщения закончились, тик таймера отработал
'приходят новые сообщения (может через несколько секунд, может через полминуты)
message
message
message
message
' и почему-то тик таймера больше не вызывается, хотя мы "включаем" его при каждом message



В VB6 это работало.
Что я не догоняю?
Почему Form1.TimerGetMes.Enabled = True повторно не срабатывает и не вызывает тика таймера?
...
Рейтинг: 0 / 0
29.01.2017, 20:47
    #39394195
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
Причем что интересно
нажимаем
Код: vbnet
1.
2.
3.
  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    MsgBox(TimerGetMes.Enabled)
  End Sub


Пишет True, а
Код: vbnet
1.
Private Sub TimerGetMes_Tick(sender As Object, e As EventArgs) Handles TimerGetMes.Tick

при этом не вызывается.
Как такое может быть?
...
Рейтинг: 0 / 0
29.01.2017, 20:54
    #39394197
VSVLAD
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
Дмитрий77,

Я бы вместо включения постоянно таймера в процедуре (а таймер может не успеть обработать сообщение, как придут опять в работу, т.е. его опять включат), сделать так:
1) В callback процедуре заносить приходящие сообщения в очередь Queue (ещё лучше, если это будет потокобезопасная очередь и сбор будет в отдельном потоке)
2) Таймер в форме может работать постоянно ~10 мс и если очередь не пуста, читать данные из неё

Сейчас сказать, почему таймер сломался сложно, возможно есть эксепшены, которые не выводятся и не рушат приложения. Посмотрите во вкладках "Вывод" и "Интерпретация", может там их найдёте.
...
Рейтинг: 0 / 0
29.01.2017, 21:09
    #39394204
VSVLAD
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
+ в шарповском варианте, у делегата есть параметр:
Код: vbnet
1.
public delegate int OpalMessageAvailableFunction([MarshalAs(UnmanagedType.LPStruct)] Opal_API.OpalMessage message);


а у вас без параметров:
Код: vbnet
1.
Public Delegate Function OpalMessageAvailableFunction() As Integer
...
Рейтинг: 0 / 0
29.01.2017, 21:34
    #39394217
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
VSVLADВ callback процедуре заносить приходящие сообщения в очередь Queue
CallBack процедура сообщения не читает.
Она только вызывается как сообщение приходит. Из нее надо вернуть 1 (принимаем сообщение и можем прочесть его через GetMessage) и посылает сигнал, что есть чего читать (в моем случае это включение таймера на Form1). А таймер уже вызывает GetMessage через 1 мс.

VSVLADа таймер может не успеть обработать сообщение, как придут опять в работу, т.е. его опять включат
Ничего страшного, тик таймера читает все сообщения(Do Loop, их уже может быть несколько) и по-любому его отключает в процедуре тика.

Вопрос в том, что включение таймера из CallBack срабатывает только один раз, т.е. .Enabled=true то он и дальше становится, а тиков не вырабатывает. В VB6 это все работало как часики.

VSVLAD2) Таймер в форме может работать постоянно ~10 мс и если очередь не пуста, читать данные из неё
Да так можно, и CallBack тогда вообще не нужен.
Но постоянно долбать таймер я не хочу, очень уж это неграмотно.
Есть разница между "постоянно долблю с задержкой 10мс" или "вызываю по необходимости с задержкой 1мс".

Можно наверно попробовать API-таймер с сохранением логики, дабы не гадать что там в .Net не так.
Еще, мне надо чтоб GetMessage() выполнялось и делало обработку в контексте потока Form1,
а вот в каком потоке вызывается CallBack (из которого я стартую таймер) в случае .Net это ХЗ.
...
Рейтинг: 0 / 0
29.01.2017, 21:44
    #39394224
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
VSVLAD+ в шарповском варианте, у делегата есть параметр:
Код: vbnet
1.
public delegate int OpalMessageAvailableFunction([MarshalAs(UnmanagedType.LPStruct)] Opal_API.OpalMessage message);


а у вас без параметров:
Код: vbnet
1.
Public Delegate Function OpalMessageAvailableFunction() As Integer


Да я в курсе.
Но тот первый вариант вызывает немедленный краш приложения (и в VB6 и в .Net), и автор "шарповского кода",
прописав эту хрень нигде в своем примере этим не пользуется, а тупо висит на потоке через GetMessage с ненулевым таймаутом.
а вот мой вариант как раз делает то что надо (я его когда то вымучил еще в VB6).
А надо:
1) вернуть 1
2) инициировать процедуру из которой вызовем GetMessage и воспользуемся результатом.
Само сообщение берется по любому через GetMessage а не из CallBack, это не противоречит документации.

Проблема с .Net-овским таймером, но вот с таймером ли.
...
Рейтинг: 0 / 0
29.01.2017, 23:08
    #39394263
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
Ну правильно,
если заменить
Код: vbnet
1.
2.
3.
4.
5.
  Public Function MyMessageAvailable() As Integer
    Debug.Print("message")
    Form1.TimerGetMes.Enabled = True
    Return 1
  End Function


на
Код: vbnet
1.
2.
3.
4.
5.
6.
  Public Function MyMessageAvailable() As Integer
     Debug.Print("message")
     SetTimer(Form1.Handle, New IntPtr(1), 1, Nothing)
    'Form1.StartTimerFromMessageAvailable()
    Return 1
  End Function


то оно на втором или третьем "message" (а первый-то проходит) начинает стрелять известную ошибку:
Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'frm1' не из того потока, в котором он был создан.
(в первом варианте с .Net-контролом не стреляет, но и не тикает)

Т.е. предположение что MyMessageAvailable стреляет в хз каких потоках верное, но ток. как лечить.
Invoke пробовал втыкать, но оно виснет мертво, видимо неправильно.
А в VB6 то в свой поток всегда стреляло, не догоняю пока.
...
Рейтинг: 0 / 0
30.01.2017, 02:33
    #39394309
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Вызов Callback из C-библиотеки
Не, ну здесь вариант
из CallBack запостить сообщение в процедуру окна да хоть бы тот же фиктивный WM_TIMER, а оттуда тягать сообщения.
Но именно PostMessage а не SendMessage, чтоб не ждать результата, иначе они друг друга закусят и висяк.
Идея та же, зато работает.

'CallBack - неважно в каком потоке вызван
Код: vbnet
1.
2.
3.
4.
  Public Function MyMessageAvailable() As Integer
    PostMessage(hForm1, WM_TIMER, 1001, IntPtr.Zero)
    Return 1
  End Function

где hForm1 -
Код: vbnet
1.
2.
3.
4.
5.
6.
 Public hForm1 As IntPtr

  Private Sub frm1_Load(sender As Object, e As EventArgs) Handles Me.Load
...
    hForm1 = Me.Handle
  End Sub

Form1.handle засовывать в CallBack не прокатит

Ну и обработка в своем основном потоке:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
  Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    'Debug.WriteLine(m.ToString())
    Select Case m.Msg
      Case WM_TIMER
        If m.WParam = New IntPtr(1001) Then
          Dim MesPtr As IntPtr
          Do
            MesPtr = OpalGetMessage(hOpal, 0)
            If MesPtr <> IntPtr.Zero Then
              'HandleMessages MesPtr
              Debug.Print(MesPtr.ToString)
              OpalFreeMessage(MesPtr)
            Else
              Exit Do
            End If
          Loop
        End If
    End Select

    ' Forward message to base WndProc.
    MyBase.WndProc(m)
  End Sub



Ну там все одно между Callback и взятием сообщения миллисекунд 15 пройдет (если дебажить).
Я вот только думаю, может ли случиться, что PostMessage сработало, основной поток поймал WM_TIMER и ничего там не нашел, потому что CallBack еще не вернул 1.
...
Рейтинг: 0 / 0
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Вызов Callback из C-библиотеки / 9 сообщений из 9, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]