http://blog.csdn.net/niniu/archive/2006/10/30/1356680.aspx
写了一个 DLL ,封装了一个类,该类有一个函数名为 WriteRecord(P: Pointer),其中参数 P 是一个记录指针,子类在 override 该函数时学根据将 P 指针转为子类所支持的记录类型,如:
var
pt: PBookInfo;
begin
pt := PBookInfo(P);
...
end;
该 DLL 有一个导出函数:
function CreateObj: TObjType; stdcall;
这个 DLL 采用动态加载的办法:
DllHandle := LoadLibrary('abc.dll');
加载成功后调用CreateObj函数创建了一个对象,然后调用了该对象的 WriteRecord 函数,此过程一切正常,然而,完毕之后调用FreeLibrary(DllHandle)时出错,经过排查发现是调用了 WriteRecord 函数导致的,在网上查了资料,并认真看了 DLL 的 dpr 文件开头部分的注释:
Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters.
这段文字的意思是:如果你的 DLL 导出了含有 string 类型的参数或者返回值为 string 类型,则必须将 ShareMem 作为 DLL 和 代用 DLL 的工程的第一个引用单元,这对于所有的含有 string 类型传递的情况都有效,即使是 string 类型嵌套在记录或者类中。【英语不好,翻译可能不准确,但意思应该是清楚的】
于是,在 DLL 和 调用 DLL 的工程 的dpr 文件中都引用的ShareMem,就没有出问题了。
以下是网上找到关于ShareMem 的说明,仅供参考:
-------------------------------------------------------------------------------------------------
1) ShareMem单元说白了就是替换EXE和DLL各自的内存管理器,使它们共同使用一个内存管理器,所以它需要在DLL和EXE中同时加入,执行的时候,总是先执行EXE文件,这时候ShareMem中的内存管理器替换EXE原来的内存管理器,当DLL被加载的时候,ShareMem单元再次被调用,它检查是否已经有了ShareMem单元的管理器,如果有了,就直接使用它。这样就完成了共用一个内存管理器的工作。如果只在DLL中使用ShareMem单元而忘了在EXE中也把它加上,ShareMem单元其实是没有意义的。
2) Borland 的 ShareMem 单元应该放入 EXE 和 DLL 才有作用,但是当把它仅仅放入 EXE 而不放入 DLL 的话,等于这个 ShareMem 中的内存管理器无效,如果仅仅放入 DLL 而不放入 EXE 的,退出 DLL 时就会如上面例子那样报运行时错误。
实际上,从 Delphi6 开始,常数String参数的传递已经不需要 ShareMem 这个单元了,也就是说,上面例子的错误,真正的原因是在 DLL 中单方面引用了 ShareMem 所致,直接从 DLL 删除 ShareMem 单元的引用,程序就能正常运行。
function ShowCalendar(AHandle: THandle;ACaption: String): TDateTime; StdCall;
本质上与
function ShowCalendar(AHandle: THandle;const ACaption: String): TDateTime; StdCall;
相同。
在传入的 ACaption 后,我们没有在 DLL 中对 ACaption 进行操作,即便操作了ACaption,也因为EXE只把它作为输出参数而不理会对它的修改,所以已经不需要 ShareMem 的参与了。
说到 ShareMem 内存,建议看一看Aimingoo对它的描述:http://www.01cn.net/cgi-bin/topic_show.cgi?id=2770
我测试了ShareMemRep和FastShareMem单独在DLL中引用的情况,ShareMemRep是Aimingoo写的单元,单独在DLL中引用,他会弹出一个信息框提醒Host没有成功,并关闭应用程序,FastShareMem会替换DLL的内存管理器,并继续运行,而且退出时并不发生运行时错误。明显的,这2个单元都比Borland 自己带的的 ShareMem 单元好用,因为他们不需要Borldmm.dll参与,也就不会在单独引用时发生Borldmm.dll不能卸载的问题。