【问题标题】:Delphi 2007 EXE and C++ DLL: Debug Output: Heap block at 027D4FF8 modified at 027D5009 past requested size of 9 ProcessDelphi 2007 EXE 和 C++ DLL:调试输出:027D4FF8 处的堆块在 027D5009 处修改,超过请求的 9 进程大小
【发布时间】:2017-10-27 17:20:51
【问题描述】:

我正在尝试在 Delphi 2007 中执行导入的函数:

function getOfficialCardNumber(): WideString ; cdecl; external 'idcapp.dll';

但几秒钟后(在调试配置中)它会停在这里:

77BD37D6 C6055562C17700   mov byte ptr [$77c16255],$00

然后说:

Debug Output: Heap block at 027D4FF8 modified at 027D5009 past requested size of 9  Process PreglediZaDom.exe (23672)

提到的函数是用 C++ (Visual Studio) 编写的

我尝试将 WideString 更改为 PWideString 等,但没有成功。有谁知道可能是什么问题?

在类似的应用程序中,但用 VB6 编写,他们给了我这个函数来转换字符串:

Public Function pisBstrToString(bstr) As String

    Dim bArray()    As Integer

    Dim longArray() As Long

    Dim str         As String

    Dim i           As Integer

    bArray = pisStringToBArray(bstr)
    longArray = pisBArrayToLongArray(bArray)

    str = ""

    For i = 1 To UBound(longArray)

        If longArray(i) > 0 And longArray(i) < 256 Then
            str = str & Chr(longArray(i))
        Else
            str = str & pisLongToUnichar(longArray(i))
        End If

    Next i

    pisBstrToString = str

End Function

我现在知道用 C++ 编写的函数是这样导出的:

BSTR _stdcall getOfficialCardNumber()

【问题讨论】:

  • 可能翻译有误? C 或 C++ 头文件中如何声明函数?或者VB6中的函数是如何声明的?
  • 我没有 C++ 头文件(dll 是由其他人构建的)。 Public Declare Function getOfficialCardNumber Lib "C:\WINDOWS\system32\idcapp.dll" () As String 是VB6中的声明
  • 我怀疑 VB6 可以处理cdecl,所以应该是stdcall。结果可能确实是WideString
  • 很久没接触VB6了,可能有以下错误:用PWideChar代替WideString。并确保使用 SysFreeString 释放结果。
  • FWIW,如果人们编写 DLL(无论使用哪种语言),他们都应该为其提供 C 或 C++ 头文件。我对编写 DLL 的看法:rvelthuis.de/articles/articles-dlls.html

标签: c++ delphi dll heap-memory


【解决方案1】:

VB6 不支持cdecl,仅支持stdcall。因此,您在 Delphi 代码中使用了错误的调用约定,导致调用堆栈管理不善。

Delphi 的 WideString 类型是 COM BSTR 字符串的包装器,VB6 确实将其用于自己的字符串 - 但不适用于外部 DLL 函数的 String 返回值!即使 VB6 期望/接受 BSTR 作为输出,DLL 函数也会在 EAX cpu 寄存器中返回 BSTR 指针,就像大多数其他返回类型一样。但是,Delphi 在处理WideString 作为返回类型时不使用EAX。而是作为隐藏的var 参数传递。看 Why can a WideString not be used as a function return value for interop? 了解更多详情。

如果 DLL 函数返回一个 char* 指针,则必须将函数返回值声明为 PAnsiChar

function getOfficialCardNumber(): PAnsiChar; stdcall; external 'idcapp.dll';

否则,如果它返回一个wchar_t*指针1,你将不得不将函数返回值声明为PWideChar

function getOfficialCardNumber(): PWideChar; stdcall; external 'idcapp.dll';

不管怎样,你还有另一个问题要处理——谁拥有被指向的内存,应该如何释放它? Delphi不能直接释放DLL返回的内存,那么内存是怎么分配的呢?

  • 如果在 DLL 中是静态的,则根本不需要释放它。

  • 如果是动态的,是哪个内存管理器分配的?

    • 如果它是 DLL 内部的内存管理器,则 DLL 必须导出一个单独的函数以在您完成使用后接收指针,以便可以使用相同的内存管理器释放它。

    • 如果是OS提供的内存API(LocalAlloc()/GlobalAlloc()IMalloc.Alloc()imalloc/CoTaskMemAlloc()等),那么Delphi代码可以直接调用对应的内存释放函数(@ 987654343@/GlobalFree()IMalloc.Free()/CoTaskMemFree()等)。


1:如果它碰巧实际上是一个BSTR 指针,您需要在使用完毕后将返回的指针传递给SysFreeString()

【讨论】:

  • 在这个dll中有一个叫做login的过程:procedure login(uName, pwd: WideString); cdecl;外部“idcapp.dll”;如果我这样声明:procedure login(uName, pwd: String); cdecl;外部“idcapp.dll”;它不起作用,所以参数必须是 WideString 类型,所以我认为返回参数也应该是 WideString
  • 是的,它在 EAX 寄存器中停止
  • String WideString 在 Delphi 中。当涉及互操作时,您无法切换它们。如果 DLL 函数需要 WideString,请将其传递给 WideString。但是你确定它实际上并不期待 PWideChar 吗? DLL 是用 Delphi 编写的,还是其他的?
  • dll 是用 C++ 编写的,我不知道他们使用什么作为返回类型,明天我会向他们索要标题
  • 在这种情况下,除非 login() 被声明为接受 BSTR 参数,否则您不应该在 Delphi 中将其声明为 WideString。使用PAnsiChar 代替char*PWideChar 代替wchar_t*。在后一种情况下,您可以使用WideString 变量,只需在传递时将其类型转换为PWideChar
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-26
  • 2015-11-04
  • 1970-01-01
  • 2013-12-13
  • 2014-06-19
  • 1970-01-01
相关资源
最近更新 更多