2015-06-11 3 views
0

Ситуация такова: у меня есть сканер штрих-кода с dll (написанный на C++, все функции stdcall), который экспортирует все необходимые функции для связи с ним. И есть несколько функций, которые следует использовать для назначения в качестве обратных вызовов (я должен предоставить соответствующие процедуры, и они будут вызваны, как только произойдет определенное событие). И вот главная проблема, с которой я столкнулся. Эти функции назначения обратного вызова могут принимать только «автономные» процедуры (в этом случае все работает нормально), но не методы или даже анонимная процедура. На этих событиях мне приходится обращаться к некоторым данным класса (сканер реализуется как класс), например, полям и методам, но обычно это невозможно из процедуры отдельного блока. Можете ли вы помочь мне решить эту проблему?Процедура отправки экспортированной DLL-функции (организация обратного вызова)

Некоторый код:

Описания сканера API:

typedef void (__stdcall *TOnDevicePlug)(bool PluggedIn); 
    extern "C" __declspec(dllexport) void __stdcall SetupOnDevicePlugCallback(TOnDevicePlug OnPresent); 

Мои описания типа:

TOnDevicePlug = procedure(PluggedIn: Boolean); stdcall; 
    TSetupOnDevicePlugCallback = function(AOnPresent: TOnDevicePlug): Integer; stdcall; 

В случае, если я вот так:

procedure OnDevicePlug(PluggedIn: Boolean); stdcall; 
    begin 
     Form2.mmLog.Lines.Add('On plugged activated'); 
    end; 

    ... 
    @FSetupOnDevicePlugCallback := GetProcAddress(FDllHandle, 'SetupOnDevicePlugCallback'); 
    FSetupOnDevicePlugCallback(OnDevicePlug); 
    ... 

Все работает. (Но опять же, я не могу работать с моим классом).

Если я пытаюсь что-то вроде этого:

FOnPlug := TOnDevicePlug(
     procedure stdcall 
     begin 
     mmLog.Lines.Add('On plugged activated'); 
     end 
    ); 
    FSetupOnDevicePlugCallback(FOnPlug); 

Где FOnPlug - поле класса типа TOnDevicePlug. В этом случае есть два варианта: если я оставляю описание старого типа (TOnDevicePlug = procedure(PluggedIn: Boolean); stdcall;), я получаю исключение из «привилегированной инструкции». Если я изменю тип на «ссылка на процедуру» (например, TOnDevicePlug = reference to procedure stdcall;), я получаю исключение нарушения прав доступа. Эти ошибки вызывают событие.

Так вы можете посоветовать мне что-то, чтобы это мероприятие работало с моим классом? Будем благодарны за любую идею. Или, может быть, вы увидите некоторые ошибки в моем коде.

ответ

1

Ваш обратный вызов должен иметь вид:

procedure(PluggedIn: Boolean); stdcall; 

Анонимный метод просто не совместимы. Написать блок контекстного процедуру так:

procedure MyOnDevicePlug(PluggedIn: Boolean); stdcall; 
begin 
    // do stuff 
end; 

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

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

Если вы не можете внести такую ​​модификацию, вам может понадобиться использовать глобальную переменную или даже thunk для получения состояния.

+0

Вы не * должны написать процедуру с ограниченной единицей *. Вы можете использовать метод статического класса. Но вы до сих пор не получите доступ к * этому * экземпляру. Это тесно связано с http://stackoverflow.com/q/2953070/960757. – TLama

+0

@TLama Да, статический класс способ тоже возможно. –

+0

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

Смежные вопросы