4

我第一次尝试在 Delphi 中编写 DLL。到现在为止还挺好。通过使用类型库,我能够毫无困难地在 DLL 中传递 Widestrings 和从 DLL 传递 Widestrings。

目前令人好奇的是,我使用 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.

-- 类型库

 // 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 在最​​后一行代码执行后就死掉了。

除了指向局部变量的指针之外,还有其他原因。但是什么?

4

5 回答 5

2
result := PWideString(s);

您在此处返回指向局部变量的指针。它立即变得无效。

于 2008-10-11T06:59:03.253 回答
1

详细说明 GSerg 的回答:

result := PWideString(s);

你会认为这没问题,因为 s 是用字符串文字初始化的......但是 Delphi 中的宽字符串不像普通字符串那样被引用计数,所以 s 实际上拥有一些动态分配的堆内存,并且只要函数返回此内存可以重复使用:(

以下应该没问题:

function foo() : PWideString;
const s : WideString = 'My dog''s got fleas';
begin
  result := PWideString(s);
end;
于 2008-10-11T11:18:54.527 回答
1

在 delphi.wikia.com 网站上创建 DLL有我一直在寻找的答案。还有解决方案。

例如,Delphi 自动分配和释放内存来存储您的字符串,它知道何时不再需要它们等。这同样适用于例如 Visual Basic,但两者都以不同的方式进行。因此,如果您要将一个由 Visual Basic 分配的字符串传递给一个用 Delphi 编写的 DLL,那么您最终会遇到大麻烦,因为现在两者都将尝试管理该字符串并且会互相打扰。

解决方案是使用FastMM,它的效果非常好!!我现在有一个替换BORLNDMM.DLL我的项目,一切正常。

于 2008-10-11T15:07:11.913 回答
0

感谢 Rob Kennedy 的新闻:comp.lang.pascal.delphi.misc

他说,除其他外:

  1. 此 DLL 不需要 ShareMem、SysUtils 或 Classes。
  2. 您获取了一个 WideString 并告诉编译器它实际上是一个指向 WideString 的指针。你在对编译器撒谎。它不在乎,但这个函数的调用者可能会。

因此,修改后的代码在没有 ShareMem(以及 DLL 向导添加的 SysUtils 和类)的情况下也能正常工作,如下所示:

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

BOSLAD代码.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 );
} 
}; 

测试.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

测试.cfg

msg=毅訜訝

所有这些都完美无缺。VB6 的 IDE 愉快地运行 DLL,并且 MsgBox 以应有的方式出现。

于 2008-10-15T04:17:07.930 回答
0

我想我们可以关闭这个。下面的代码似乎足以让人们news:comp.lang.pascal.delphi.misc开心,我真的需要从概念测试转向实际做一些事情。

BOSLAD.bdsproj:

library BOSLAD;

uses
  BOSLADCode in 'BOSLADCode.pas';

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

begin
end.

BOSLAD代码.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 崩溃。

于 2008-10-17T15:54:15.417 回答