Тестирование библиотеки DLL Delphi приводит к сбою VB6 IDE

#delphi #dll #vb6 #typelib

Вопрос:

Я впервые попробовал написать DLL на Delphi. Пока все хорошо. Используя typelib, я смог без труда передавать расширенные строки в библиотеку DLL и из нее.

Что любопытно на данный момент, так это то, что я использую VB6 в качестве испытательного стенда, и каждый раз, когда я запускаю тест в среде IDE, программа запускается, а затем процесс IDE внезапно исчезает из памяти — ни сообщений об ошибках, ничего. Если я пройдусь по коду, все будет работать нормально, пока я не выполню последнюю строку, а затем IDE исчезнет.

Напротив, когда я компилирую тест в EXE-файл, программа запускается до конца, без сообщений об ошибках и т. Д.

У кого-нибудь раньше была эта проблема, и есть ли очевидное решение, которое смотрит мне в лицо?

Исходный код ниже, на случай, если это имеет значение:

— проект

 library BOSLAD;

uses
  ShareMem,
  SysUtils,
  Classes,
  BOSLADCode in 'BOSLADCode.pas';

exports
  version,
  DMesg,
  foo;
{$R *.res}

begin
end.
 

— единица измерения

 unit BOSLADCode;

interface
  function version() : Double; stdcall;
  procedure DMesg(sText : WideString; sHead : WideString ); stdcall;
  function foo() : PWideString; stdcall;

implementation
  uses Windows;

  function version() : Double;
  var
    s : String;
  begin
    result := 0.001;
  end;

  procedure DMesg( sText : WideString; sHead : WideString);
  begin
    Windows.MessageBoxW(0, PWideChar(sText), PWideChar(sHead), 0);
  end;

  function foo() : PWideString;
  var s : WideString;
  begin
    s := 'My dog''s got fleas';
    result := PWideString(s);
  end;
end.
 

— typelib

  // This is the type library for BOSLAD.dll
      [
      // Use GUIDGEN.EXE to create the UUID that uniquely identifies
      // this library on the user's system. NOTE: This must be done!!
         uuid(0C55D7DA-0840-40c0-B77C-DC72BE9D109E),
      // This helpstring defines how the library will appear in the
      // References dialog of VB.
         helpstring("BOSLAD TypeLib"),
      // Assume standard English locale.
         lcid(0x0409),
      // Assign a version number to keep track of changes.
         version(1.0)
      ]
      library BOSLAD
      {

      // Now define the module that will "declare" your C functions.
      [
         helpstring("Functions in BOSLAD.DLL"),
         version(1.0),
      // Give the name of your DLL here.
         dllname("BOSLAD.dll")
      ]
      module BOSLADFunctions
      {
[helpstring("version"), entry("version")] void __stdcall version( [out,retval] double* res );
[helpstring("DMesg"), entry("DMesg")] void __stdcall DMesg( [in] BSTR msg, [in] BSTR head );
[helpstring("foo"), entry("foo")] void __stdcall foo( [out,retval] BSTR* msg );
      } // End of Module
      }; // End of Library
 

Я переместил объявление WideString за пределы функции, в которой я его объявил, в расчете на то, что это увеличит время жизни переменной до большего, чем просто время жизни foo функции. Это не имело никакого значения.

Аналогичным образом я прокомментировал из VB6 вызов foo функции. Это тоже не имело значения. Независимо от того, что я делаю, среда разработки VB6 умирает после выполнения последней строки кода.

Причиной является что-то помимо указателей на локальные переменные. Но что?

Ответ №1:

 result := PWideString(s);
 

Здесь вы возвращаете указатель на локальную переменную. Это немедленно становится недействительным.

Ответ №2:

Чтобы подробнее остановиться на ответе Гсерга:

 result := PWideString(s);
 

вы могли бы подумать, что это было бы нормально, потому что s был инициализирован строковым литералом… но широкие строки в Delphi не считаются ссылками, как обычные строки, поэтому s фактически содержит немного динамически выделяемой памяти кучи, и как только функция вернет эту память, ее можно будет использовать повторно 🙁

Однако следующее должно быть в порядке:

 function foo() : PWideString;
const s : WideString = 'My dog''s got fleas';
begin
  result := PWideString(s);
end;
 

Комментарии:

1. Я отмечаю это, потому что что-то нужно отметить, и я не могу отмечать свои собственные решения. Иногда ТАК себе отстой.

Ответ №3:

Создание библиотек DLL на delphi.wikia.com на сайте есть ответ, который я так долго искал. И решение тоже.

