终于体会到了,异常的解决办法
分类:多线程

 

一 Dll的制作一般步骤
二 参数传递
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
四 全局变量的使用
五 调用静态载入
六 调用动态载入
七 在DLL建立一个Tform
八 在DLL中建立一个TMDIChildform
九 示例:
十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
十一 相关资料

  今天用Delphi编写了个dll,用到了TStringList来传递多参数,如下:

我是个Delphi fans,自从试了一下Delphi8,第二天就删掉了,bug太多,没法用,只好不太情愿地使用着C#,Delphi2005出来了,就又装了一个试用版用了一下,感觉不错,有点欣喜若狂,就准备使用起来,但是随着程序规模的不断扩大,越来越多地发现了至少是令人不爽的地方:

为了让人能快速的理解 静态调用、动态调用,现在做一个函数封装在一个DLL中,然后在APPLICATION form里面调用这个函数,这个函数处理两个数的和。用代码和图片说话:
代码如下

一 Dll的制作一般分为以下几步:
1 在一个DLL工程里写一个过程或函数
2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。

 1 function DBM_SetParam(procName: string; paramValues: TStringList = nil): Boolean;stdcall;   
 2 var  
 3   i: integer;   
 4 begin  
 5   try  
 6     if (paramValues<>nil) then  
 7     begin  
 8       for i := 0 to paramValues.Count - 1 do  
 9       begin  
10         GParameters.AddParameter;   
11         GParameters[i].Value := paramValues[i];   
12       end;   
13     end;   
14     Result := True;   
15   except on e: Exception do  
16     begin  
17       Result := False;   
18     end;   
19   end;   
20 end; 

1、占用内存太多,一般来讲写一会程序就要用掉400M,更可怕的是每调试一次,内存就会增加好多,调试完了还不给我释放

library Project1;

二 参数传递
1 参数类型最好与window C++的参数类型一致。不要用DELPHI的数据类型。
2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。
3 用stdcall声明后缀。
4 最好大小写敏感。
5 无须用far调用后缀,那只是为了与windows 16位程序兼容。

然而在应用程序中调用的时候,却出了点问题:整个运行过程是有效的,并且参数也都传递成功,但是当结束了该API函数执行后每次都会弹出“Invalid pointer operation”的异常。调用代码如下:

2、不稳定,尤其是在界面设计上,经常把我关联的事件从控件上删掉,要么就是莫名其妙的一些错误提示,当然这些提示都是在设计器中的,还不会影响正常的Delphi使用,但是经常刚刚还好的界面我从代码切换到IDE设计机器上,就什么也显示不出来了。

{ 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的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。

 1 procedure TForm1.Button3Click(Sender: TObject);   
 2 var  
 3   paramList: TStringList;   
 4 begin  
 5   paramList := TStringList.Create;   
 6   paramList.Add('2');   
 7   if (not DBM_SetParam('GetUserName', paramList)) then  
 8   begin  
 9     ShowMessage('Set parameters failed!');   
10   end;        
11 end;  

3、内部不一致,能编译通过的,但是且在没错的地方用红色波浪线提示语法错误

uses
SysUtils,
Classes;
function incc(a,b:integer):Integer; stdcall; //真正的代码开始;
begin
Result :=a+b;
end;
{$R *.res}

在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:

 

明明支持

exports //允许调用;
incc;

procedure DllEnterPoint(dwReason: DWORD);far;stdcall;
dwReason参数有四种类型:
DLL_PROCESS_ATTACH:进程进入时
DLL_PROCESS_DETACH:进程退出时
DLL_THREAD_ATTACH :线程进入时
DLL_THREAD_DETACH :线程退出时

 

 var

end.

在初始化部分写:
DLLProc := @DLLEnterPoint;
DllEnterPoint(DLL_PROCESS_ATTACH);

 

   a:array of integer;

按Ctrl+f9编译以后会在文件夹下面会产生一个 project1.dll的DLL文件。下面,我们就开始用静态调用和动态调用两种方式调用这个DLL里面的函数。

2 如form上有TdcomConnection组件,就Uses Activex,在初始化时写一句CoInitialize (nil);
3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。

   在google里搜索Invalid pointer value发现遇到同样问题的同志也不在少数。顺藤摸瓜,终于在Delphi联机帮助里找到了下面的描述:

  a := new(array[5]of integer,',',');

一:静态调用

四 全局变量的使用
在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。

    On Windows, if a DLL exports routines that pass long strings or dynamic arrays as parameters or function results (whether directly or nested in records or objects), then the DLL and its client applications (or DLLs) must all use the ShareMem unit. The same is true if one application or DLL allocates memory with New or GetMem which is deallocated by a call to Dispose or FreeMem in another module. ShareMem should always be the first unit listed in any program or library uses clause where it occurs.

编译没问题,却要给我在线错误提示,让我不爽

新建一个application form 在这个窗体上加两个文本框,取名edt1,edt2 用代码说话。

五 调用静态载入
1 客户端函数声名:
1)大小写敏感。
2)与DLL中的声明一样。
如: showform(form:Tform);Far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windows c++一样。
4)调用时DLL必须在windows搜索路径中,顺序是:当前目录;Path路径;windows;widowssystem;windowsssystem32;

ShareMem is the interface unit for the BORLANDMM.DLL memory manager, which allows modules to share dynamically allocated memory. BORLANDMM.DLL must be deployed with applications and DLLs that use ShareMem. When an application or DLL uses ShareMem, its memory manager is replaced by the memory manager in BORLANDMM.DLL.

a明明是一个数组,我要是写a.length 他就编译不通过,非要写成&Array(a).Length才行

unit Unit1;

