2009-12-10 1 views
2

В настоящее время я пишу модуль, который взаимодействует с черным ящиком сторонней DLL для проверки сканера. Мне нужно, чтобы функции DLL загружались динамически, и это работает для всех, кроме одной функции.
Функция SetScanParameters имеет структуру записи в качестве параметра, которая, как мне кажется, каким-то образом мешает методологии, которую я использую, для ее динамической загрузки (см. Ниже). При динамической загрузке функция прерывается нарушением доступа.
SetScanParameters действительно загружается и функционирует должным образом при статической загрузке. Есть ли что-то еще, что мне нужно сделать для динамической загрузки функции с помощью структуры записи?Delphi: Почему я могу связать эту функцию статически, но не динамически?

самостоятельно отредактированы для ясности:

Тип записи:

TBK_ScanParameter=packed record 
    Left:short; 
    Top:short; 
    Width:short; 
    Length:short; 
    // 
    xResolution:short; 
    yResolution:short; 
    BitsPerPixel:short; 
    LightControl:short; 
    MotorControl:short; 
    // 
    rGain:short; 
    gGain:short; 
    bGain:short; 
    rOffset:short; 
    gOffset:short; 
    bOffset:short; 
    rExposure:short; 
    gExposure:short; 
    bExposure:short; 
    // 
    FeedDirection:short; 
    CropImage:short; 
    ScanWithMICR:short; 
    // 
    Reserved:array [0..14] of short; 
    end; 

Статическая декларация:

function BK_SetScanParameter(var ScanParameter:TBK_ScanParameter):integer; cdecl; 

Статическая реализация:

function BK_SetScanParameter(var ScanParameter:TBK_ScanParameter):integer; cdecl; external 'BKV2.dll' name '[email protected]'; 

Динамическая логика (или что чюо ÜLD быть динамической логики, если я не должен использовать статический вызов, чтобы сделать его работу):

function TdmScannerV2.SetScanParameter(pScanParameter: TBK_ScanParameter): string; 
type 
    TBK_SetScanParameter = function (var ScanParameter:TBK_ScanParameter):integer; stdcall; 
var 
    hV2Dll:HMODULE; 
    func:TBK_SetScanParameter; 
begin 
    hV2Dll:=0; 
    result := ''; 
    try 
    hV2Dll:=LoadLibrary('BKV2.dll'); 
    if hV2Dll>0 then 
    begin 
     @func:=GetProcAddress(hV2Dll, '[email protected]'); 
     if Assigned(@func) then 
     begin 
     try 
      if BK_SetScanParameter(pScanParameter) > 0 then {This one works, but is static} 
      //if func(pScanParameter) > 0 then {this one gets an AV} 
      begin 
      Result := 'Y:Scan Parameters Set'; 
      end 
      else 
      Result := 'ERROR:Failure code returned'; 
      { 
      if func(pScanParameter) > 0 then 
      Result := 'Y:Scan Parameters Set' 
      else 
      Result := 'ERROR:Failure code returned'; 
      } 
     except 
      on e:Exception do 
      begin 
      Result := 'ERROR:Exception:' + e.Message; 
      end; 
     end; 
     end 
     else 
     Result := 'ERROR:Unable to load BK_SetScanParameter'; 
    end 
    else 
     Result := 'ERROR:Unable to load BKV2.dll'; 
    finally 
    if hV2Dll>0 then FreeLibrary(hV2Dll); 
    end; 
end; 

И я попытался с помощью STDCALL, Cdecl, директивой SafeCall, паскаль, и зарегистрировать на динамику и все они привели к AV. Я также попытался сделать массив в структуре [1..15] вместо [0..14]. И в Но я не получаю, если я передаю структуру в статическую версию, она работает.

Кроме того, в OP было несколько опечаток, и я прошу прощения за это. Я переписывал код в OP и делал несколько опечаток, которые немного мутировали поток. Я заменил его копией/вставкой текущей тестовой функции.

редактирования: Ниже ЬурейеЕ, как описано в документации для DLL:

typedef struct ScanParameter 
{ 
    short Left;   // left start positsion 
    short Top;    // top start positsion 
    short Width;   // scan image width in 1/100 inch 
    short Length;   // scan image length in 1/100 inch 

    short xResolution;  // horizontal resolution 
    short yResolution;  // vertical resolution 
    short BitsPerPixel; // 24bit color, 8bit gray 
    short LightControl; // 0 - All lamp Off, 1 - red, 2 - green, 3 - blue, 4 - All lamp On 
    short MotorControl; // Motor Control, 0 - off, 1 = on 

    short rGain;   // AFE R-Gain 
    short gGain;   // AFE G-Gain 
    short bGain;   // AFE B-Gain 
    short rOffset;  // AFE R-Offset 
    short gOffset;  // AFE G-Offset 
    short bOffset;  // AFE B-Offset 
    short rExposure;   // AFE R-Exposure 
    short gExposure;   // AFE G-Exposure 
    short bExposure;   // AFE B-Exposure 

    short FeedDirection; // feedout paper direction, 0 –fordward, 1 - backward 
    short CropImage;  // 0 - no trim edge , 1 - trim edge 
    short ScanWithMICR; // 0 –off, 1 –scan image until paper leave device 

    short Reserved[15]; 
} ScanParameter; 
+1

Добро пожаловать в Переполнение стека. Вы должны быть более точными. Если GetProcAddress завершается успешно, то вы загрузили функцию, поэтому ваш заголовок просто неверен. Кроме того, вы не можете «загружать» функцию статически. Здесь нет ничего статического. DLL * всегда * связаны динамически. Вот что означает * D *. Различие между динамическими связями * load-time * и * run-time *. Время загрузки - это когда ОС считывает таблицу импорта вашей программы и разрешает вам адреса функций, поскольку она загружает программу в память. Время запуска - это когда вы загружаете модуль и выбираете адрес непосредственно с помощью GetProcAddress. –

+0

Ваша статическая динамика - как яблоки для апельсинов. Одна из них - это функция cdecl из Scan.dll, другая - функция stdcal из ScanDLL.dll. Или вы не опубликовали свой реальный код .... –

+0

Итак, из документации, похоже, DLL была написана на C. Возможно ли получить исходное объявление функции DLL? Кроме того ... Хотя я немного ржавый на C нюансах, я вполне уверен, что 'short Reserved [15];' должен быть записан как 'Reserved: array [0..15] short;' в Delphi. –

ответ

1

Мое предложение проверить соглашение о вызове, в Дельфах по умолчанию вызывающему конвенции был Паскаль, но Microsoft длл скомпилирован будет Cdecl наиболее вероятно. Поэтому попробуйте определить func как

TSetScanParameter = function (var ScanParameter:TParams):integer; cdecl; 

как вы сделали при статическом определении.

+0

Я пробовал cdecl, stdcall и safecall на динамическом. Я просто попробовал два других (pascal и register), не повезло. Я также попытался использовать указатель на TScanParameter вместо ключевого слова var. Но все они дают мне ошибку AV. – RikG

+0

Я понимаю, что вы получаете AV после получения адреса функции и вызова функции, не так ли? – whoi

+3

Рик, не просто * попробуйте * кучу вещей. Попробуйте найти * не экспериментально * (т. Е. Не догадываясь), который является * правильным *, а затем использовать только это. Что вы используете в качестве ссылки для этой функции? Если это код C, отредактируйте свой вопрос, включив оригинальные объявления. –

0

Это, скорее всего, проблема с тем, как Delphi хранит структуру записи, и это несовместимо с структурой записи в DLL. У меня были аналогичные проблемы с DLL, которые я написал на C-интерфейсе для Delphi.

Процитируем

Обычно, сложные типы данных, такие как записи имеют свои элементы выровнены по 2, 4 или 8 границах байтов, в соответствии с типом данных. Например, поле Word будет выровнено по 4-байтовой границе. Записи также дополняются, чтобы они заканчивались на границе 4 байта.

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