Например, Delphi автоматически выделяет и освобождает память для хранения ваших строк, он знает, когда они больше не нужны и т. Д. То же самое относится, например, к Visual Basic, но оба делают это по-разному. Итак, если бы вы передали строку, выделенную Visual Basic, в библиотеку DLL, написанную на Delphi, у вас были бы большие проблемы, потому что теперь оба попытались бы управлять строкой и вцепились бы друг другу в волосы.

Решение заключается в использовании FastMM, и оно работает блестяще!! Теперь у меня есть замена BORLNDMM.DLL в моем проекте, и все просто работает.

Комментарии:

1. Это касается струн, а не широких шнуров. Последние выделяются через COM — просто из-за совместимости.

2. И FastMM, как и ShareMem, работает только в том случае, если все используют этот менеджер памяти. VB определенно этого не делает.

Ответ №4:

Я только что полностью разобрался в этом, благодаря Робу Кеннеди в новостях:comp.lang.pascal.delphi.разное

Он сказал, среди прочего, что:

  1. Эта библиотека DLL не нуждается в ShareMem, SysUtils или классах.
  2. Вы взяли WideString и сказали компилятору, что на самом деле это указатель на WideString. Вы лжете компилятору. Ему все равно, но вызывающий эту функцию, вероятно, делает это.

Таким образом, пересмотренный код, который отлично работает без ShareMem (и SysUtils и классов, которые были добавлены мастером DLL, как это происходит), выглядит следующим образом:

 library BOSLAD;
uses
  BOSLADCode in 'BOSLADCode.pas';
exports
  version,
  DMesg,
  foo;
{$R *.res}
begin
end.
 

Босладкод.пас:

 unit BOSLADCode;

interface
  function version() : Double; stdcall;
  procedure DMesg(sText : PWideChar; sHead : PWideChar ); stdcall;
  function foo() : PWideChar; stdcall;

implementation
  uses Windows;

  var s : WideString;

  function version() : Double;
  begin
    result := 0.001;
  end;

  procedure DMesg( sText : PWideChar; sHead : PWideChar);
  begin
    Windows.MessageBoxW(0, sText, sHead, 0);
  end;

  function foo() : PWideChar;
  begin
    s := 'My dog''s got fleas';
    result := PWideChar(s);
  end;
end.
 

бослад.одл:

 // This is the type library for BOSLAD.dll
[
uuid(0C55D7DA-0840-40c0-B77C-DC72BE9D109E),
helpstring("BOSLAD TypeLib"),
lcid(0x0409),
version(1.0)
]
library BOSLAD
{
[
helpstring("Functions in BOSLAD.DLL"),
version(1.0),
dllname("BOSLAD.dll")
]
module BOSLADFunctions
{
[helpstring("version"), entry("version")] 
    void __stdcall version( [out,retval] double* res );
[helpstring("DMesg"), entry("DMesg")] 
    void __stdcall DMesg( [in] BSTR msg, [in] BSTR head );
[helpstring("foo"), entry("foo")] 
    void __stdcall foo( [out,retval] BSTR* msg );
} 
}; 
 

тест.бас:

 Sub Main()
    Dim cfg As New CFGProject.cfg
    cfg.Load "test.cfg"
    Dim s As String
    s = cfg.Recall("msg")
    DMesg s, "" amp; version
    s = foo
    DMesg s, "" amp; version
End Sub
 

тест.cfg

 msg=毅訜訝
 

Все это прекрасно работает. IDE VB6 с радостью запускает DLL, и MsgBoxs отображаются со всем, как и должно быть.

Ответ №5:

Я думаю, мы можем закрыть это дело. Приведенного ниже кода, по-видимому, достаточно, чтобы люди news:comp.lang.pascal.delphi.misc были довольны, и мне действительно нужно перейти от концептуального тестирования к тому, чтобы что-то с ним сделать.

БОСЛАД.bdsproj:

 library BOSLAD;

uses
  BOSLADCode in 'BOSLADCode.pas';

exports
  version,
  DMesg,
  foo;
{$R *.res}

begin
end.
 

Босладкод.пас:

 unit BOSLADCode;

interface
  function version() : Double; stdcall;
  procedure DMesg(const sText : WideString; const sHead : WideString ); stdcall;
  function foo() : PWideChar; stdcall;

implementation
  uses Windows, ActiveX;


  function version() : Double;
  begin
    result := 0.001;
  end;

  procedure DMesg( const sText : WideString; const sHead : WideString);
  begin
    Windows.MessageBoxW(0, PWideChar(sText), PWideChar(sHead), 0);
  end;

  function foo() : PWideChar;
  var s : WideString;
  begin
    s := 'My dog''s got fleas';
    result := SysAllocString(PWideChar(s));
  end;
end.
 

Теперь VB счастлив, и у меня не бывает странных сбоев IDE.