测试Delphi DLL导致VB6 IDE崩溃

4
我第一次尝试使用Delphi编写DLL,目前为止还不错。通过使用类型库,我能够轻松地传递WideString到DLL并从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 IDE都会崩溃。

除了指向本地变量的指针之外,还有其他原因导致了这种情况。但是是什么呢?

5个回答

2
result := PWideString(s);

你在这里返回一个指向本地变量的指针。它会立即失效。

1

在delphi.wikia.com网站上创建DLLs的文章给出了我一直在寻找的答案。而且还有解决方案。

例如,Delphi会自动分配和释放用于存储字符串的内存,它知道何时不再需要等等。Visual Basic也是如此,但两者的方式不同。因此,如果您将由Visual Basic分配的字符串传递给用Delphi编写的DLL,您将遇到大麻烦,因为两者现在都会尝试管理该字符串并相互干扰。

解决方案是使用FastMM,它非常出色!现在我的项目中有一个替代BORLNDMM.DLL,一切都正常工作。


这适用于字符串,而不适用于WideStrings。后者通过COM分配 - 仅仅是因为互操作性。 - gabr
FastMM和ShareMem一样,只有在所有人都使用该内存管理器时才能正常工作。VB显然不支持这一点。 - Lars Truijens

1

对GSerg的回答进行详细说明:

result := PWideString(s);

你可能认为这没问题,因为s是用字符串字面量初始化的...但是在Delphi中,宽字符串不像普通字符串一样具有引用计数功能,所以s实际上持有一小块动态分配的堆内存,一旦函数返回,该内存就可以被重用 :(

以下应该没问题:

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

我会打勾这个问题,因为需要有一个勾选项,而且我不能勾选自己的解决方案。有时候SO真的很烂。 - bugmagnet

0

我在 news:comp.lang.pascal.delphi.misc 得到了 Rob Kennedy 的帮助,终于搞清楚了这个问题。

他说,除其他事项外:

  1. 这个 DLL 不需要 ShareMem、SysUtils 或 Classes。
  2. 你拿一个 WideString 并告诉编译器它实际上是指向一个 WideString 的指针。你在欺骗编译器。编译器不会在意,但调用这个函数的人可能会在意。

所以修订后的代码,在没有 ShareMem(和 SysUtils 和 Classes,这些是 DLL 向导添加的)的情况下正常工作,如下所示:

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

BOSLADCode.pas:

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.

boslad.odl:

// 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 );
} 
}; 

test.bas:

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

test.cfg

msg=毅訜訝

所有的都完美地运作了。VB6的IDE愉快地运行着DLL,MsgBox也按照应有的方式出现。


0

我认为我们可以关闭这个问题。下面的代码似乎足以让news:comp.lang.pascal.delphi.misc上的人们满意,而我真的需要从概念测试转向实际操作。

BOSLAD.bdsproj:

library BOSLAD;

uses
  BOSLADCode in 'BOSLADCode.pas';

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

begin
end.

BOSLADCode.pas:

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崩溃了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接