六 调用动态载入
1 建立一种过程类型(或者是一个Function)[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。如:

Note

4、对namespace的支持莫名其秒,非要搞个复杂的命名规则,在unit名称上做文章,虽然这样可以获得最大的对传统win32 pas的语法支持,但是感觉这种方式给.net开带来很多不便,结果到了uses地方还要引用全名,而不是名称空间名,还要时时提醒自己用到的是别的.net开发的dll还是delphi开发的dll,对同一种东西非要搞出两套标准,真是让我恼火

interface

[delphi]
ype 
mypointer=procedure(form:Tform);Far;external;  
//mypointer=function(form:Tform);Far;external;  
 
var 
  Hinst:Thandle; 
  showform:mypointer; 
begin 
   Hinst:=loadlibrary('yproject_dll');//Load一个Dll,按文件名找。  
   showform:=getprocaddress(Hinst,'showform');//按函数名找,大小写敏感。如果你知道自动化对象的本质就清楚了。  
   showform(application.mainform);//找到函数入口指针就调用。  
   Freelibrary(Hinst); 
end; 

Linux uses glibc's malloc to manage shared memory.

5、debug太差

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
mypointer=procedure(form:Tform);Far;external;
//mypointer=function(form:Tform);Far;external;

    【2010-6-3:翻译一下,供大家参考:

对比Delphi7简直是倒退

type
TForm1 = class(TForm)
btn1: TButton;
edt1: TEdit;
edt2: TEdit;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
  Hinst:Thandle;
  showform:mypointer;
begin
   Hinst:=loadlibrary('yproject_dll');//Load一个Dll,按文件名找。
   showform:=getprocaddress(Hinst,'showform');//按函数名找,大小写敏感。如果你知道自动化对象的本质就清楚了。
   showform(application.mainform);//找到函数入口指针就调用。
   Freelibrary(Hinst);
end;

    Windows中如果一个动态库导出函数以参数形式传递或者返回了一个长字符串或者动态数组(无论是直接传递还是包含在一个对象中),那么动态库以及其调用程序都必须得引用ShareMem单元。同样,如果程序或者库通过New或者GetMem申请了内存,并且该内存在另一个模块中通过Dispose或FreeMem释放,也得这么做。务必把ShareMem放在程序或者库所有引用单元之首。

在win32下,可以调试程序中的变量,到了.net  那个watch窗口就成了摆设,根本没用

var
Form1: TForm1;

七 在DLL建立一个Tform
1 把你的form Uses到Dll中,你的form用到的关联的单元也要Uses进来[这是最麻烦的一点,因为你的form或许Uses了许多特殊的单元或函数]
2 传递一个Application参数,用它建立form.

ShareMem是BORLANDMM.dll内存管理单元接口,它允许模块间共享动态申请内存。BORLANDMM.dll必须与使用了改单元的程序或者Dll一块儿部署。当程序或者动态库使用了ShareMem,其内存管理将会由BORLANDMM.dll接管。

只能看看local var窗口中的东西,然后再一层一层地找下去

implementation

八 在DLL中建立一个TMDIChildform
1 Dll中的MDIform.formstyle不用为fmMDIChild.
2 在Createform后写以下两句:

注意:
Linux下使用glibc的malloc来管理共享内存。】

6、更要命的是如果用Delphi编译了一个dll,你到C#想怎么用怎么用,你到了Delphi里,不行,你还得同时提供*.dcpil文件,否则不让你用,真是没话说了

function incc(a,b:Integer):Integer;stdcall; external 'Project1.dll'; //加载这个动态库。

[delphi]
function Showform(mainform:Tform):integer;stdcall 
var 
  form1: Tform1; 
  ptr:PLongInt; 
begin 
  ptr:[email protected](Application.Mainform);//先把dll的Mainform句柄保存起来,也无须释放,只不过是替换一下  
  ptr^:=LongInt(mainform);//用主调程序的mainform替换DLL的Mainform。Mainform是特殊的WINDOW,它专门管理 Application中的forms资源.  
//为什么不直接Application.Mainform := mainform,因为Application.Mainform是只读属性  
  form1:=Tform1.Create(mainform);//用参数建立  
end; 
备注:参数是主调程序的Application.Mainform 

    上述问题仅在windows里才有啊,呵呵,于是在dll里把Uses子句的top 1增加ShareMem引用,另外在应用程序的Project域的Uses子句第一个引用也加上ShareMem。最后编译,运行,没有看到哪个烦人的异常提示了:P

真是强忍着把那个大程序搞完,虽然用户用起来还行,但是这期间的开发过程真是要命阿。

{$R *.dfm}

function Showform(mainform:Tform):integer;stdcall
var
  form1: Tform1;
  ptr:PLongInt;
begin
  ptr:[email protected](Application.Mainform);//先把dll的Mainform句柄保存起来,也无须释放,只不过是替换一下
  ptr^:=LongInt(mainform);//用主调程序的mainform替换DLL的Mainform。Mainform是特殊的WINDOW,它专门管理 Application中的forms资源.
//为什么不直接Application.Mainform := mainform,因为Application.Mainform是只读属性
  form1:=Tform1.Create(mainform);//用参数建立
end;
备注:参数是主调程序的Application.Mainform

 

还好现在出了个Chrom,对他的各个方面还算比较满意的,但是发现好像有个bug

procedure TForm1.btn1Click(Sender: TObject);
var
aa:Integer;
begin
aa:=incc(StrToInt(edt1.Text),StrToInt(edt2.Text));//开始调用
ShowMessage(IntToStr(aa));//弹出结果。
end;

九 示例:
DLL源代码:

本文来自CSDN博客,转载请标明出处:

如果这样定义

end.

 

 

TA = class

二:相比静态调用,动态调用占用的资源要小点,哎呀,具体好处我就不说了,现在来看看具体怎么能实现,同样的在建立一个和静态调用的窗体。再用代码说话。

[delphi]

protected

unit Unit11;

library Project2; 
 
uses 
SysUtils, 
Classes, 
Dialogs, 
forms, 
Unit2 in 'Unit2.pas' {form2}; 
 
{$R *.RES} 
var 
ccc: Pchar; 
 
procedure Openform(mainform:Tform);stdcall; 
var 
  form1: Tform1; 
  ptr:PLongInt; 
begin 
  ptr:[email protected](Application.Mainform); 
  ptr^:=LongInt(mainform); 
  form1:=Tform1.Create(mainform); 
end; 
 
procedure InputCCC(Text: Pchar);stdcall; 
begin 
  ccc := Text; 
end; 
 
procedure ShowCCC;stdcall; 
begin 
  ShowMessage(String(ccc)); 
end; 
 
exports 
  Openform; 
  InputCCC, 
  ShowCCC; 
 
 
begin 
end. 
 
调用方源代码: 
unit Unit1; 
 
interface 
 
uses 
Windows, Messages, SysUtils, Classes, Graphics, Controls, forms, Dialogs, 
StdCtrls; 
 
type 
Tform1 = class(Tform) 
Button1: TButton; 
Button2: TButton; 
Edit1: TEdit; 
procedure Button1Click(Sender: TObject); 
procedure Button2Click(Sender: TObject); 
private 
{ Private declarations } 
public 
{ Public declarations } 
end; 
 
var 
form1: Tform1; 
 
implementation 
 
{$R *.DFM} 
procedure Openform(mainform:Tform);stdcall;External'project2.dll'; 
procedure ShowCCC;stdcall;External'project2.dll'; 
procedure InputCCC(Text: Pchar);stdcall;External'project2.dll'; 
 
procedure Tform1.Button1Click(Sender: TObject); 
var 
  Text: Pchar; 
begin 
  Text := Pchar(Edit1.Text); 
// Openform(Application.Mainform);//为了调MDICHILD  
InputCCC(Text);//为了实验DLL中的全局变量是否在各个应用程序间共享  
end; 
 
procedure Tform1.Button2Click(Sender: TObject); 
begin 
  ShowCCC;//这里表明WINDOWS 32位应用程序DLL中的全局变量也是在应用程序地址空间中,16位应用程序或许不同,没有做实验。  
end; 

   fa:integer;

interface

library Project2;

end;

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

uses
SysUtils,
Classes,
Dialogs,
forms,
Unit2 in 'Unit2.pas' {form2};

TB  = class(TA)

type
TForm1 = class(TForm)
edt1: TEdit;
edt2: TEdit;
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

{$R *.RES}
var
ccc: Pchar;

public

var
Form1: TForm1;

procedure Openform(mainform:Tform);stdcall;
var
  form1: Tform1;
  ptr:PLongInt;
begin
  ptr:[email protected](Application.Mainform);
  ptr^:=LongInt(mainform);
  form1:=Tform1.Create(mainform);
end;

  function BBB:TA;

implementation

procedure InputCCC(Text: Pchar);stdcall;
begin
  ccc := Text;
end;

end;

{$R *.dfm}

procedure ShowCCC;stdcall;
begin
  ShowMessage(String(ccc));
end;

function TB.BBB:TA;

type
Tmyfun = function (a,b:integer):Integer; stdcall;//定义一个函数类型,注意一点过程类型种的参数应该与DLL中用到的方法的参数一致。

exports
  Openform;
  InputCCC,
  ShowCCC;

var

procedure TForm1.btn1Click(Sender: TObject);
var
myhandle:THandle;
FPointer:Pointer;
Myfun :Tmyfun;
begin
myhandle:=LoadLibrary('C:Documents and SettingsAdministrator桌面新建文件夹Project1.dll') ;//加载这个DLL
if myhandle >0 then//加载成功就执行。
try
FPointer :=GetProcAddress(myhandle,PChar('incc')); //取函数的地址。
if FPointer <>nil then //如果函数存在就调用
begin
Myfun := Tmyfun(FPointer); 
showmessage(IntToStr(Myfun(StrToInt(edt1.Text),StrToInt(edt2.Text))));//弹出算出的结果。
end;
except
FreeLibrary(myhandle);
end;
end;

begin
end.

  rr:TB;

end.

调用方源代码:
unit Unit1;

begin

 

interface

  rr := new TB;

在Delphi中静态调用DLL 

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, forms, Dialogs,
StdCtrls;

  rr.fa : =1;

  调用一个DLL比写一个DLL要容易一些。首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较。同样的,我们先举一个静态调用的例子。

type
Tform1 = class(Tform)
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

result :=rr;

unit Unit1; 

var
form1: Tform1;

end;

interface 

implementation

就是编译不通过,感觉chrom对protected处理有点小问题

uses 
Windows, Messages, SysUtils, Classes, Graphics, 
Controls, Forms, Dialogs, StdCtrls; 

{$R *.DFM}
procedure Openform(mainform:Tform);stdcall;External'project2.dll';
procedure ShowCCC;stdcall;External'project2.dll';
procedure InputCCC(Text: Pchar);stdcall;External'project2.dll';

最后只能感叹,不用C#活得真累

type 
TForm1 = class(TForm) 
Edit1: TEdit; 
Button1: TButton; 
procedure Button1Click(Sender: TObject); 
private 
{ Private declarations } 
public 
{ Public declarations } 
end; 

procedure Tform1.Button1Click(Sender: TObject);
var
  Text: Pchar;
begin
  Text := Pchar(Edit1.Text);
// Openform(Application.Mainform);//为了调MDICHILD
InputCCC(Text);//为了实验DLL中的全局变量是否在各个应用程序间共享
end;

var 
Form1: TForm1; 

procedure Tform1.Button2Click(Sender: TObject);
begin
  ShowCCC;//这里表明WINDOWS 32位应用程序DLL中的全局变量也是在应用程序地址空间中,16位应用程序或许不同,没有做实验。
end;  

implementation 

 

{$R *.DFM} 

十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
1 与PowerBuilder混合编程
在定义不定长动态数组方面在函数退出清理堆栈时老出现不可重现的地址错,原因未明,大概与PB的编译器原理有关,即使PB编译成二进制代码也如此。

//本行以下代码为我们真正动手写的代码 

 

function TestDll(i:integer):integer;stdcall; 
external ’Delphi.dll’; 

在DELPHI中编写DLL时,如果DLL有创建ADO对象要被调用函数

procedure TForm1.Button1Click(Sender: TObject); 
begin 
Edit1.Text:=IntToStr(TestDll(1)); 
end; 

开始处写:CoInitialize(nil);
结束时写:CoUninitialize;
如要返回字符串要用PChar,最好用PChar用out或var方式返回,

end.

PChar的内存分配和释放在调用函数处理:GetMem(p, Size);FreeMem(p);

 

 

  上面的例子中我们在窗体上放置了一个编辑框(Edit)和一个按钮(Button),并且书写了很少的代码来测试我们刚刚编写的Delphi.dll。大家可以看到我们唯一做的工作是将TestDll函数的说明部分放在了implementation中,并且用external语句指定了Delphi.dll的位置。(本例中调用程序和Delphi.dll在同一个目录中。)让人兴奋的是,我们自己编写的TestDll函数很快被Delphi认出来了。您可做这样一个实验:输入“TestDll(”,很快Delphi就会用fly-by提示条提示您应该输入的参数是什么,就像我们使用Delphi中定义的其他函数一样简单。注意事项有以下一些: 
一、调用参数用stdcall 
  和前面提到的一样,当引用DLL中的函数和过程时也要使用stdcall参数,原因和前面提到的一样。 

[delphi]
procedure CommonDLL(AHnd: THandle; //AApp: TApplication;  
ADllFileName: PChar; 
AConnStr: PChar; AUserID: integer; ABillTypeID: integer); 
var 
  LPtr:PLongint; 
  strCon: widestring; 
  strDllFileName: string; 
begin 
  CoInitialize(nil); 
  strCon := StrPas(AConnstr); 
  strDllFileName := StrPas(ADllFileName); 
try 
  Application.Handle := AHnd; 
  Screen := AScr; 
  LPtr := @Application.Mainform; 
  LPtr^ := Longint(AApp.Mainform); 
 
finally 
end; 
  CoUninitialize; 
end; 
 
 
在加载DLL时要保存原来的句柄,退出DLL时还原句柄: 
var 
OldHnd: THandle; 
// OldApp: TApplication;  
OldScr: TScreen; 
 
procedure InitDll(dWseason: DWORD); 
begin 
  case dWseason of 
  DLL_PROCESS_ATTACH: 
begin 
  OldHnd := Application.Handle; 
// OldApp := Application;  
  OldScr := Screen; 
end; 
DLL_PROCESS_DETACH: 
begin 
  Application.Handle := OldHnd; 
// OldApp := GApplication;  
  OldScr := GScreen; 
end; 
end; 
end; 
 
begin 
  DllProc := @InitDll; 
  InitDll(DLL_PROCESS_ATTACH); 
end. 

二、用external语句指定被调用的DLL文件的路径和名称 
  正如大家看到的,我们在external语句中指定了所要调用的DLL文件的名称。没有写路径是因为该DLL文件和调用它的主程序在同一目录下。如果该DLL文件在C:,则我们可将上面的引用语句写为external ’C:Delphi.dll’。注意文件的后缀.dll必须写上。 

procedure CommonDLL(AHnd: THandle; //AApp: TApplication;
ADllFileName: PChar;
AConnStr: PChar; AUserID: integer; ABillTypeID: integer);
var
  LPtr:PLongint;
  strCon: widestring;
  strDllFileName: string;
begin
  CoInitialize(nil);
  strCon := StrPas(AConnstr);
  strDllFileName := StrPas(ADllFileName);
try
  Application.Handle := AHnd;
  Screen := AScr;
  LPtr := @Application.Mainform;
  LPtr^ := Longint(AApp.Mainform);

三、不能从DLL中调用全局变量 
  如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。 

finally
end;
  CoUninitialize;
end;

四、被调用的DLL必须存在 
  这一点很重要,使用静态调用方法时要求所调用的DLL文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。

在加载DLL时要保存原来的句柄,退出DLL时还原句柄:
var
OldHnd: THandle;
// OldApp: TApplication;
OldScr: TScreen;

在Delphi中动态调用DLL 
  动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。

procedure InitDll(dWseason: DWORD);
begin
  case dWseason of
  DLL_PROCESS_ATTACH:
begin
  OldHnd := Application.Handle;
// OldApp := Application;
  OldScr := Screen;
end;
DLL_PROCESS_DETACH:
begin
  Application.Handle := OldHnd;
// OldApp := GApplication;
  OldScr := GScreen;
end;
end;
end;

#include 

begin
  DllProc := @InitDll;
  InitDll(DLL_PROCESS_ATTACH);
end.

extern ”C” _declspec(dllexport) 
int WINAPI TestC(int i) 

return i; 
}

在DLL被用函数中有创建窗体对象,一定要记得传Application或者Application.Handle。
DLL中的窗体一般要用formClass.Create(application)来创建比较好。

  编译后生成一个DLL文件,在这里我们称该文件为Cpp.dll,该DLL中只有一个返回整数类型的函数TestC。为了方便说明,我们仍然引用上面的调用程序,只是将原来的Button1Click过程中的语句用下面的代码替换掉了。 

我见过有DLL会传Screen对象到DLL,这个我不没具体了解为什么,也希望有知道朋友告诉我有一下传Screen对象有什么作用,有什么优缺点。

procedure TForm1.Button1Click(Sender: TObject); 
type 
TIntFunc=function(i:integer):integer;stdcall; 
var 
Th:Thandle; 
Tf:TIntFunc; 
Tp:TFarProc; 
begin 
Th:=LoadLibrary(’Cpp.dll’); {装载DLL} 
if Th>0 then 
try 
Tp:=GetProcAddress(Th,PChar(’TestC’)); 
if Tp<>nil 
then begin 
Tf:=TIntFunc(Tp); 
Edit1.Text:=IntToStr(Tf(1)); {调用TestC函数} 
end 
else 
ShowMessage(’TestC函数没有找到’); 
finally 
FreeLibrary(Th); {释放DLL} 
end 
else 
ShowMessage(’Cpp.dll没有找到’); 
end;

 

  大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改LoadLibrary(’Cpp.dll’)中的DLL名称为’Delphi.dll’就可动态更改所调用的DLL。 

传Screen是为了使主程序主窗体的MDIChildCount正常增加,
否则,不管打开多少DLL中的MDIChild窗体,MDIChildCount都不会增加。

一、定义所要调用的函数或过程的类型 
  在上面的代码中我们定义了一个TIntFunc类型,这是对应我们将要调用的函数TestC的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。 

第一章 为什么要使用动态链接库(DLL) top
提起DLL您一定不会陌生,在Windows中有着大量的以DLL为后缀的文件,它们是保证Windows正常运行和维护升级的重要保证。(举个例子,笔者的Win95 System目录下尽有500多个DLL文件。)其实,DLL是一种特殊的可执行文件。说它特殊主要是因为一般它都不能直接运行,需要宿主程序比如*.EXE程序或其他DLL的动态调用才能够使用。简单的说,在通常情况下DLL是经过编译的函数和过程的集合。
使用DLL技术主要有以下几个原因:

二、释放所调用的DLL 
  我们用LoadLibrary动态的调用了一个DLL,但要记住必须在使用完后手动地用FreeLibrary将该DLL释放掉,否则该DLL将一直占用内存直到您退出Windows或关机为止。 

一、减小可执行文件大小。
DLL技术的产生有很大一部分原因是为了减小可执行文件的大小。当操作系统进入Windows时代后,其大小已经达到几十兆乃至几百兆。试想如果还是使用DOS时代的单执行文件体系的话一个可执行文件的大小可能将达到数十兆,这是大家都不能接受的。解决的方法就是采用动态链接技术将一个大的可执行文件分割成许多小的可执行程序。

  现在我们来评价一下两种调用DLL的方法的优缺点。静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;但是静态方法不能灵活地在运行时装卸所需的DLL,而是在主程序开始运行时就装载指定的DLL直到程序结束时才释放该DLL,另外只有基于编译器和链接器的系统(如Delphi)才可以使用该方法。动态方法较好地解决了静态方法中存在的不足,可以方便地访问DLL中的函数和过程,甚至一些老版本DLL中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。

二、实现资源共享。
这里指的资源共享包括很多方面,最多的是内存共享、代码共享等等。早期的程序员经常碰到这样的事情,在不同的编程任务中编写同样的代码。这种方法显然浪费了很多时间,为了解决这个问题人们编写了各种各样的库。但由于编程语言和环境的不同这些库一般都不能通用,而且用户在运行程序时还需要这些库才行,极不方便。DLL的出现就像制定了一个标准一样,使这些库有了统一的规范。这样一来,用不同编程语言的程序员可以方便的使用用别的编程语言编写的DLL。另外,DLL还有一个突出的特点就是在内存中只装载一次,这一点可以节省有限的内存,而且可以同时为多个进程服务。

使用DLL的实用技巧 top 

三、便于维护和升级。
细心的朋友可能发现有一些DLL文件是有版本说明的。(查看DLL文件的属性可以看到,但不是每一个DLL文件都有)这是为了便于维护和升级。举个例子吧,早期的Win95中有一个BUG那就是在闰年不能正确显示2月29日这一天。后来,Microsoft发布了一个补丁程序纠正了这个BUG。值得一提的是,我们并没有重装Win95,而是用新版本的DLL代替了旧版本的DLL。(具体是哪一个DLL文件笔者一时想不起来了。)另一个常见的例子是驱动程序的升级。例如,著名的DirectX就多次升级,现在已经发展到了6.0版了。更妙的是,当我们试图安装较低版本的DLL时,系统会给我们提示,避免人为的操作错误。例如我们升级某硬件的驱动程序时,经常碰到Windows提示我们当前安装的驱动程序比原来的驱动程序旧。

一、编写技巧 
  1 、为了保证DLL的正确性,可先编写成普通的应用程序的一部分,调试无误后再从主程序中分离出来,编译成DLL。 

四、比较安全。
这里说的安全也包括很多方面。比如,DLL文件遭受病毒的侵害机率要比普通的EXE文件低很多。另外,由于是动态链接的,这给一些从事破坏工作的“高手”们多少带来了一些反汇编的困难。

  2 、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名称,如:Edit1.Text中的Edit1名称;或者自定义非Windows定义的类型,如某种记录。 

第二章 在Delphi中编写DLL

  3 、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注释。 

注意:在这里笔者假定读者使用的是Delphi 3或Delphi 4开场白说了那么多,总该言归正传了。编写DLL其实也不是一件十分困难的事,只是要注意一些事项就够了。为便于说明,我们先举一个例子。

  4 、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用SysUtils单元。 

library Delphi;

  5 、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如Dialogs单元。例如一般情况下,我们可以不引用Classes单元,这样可使编译后的DLL减小大约16Kb。 

uses
SysUtils,
Classes;

二、调用技巧 
  1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C++编写的DLL例子中,如果去掉extern ”C”语句,C++会编译出一些奇怪的函数名,原来的TestC函数会被命名为@TestC$s等等可笑的怪名字,这是由于C++采用了C++ name mangling技术。这个函数名在Delphi中是非法的,我们可以这样解决这个问题: 
改写引用函数为

function TestDll(i:integer):integer;stdcall;
begin
Result:=i;
end;

function TestC(i:integer):integer;stdcall; 
external ’Cpp.dll’;name ’@TestC$s’;

exports
TestDll;

其中name的作用就是重命名。 

begin
end.

  2 、可把我们编写的DLL放到Windows目录下或者Windowssystem目录下。这样做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统目录中的地步吧! 

只要编译上面的代码,就可以得到一个名为Delphi.dll的动态链接库。

三、调试技巧
  1 、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就是在Run|parameters菜单中设置一个宿主程序。在Local页的Host Application栏中添上宿主程序的名字就可进行单步调试、断点观察和运行了。 

现在,让我们来看看有哪些需要注意的地方。

  2 、添加DLL的版本信息。开场白中提到了版本信息对于DLL是很重要的,如果包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们如果直接使用Project|options菜单中Version选项是不行的,这一点Delphi的帮助文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例: 

一、在DLL中编写的函数或过程都必须加上stdcall调用参数。

library Delphi; 

二、所写的函数和过程应该用exports语句声明为外部函数。
三、当使用了长字符串类型的参数、变量时要引用ShareMem。
Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2G。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例:

uses 
SysUtils, 
Classes; 

uses
  ShareMem,
SysUtils,
Classes;

{$R *.RES} 
//注意,上面这行代码必须加在这个位置 

还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。

function TestDll(i:integer):integer;stdcall; 
begin 
Result:=i; 
end; 

第三章 在Delphi中静态调用DLL

exports 
TestDll; 

调用一个DLL比写一个DLL要容易一些。首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较。同样的,我们先举一个静态调用的例子。

begin 
end.

[delphi]
unit Unit1;  
 
interface  
 
uses  
Windows, Messages, SysUtils, Classes, Graphics,  
Controls, forms, Dialogs, StdCtrls;  
 
type  
Tform1 = class(Tform)  
Edit1: TEdit;  
Button1: TButton;  
procedure Button1Click(Sender: TObject);  
private  
{ Private declarations }  
public  
{ Public declarations }  
end;  
 
var  
form1: Tform1;  
 
implementation  
 
{$R *.DFM}  
 
//本行以下代码为我们真正动手写的代码   
 
function TestDll(i:integer):integer;stdcall; external ’Delphi.dll’;  
 
procedure Tform1.Button1Click(Sender: TObject);  
begin  
  Edit1.Text:=IntToStr(TestDll(1));  
end;  
 
end.  

  3 、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符数字和下划线混合的方式。如:jl_try16.dll。 

unit Unit1;

  4 、如果您原来在Delphi 1或Delphi 2中已经编译了某些DLL的话,您原来编译的DLL是16位的。只要将源代码在新的Delphi 3或Delphi 4环境下重新编译,就可以得到32位的DLL了。 

interface

[后记]:除了上面介绍的DLL最常用的使用方法外,DLL还可以用于做资源的载体。例如,在Windows中更改图标就是使用的DLL中的资源。另外,熟练掌握了DLL的设计技术,对使用更为高级的OLE、COM以及ActiveX编程都有很多益处。

uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, forms, Dialogs, StdCtrls;

 

type
Tform1 = class(Tform)
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
form1: Tform1;

implementation

{$R *.DFM}

//本行以下代码为我们真正动手写的代码

function TestDll(i:integer):integer;stdcall; external ’Delphi.dll’;

procedure Tform1.Button1Click(Sender: TObject);
begin
  Edit1.Text:=IntToStr(TestDll(1));
end;

end.

 

 

一、调用参数用stdcall。
和前面提到的一样,当引用DLL中的函数和过程时也要使用stdcall参数,原因和前面提到的一样。

二、用external语句指定被调用的DLL文件的路径和名称。
正如大家看到的,我们在external语句中指定了所要调用的DLL文件的名称。没有写路径是因为该DLL文件和调用它的主程序在同一目录下。如果该DLL文件在C:,则我们可将上面的引用语句写为external ’C:Delphi.dll’。注意文件的后缀.dll必须写上。

三、不能从DLL中调用全局变量。
如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给DLL。

四、被调用的DLL必须存在。

 

第四章 在Delphi中动态调用DLL

动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。

#include

extern ”C” _declspec(dllexport)
int WINAPI TestC(int i)
{
return i;
}

编译后生成一个DLL文件,在这里我们称该文件为Cpp.dll,该DLL中只有一个返回整数类型的函数TestC。为了方便说明,我们仍然引用上面的调用程序,只是将原来的Button1Click过程中的语句用下面的代码替换掉了。

[delphi]
procedure Tform1.Button1Click(Sender: TObject);  
type  
TIntFunc=function(i:integer):integer;stdcall;  
var  
  Th:Thandle;  
  Tf:TIntFunc;  
  Tp:TFarProc;  
begin  
  Th:=LoadLibrary(’Cpp.dll’); {装载DLL}  
  if Th>0 then  
try  
  Tp:=GetProcAddress(Th,PChar(’TestC’));  
  if Tp<>nil  then begin  
    Tf:=TIntFunc(Tp);  
     Edit1.Text:=IntToStr(Tf(1)); {调用TestC函数}  
  end  
  else  
      ShowMessage(’TestC函数没有找到’);  
finally  
   FreeLibrary(Th); {释放DLL}  
end  
else  
   ShowMessage(’Cpp.dll没有找到’);  
end;  

procedure Tform1.Button1Click(Sender: TObject);
type
TIntFunc=function(i:integer):integer;stdcall;
var
  Th:Thandle;
  Tf:TIntFunc;
  Tp:TFarProc;
begin
  Th:=LoadLibrary(’Cpp.dll’); {装载DLL}
  if Th>0 then
try
  Tp:=GetProcAddress(Th,PChar(’TestC’));
  if Tp<>nil  then begin
    Tf:=TIntFunc(Tp);
     Edit1.Text:=IntToStr(Tf(1)); {调用TestC函数}
  end
  else
      ShowMessage(’TestC函数没有找到’);
finally
   FreeLibrary(Th); {释放DLL}
end
else
   ShowMessage(’Cpp.dll没有找到’);
end;

大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改LoadLibrary(’Cpp.dll’)中的DLL名称为’Delphi.dll’就可动态更改所调用的DLL。

一、定义所要调用的函数或过程的类型。
在上面的代码中我们定义了一个TIntFunc类型,这是对应我们将要调用的函数TestC的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。

二、释放所调用的DLL。
我们用LoadLibrary动态的调用了一个DLL,但要记住必须在使用完后手动地用FreeLibrary将该DLL释放掉,否则该DLL将一直占用内存直到您退出Windows或关机为止。

两种调用DLL的方法的优缺点。

静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;

但是静态方法不能灵活地在运行时装卸所需的DLL,而是在主程序开始运行时就装载指定的DLL直到程序结束时才释放该DLL,另外只有基于编译器和链接器的系统(如Delphi)才可以使用该方法。

动态方法较好地解决了静态方法中存在的不足,可以方便地访问DLL中的函数和过程,甚至一些老版本DLL中新添加的函数或过程;

但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。

 

第五章 使用DLL的实用技巧

一、编写技巧。
1 、为了保证DLL的正确性,可先编写成普通的应用程序的一部分,调试无误后再从主程序中分离出来,编译成DLL。

2 、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名称,如:Edit1.Text中的Edit1名称;或者自定义非Windows定义的类型,如某种记录。

3 、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注释。

4 、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用SysUtils单元。

5 、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如Dialogs单元。例如一般情况下,我们可以不引用Classes单元,这样可使编译后的DLL减小大约16Kb。

二、调用技巧。
1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C++编写的DLL例子中,如果去掉extern ”C”语句,C++会编译出一些奇怪的函数名,原来的TestC函数会被命名为@TestC$s等等可笑的怪名字,这是由于C++采用了C++ name mangling技术。这个函数名在Delphi中是非法的,我们可以这样解决这个问题:
改写引用函数为
function TestC(i:integer):integer;stdcall;
external ’Cpp.dll’;name ’@TestC$s’;
其中name的作用就是重命名。

2 、可把我们编写的DLL放到Windows目录下或者Windowssystem目录下。这样做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统目录中的地步吧!

三、调试技巧。
1 、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就是在Run|parameters菜单中设置一个宿主程序。在Local页的Host Application栏中添上宿主程序的名字就可进行单步调试、断点观察和运行了。

2 、添加DLL的版本信息。开场白中提到了版本信息对于DLL是很重要的,如果包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们如果直接使用Project|options菜单中Version选项是不行的,这一点Delphi的帮助文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例:

library Delphi;

uses
SysUtils,
Classes;

{$R *.RES}
//注意,上面这行代码必须加在这个位置

function TestDll(i:integer):integer;stdcall;
begin
  Result:=i;
end;

exports
TestDll;

begin
end.

3 、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符数字和下划线混合的方式。如:jl_try16.dll。

4 、如果您原来在Delphi 1或Delphi 2中已经编译了某些DLL的话,您原来编译的DLL是16位的。只要将源代码在新的Delphi 3或Delphi 4环境下重新编译,就可以得到32位的DLL了。

[后记]:除了上面介绍的DLL最常用的使用方法外,DLL还可以用于做资源的载体。例如,在Windows中更改图标就是使用的DLL中的资源。另外,熟练掌握了DLL的设计技术,对使用更为高级的OLE、COM以及ActiveX编程都有很多益处。

Delphi中如何调用DLL
马上想得到的使用说明有以下几点:

  1. 所需动态连结的 DLL 须置放在与执行档同一目录或Windows System 目录2. 确认 DLL export 出来的函式的原型, 以目前的情况而言, 通常只拿得到 C语言的函数原型,这时要注意 C 与 object Pascal 相对应的型别, 如果需要, 在interface 一节定义所需的资料类别

  2. 在 implementation 节中宣告欲使用的函式, 语法大致如下:

procedure ProcName(Argu...); far; external ’DLL档名’;

index n;

function FuncName(Argr...): DataType; far;

external ’DLL档名’; index n;

宣告时, index n 如果不写, 便是参考资料中所谓 import by name 的方式, 此时, 由於需要从 DLL 的 name table 中找出这个函式, 因此, 连结执行速度比import by ordinal稍慢一些, 此外, 还有一种 by new name, 由於我没用过, 您可以查一参考资料, 大意是可以 import 後改用另一个程式命名呼叫这个函式

  1. 然後, 呼叫与使用就与一般的Delphi 没有两样5. 上述是直接写到呼叫DLL函式的程式单元中, 此外,也可以将DLL的呼叫宣告集中到一个程式单元(Import unit), Delphi 内附的 WinTypes, WinProcs是一个例子,

您可以参考一下,同时观察一下 C 与 Pascal 互相对应的资料型态6. 除了上述的 static import 的方式, 另外有一种 dynamic import 的写法,先宣告一个程序类型(procedural-type),程式执行时, 以LoadLibrary() API Load进来後, 再以 GetProcAddress() API 取得函式的位址的方式来连结呼叫, 在ObjectPascal Language Guide P.132-133 有一个例子, 您可以参考看看

如果要举个例子, 以下是从我以前的程式节录出来的片断:

 

[delphi]
(* for CWindows 3.1 *) 
 
unit Ime31; 
 
interface 
 
uses 
 
SysUtils, WinTypes, WinProcs, Dialogs; 
 
type 
 
(* 必要的资料型态宣告 *) 
 
  tDateNTime = record 
  wYear, wMonth, wDay: word; 
  wHour, wMin, wSec: word; 
end; 
  TImePro = record 
  hWndIme: HWnd; { IME handle } 
  dtInstDate: tDateNTime; { Date and time of installation } 
  wVersion: word; { the version of IME } 
 
  szDescription: array[0..49] of byte; { Description of IME module} 
  szName: array[0..79] of byte; { Module name of the IME } 
  szOptions: array[0..29] of byte; { options of IME at startup} 
 fEnable: boolean; { IME status; True=activated,False=deactivated } 
end; 
 
pTImePro = ^TImePro; 
 
function SetIme(const sImeFileName: string): boolean; far; 
 
implementation 
 
(* begin 呼叫 winnls.dll export 函数的宣告 *) 
 
function ImpSetIme(hWndIme: HWND; lpImePro: pTImePro): boolean;far; external ’winnls.dll’; 
 
(* end 呼叫 winnls.dll export 函数的宣告 *) 
 
(* -------------------------------------------------- *) 
 
(* SetIme(const sImeFileName: string): boolean;
 
(* ======
 
(* 切换到某一特定的输入法
 
(*
 
(* 传入引数:
 
(* sImeFileName: 输入法 IME 档名, 例: phon.ime;
 
(* 空字串: 英数输入法
 
(*
 
(* 传回值:
 
(* True: 切换成功
 
(* False: 失败
 
(* -------------------------------------------------- *) 
 
function SetIme(const sImeFileName: string): boolean; 
var 
  pImePro: pTImePro; 
begin 
Result := False; 
if MaxAvail < SizeOf(TImePro) then 
begin 
MessageDlg(’记忆体不足’, mtWarning, [mbOk], 0); 
Exit; 
end 
else 
begin 
New(pImePro); 
try 
if sImeFileName = ’’ then (* 空字串, 还原到英数输入法 *) 
pImePro^.szName[0] := 0 
else 
StrPCopy(@pImePro^.szName, sImeFileName); 
Result := ImpSetIme(0, pImePro); (* 呼叫 ImpSetIme *) 
finally 
Dispose(pImePro); 
end; { of try } 
end; 
end; { of SetIme } 
end. 
 
;  

(* for CWindows 3.1 *)

unit Ime31;

interface

uses

SysUtils, WinTypes, WinProcs, Dialogs;

type

(* 必要的资料型态宣告 *)

  tDateNTime = record
  wYear, wMonth, wDay: word;
  wHour, wMin, wSec: word;
end;
  TImePro = record
  hWndIme: HWnd; { IME handle }
  dtInstDate: tDateNTime; { Date and time of installation }
  wVersion: word; { the version of IME }

  szDescription: array[0..49] of byte; { Description of IME module}
  szName: array[0..79] of byte; { Module name of the IME }
  szOptions: array[0..29] of byte; { options of IME at startup}
 fEnable: boolean; { IME status; True=activated,False=deactivated }
end;

pTImePro = ^TImePro;

function SetIme(const sImeFileName: string): boolean; far;

implementation

(* begin 呼叫 winnls.dll export 函数的宣告 *)

function ImpSetIme(hWndIme: HWND; lpImePro: pTImePro): boolean;far; external ’winnls.dll’;

(* end 呼叫 winnls.dll export 函数的宣告 *)

(* -------------------------------------------------- *)

(* SetIme(const sImeFileName: string): boolean;

(* ======

(* 切换到某一特定的输入法

(*

(* 传入引数:

(* sImeFileName: 输入法 IME 档名, 例: phon.ime;

(* 空字串: 英数输入法

(*

(* 传回值:

(* True: 切换成功

(* False: 失败

(* -------------------------------------------------- *)

function SetIme(const sImeFileName: string): boolean;
var
  pImePro: pTImePro;
begin
Result := False;
if MaxAvail < SizeOf(TImePro) then
begin
MessageDlg(’记忆体不足’, mtWarning, [mbOk], 0);
Exit;
end
else
begin
New(pImePro);
try
if sImeFileName = ’’ then (* 空字串, 还原到英数输入法 *)
pImePro^.szName[0] := 0
else
StrPCopy(@pImePro^.szName, sImeFileName);
Result := ImpSetIme(0, pImePro); (* 呼叫 ImpSetIme *)
finally
Dispose(pImePro);
end; { of try }
end;
end; { of SetIme }
end.

;

 

2007-4-6 19:25:51 如要返回字符串要用PChar,最好用PChar用out或var方式返回,PChar的内存分配和释放在调用函数处理:GetMem(p, Size); FreeMem(p);

[delphi]
而在被调用函数写的方式应该是: 
 
 
procedure GetStr(var Pstr: PChar); 
var 
str: string 
begin 
str := 'return string'; 
strCopy(PStr, PChar(str)); 
end; 
 
调用函数写法: 
TGetStr= procedure(var Pstr: PChar); 
 
funtion GetDllStr: string 
var 
DllHnd: THandle; 
GetStr: TGetStr; 
Str: PChar; 
strPath: string; 
begin 
AHaveWhere := 0; 
DllHnd := LoadLibrary(PChar('testdll')); 
try 
if (DllHnd <> 0) then 
begin 
@GetStr :=GetProcAddress(DllHnd, 'GetStr'); 
if (@GetStr<>nil) then 
begin 
GetMem(Str, 1024); 
try 
GetStr(Filter); 
result := StrPas(Filter); 
finally 
FreeMem(Str); 
end; 
end 
else 
begin 
application.MessageBox(PChar('DLL加载出错,DLL可能不存在!'), PChar('错误'), 
MB_ICONWARNING or MB_OK); 
end; 
end; 
finally 
FreeLibrary(DllHnd); 
 
<SPAN>end;</SPAN> 

Dll的制作一般步骤 二 参数传递 三 DLL的初始化和退出清理[如果需要初始化和退出清理] 四 全局变量的使用 五 调用静态载入 六 调用动态...

本文由10bet手机官网发布于多线程,转载请注明出处:终于体会到了,异常的解决办法

上一篇:没有了 下一篇:没有了
猜你喜欢
热门排行
精彩图文