Первое, что нужно попробовать - использовать ключевое слово Packed в записи.

TPackedRecord = Packed Record 

Снова цитирую руководство

Упакованная ключевое слово говорит Delphi минимизировать память, занимаемую определенного объекта. Packed переопределяет это, сжимая данные в самом маленьком хранилище, хотя и с уменьшенной скоростью доступа.

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

+0

Ни Packed Record, ни использование cdecl не решили. Похоже, мне придется пойти с планом резервного копирования написания DLL-обертки для этой DLL – RikG

+0

Это OR, а не XOR, я надеюсь? (как вы пытались оба сразу?) – Cruachan

+0

Да, ИЛИ. Я пробовал все комбинации. – RikG

2

Вы сделали определить два соглашения по вызову:

  1. Cdecl в статической декларации
  2. STDCALL в динамической декларации.
3

Как уже упоминалось выше, вызывающее соглашение выглядит так, как будто оно должно быть cdecl, а не stdcall. Во-вторых, попробуйте изменить библиотеку загрузки, которая будет

hV2Dll := LoadLibrary('Scan.dll'); 

Оригинал имел «ScanDLL.dll».

+0

Эй, чак - это было давно! –

0

Вы говорите следующее работает:

function SetScanParameter(var Scans:TParams):integer; cdecl; external 'Scan.dll' name '[email protected]'; 

Тем не менее, другой не работает .... Для начала, убедитесь, что вы делаете то же самое в каждом. В настоящее время вы этого не делаете, см. Ниже:

TSetScanParameter = function (var ScanParameter: TParams): integer; stdcall;
hV2Dll: = LoadLibrary ('ScanDLL.dll');

Не говоря уже о том, что вы можете передавать разные разные значения в каждом вызове, вы можете использовать разные объявления TParams. Если вы хотите узнать, почему один apple отличается от другого apple, вы не идете и не сравниваете его с оранжевым.

Получите эти основы правильно; то, если у вас все еще есть проблемы:

  • Предоставьте нам объявление TParams.
  • Предоставьте нам соответствующий отрывок из этой документации сторонней DLL.
  • Язык, используемый для создания библиотеки DLL, был бы полезен, если вы это узнаете.
  • Фактические декларации метода и структуры на указанном выше языке были бы еще более полезными.

Если вы хотите помочь, по крайней мере, прилагайте все усилия, чтобы помочь нам помочь вам.

0

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

Afaik выравнивание является типичным для компиляторов C (все либо короткое, либо сложное короткое, и все выровнены на естественных bounderies), но пытаясь выровнять массив на более крупных boundery (4,8,16 байт) и/или добавление записи с помощью некоторого дополнительного манекена также может помочь.

0

Можете ли вы опубликовать объявление функции C из файла заголовка? Также проверьте адрес загрузки DLL, когда функция скомпилирована как статический адрес vs, где динамическая загрузка DLL динамически. Сама DLL может иметь определенный код, специфичный для местоположения, и при его статичном связывании загрузчик Windows загрузит DLL для исправления местоположения в памяти, но когда вы загружаете его динамически, может быть что-то в этом месте, поэтому загрузчик Windows переместит DLL на новый но код внутри DLL, возможно, не был написан правильно, и он все еще может пытаться получить доступ к некоторому статическому адресу памяти. Еще одна вещь, которую стоит попробовать - загрузить и выгрузить DLL вне вызова функции и объявить динамический тип функции вне функции, компилятор Delphi может оптимизировать что-то по-другому, когда вы объявляете все это внутри функции. Наконец, убедитесь, что вы используете правильное соглашение о вызове (должно быть доступно в файле заголовка C), поскольку они очень разные, а функции DLL будут вызывать проблемы с A/Vs или стеком, если они вызываются с неправильным соглашением.

0

Спасибо за помощь, ребята. Оказывается, это уже не проблема. DLL, которую выпустила компания-сканер, была немного ошибочной, и они выпустили новую, которая устраняет проблему.