17
7月
Delphi制作DLL
一 Dll的制作一般步骤
二 参数传递
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
四 全局变量的使用
五 调用静态载入
六 调用动态载入
七 在DLL建立一个TForM
八 在DLL中建立一个TMDIChildForM
九 示例:
十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
十一 相关资料
一 Dll的制作一般分为以下几步:
1 在一个DLL工程里写一个过程或函数
2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。
二 参数传递
1 参数类型最好与window C++的参数类型一致。不要用DELPHI的数据类型。
2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。
3 用stdcall声明后缀。
4 最好大小写敏感。
5 无须用far调用后缀,那只是为了与windows 16位程序兼容。
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:
procedure DllEnterPoint(dwReason: DWORD);far;stdcall;
dwReason参数有四种类型:
DLL_PROCESS_ATTACH:进程进入时
DLL_PROCESS_DETACH:进程退出时
DLL_THREAD_ATTACH :线程进入时
DLL_THREAD_DETACH :线程退出时
在初始化部分写:
DLLProc := @DLLEnterPoint;
DllEnterPoint(DLL_PROCESS_ATTACH);
2 如Form上有TdcomConnection组件,就Uses Activex,在初始化时写一句CoInitialize (nil);
3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。
四 全局变量的使用
在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。
五 调用静态载入
1 客户端函数声名:
1)大小写敏感。
2)与DLL中的声明一样。
如: showform(form:Tform);Far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windows c++一样。
4)调用时DLL必须在windows搜索路径中,顺序是:当前目录;Path路径;windows;widows\system;windows\ssystem32;
六 调用动态载入
1 建立一种过程类型(或者是一个Function)[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。如:
type
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;
七 在DLL建立一个TForM
1 把你的Form Uses到Dll中,你的Form用到的关联的单元也要Uses进来[这是最麻烦的一点,因为你的Form或许Uses了许多特殊的单元或函数]
2 传递一个Application参数,用它建立Form.
八 在DLL中建立一个TMDIChildForM
1 Dll中的MDIForm.FormStyle不用为fmMDIChild.
2 在CreateForm后写以下两句:
function ShowForm(mainForm:TForm):integer;stdcall
var
Form1: TForm1;
ptr:PLongInt;
begin
ptr:=@(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
九 示例:
DLL源代码:
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:=@(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;
十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
1 与PowerBuilder混合编程
在定义不定长动态数组方面在函数退出清理堆栈时老出现不可重现的地址错,原因未明,大概与PB的编译器原理有关,即使PB编译成二进制代码也如此。
在DELPHI中编写DLL时,如果DLL有创建ADO对象要被调用函数开始处写:CoInitialize(nil);
结束时写:CoUninitialize;
如要返回字符串要用PChar,最好用PChar用out或var方式返回,PChar的内存分配和释放在调用函数处理:GetMem(p, Size);FreeMem(p);
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.
在DLL被用函数中有创建窗体对象,一定要记得传Application或者Application.Handle。
DLL中的窗体一般要用FormClass.Create(application)来创建比较好。
我见过有DLL会传Screen对象到DLL,这个我不没具体了解为什么,也希望有知道朋友告诉我有一下传Screen对象有什么作用,有什么优缺点。
传Screen是为了使主程序主窗体的MDIChildCount正常增加,
否则,不管打开多少DLL中的MDIChild窗体,MDIChildCount都不会增加。
第一章 为什么要使用动态链接库(DLL) top
提起DLL您一定不会陌生,在Windows中有着大量的以DLL为后缀的文件,它们是保证Windows正常运行和维护升级的重要保证。(举个例子,笔者的Win95 System目录下尽有500多个DLL文件。)其实,DLL是一种特殊的可执行文件。说它特殊主要是因为一般它都不能直接运行,需要宿主程序比如*.EXE程序或其他DLL的动态调用才能够使用。简单的说,在通常情况下DLL是经过编译的函数和过程的集合。
使用DLL技术主要有以下几个原因:
一、减小可执行文件大小。
DLL技术的产生有很大一部分原因是为了减小可执行文件的大小。当操作系统进入Windows时代后,其大小已经达到几十兆乃至几百兆。试想如果还是使用DOS时代的单执行文件体系的话一个可执行文件的大小可能将达到数十兆,这是大家都不能接受的。解决的方法就是采用动态链接技术将一个大的可执行文件分割成许多小的可执行程序。
二、实现资源共享。
这里指的资源共享包括很多方面,最多的是内存共享、代码共享等等。早期的程序员经常碰到这样的事情,在不同的编程任务中编写同样的代码。这种方法显然浪费了很多时间,为了解决这个问题人们编写了各种各样的库。但由于编程语言和环境的不同这些库一般都不能通用,而且用户在运行程序时还需要这些库才行,极不方便。DLL的出现就像制定了一个标准一样,使这些库有了统一的规范。这样一来,用不同编程语言的程序员可以方便的使用用别的编程语言编写的DLL。另外,DLL还有一个突出的特点就是在内存中只装载一次,这一点可以节省有限的内存,而且可以同时为多个进程服务。
三、便于维护和升级。
细心的朋友可能发现有一些DLL文件是有版本说明的。(查看DLL文件的属性可以看到,但不是每一个DLL文件都有)这是为了便于维护和升级。举个例子吧,早期的Win95中有一个BUG那就是在闰年不能正确显示2月29日这一天。后来,Microsoft发布了一个补丁程序纠正了这个BUG。值得一提的是,我们并没有重装Win95,而是用新版本的DLL代替了旧版本的DLL。(具体是哪一个DLL文件笔者一时想不起来了。)另一个常见的例子是驱动程序的升级。例如,著名的DirectX就多次升级,现在已经发展到了6.0版了。更妙的是,当我们试图安装较低版本的DLL时,系统会给我们提示,避免人为的操作错误。例如我们升级某硬件的驱动程序时,经常碰到Windows提示我们当前安装的驱动程序比原来的驱动程序旧。
四、比较安全。
这里说的安全也包括很多方面。比如,DLL文件遭受病毒的侵害机率要比普通的EXE文件低很多。另外,由于是动态链接的,这给一些从事破坏工作的“高手”们多少带来了一些反汇编的困难。
第二章 在Delphi中编写DLL
注意:在这里笔者假定读者使用的是Delphi 3或Delphi 4开场白说了那么多,总该言归正传了。编写DLL其实也不是一件十分困难的事,只是要注意一些事项就够了。为便于说明,我们先举一个例子。
library Delphi;
uses
SysUtils,
Classes;
function TestDll(i:integer):integer;stdcall;
begin
Result:=i;
end;
exports
TestDll;
begin
end.
上面的例子是不是很简单?熟悉Delphi的朋友可以看出以上代码和一般的Delphi程序的编写基本是相同的,只是在TestDll函数后多了一个stdcall参数并且用exports语句声明了TestDll函数。只要编译上面的代码,就可以得到一个名为Delphi.dll的动态链接库。现在,让我们来看看有哪些需要注意的地方。 一、在DLL中编写的函数或过程都必须加上stdcall调用参数。在Delphi 1或Delphi 2环境下该调用参数是far。从Delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响DLL的编译和生成,但当调用这个DLL时会发生很严重的错误,导致操作系统的死锁。原因是register参数是Delphi的默认参数。
二、所写的函数和过程应该用exports语句声明为外部函数。
正如大家看到的,TestDll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(Quick View)”功能查看该DLL文件。(如果没有“快速查看”选项可以从Windows CD上安装。)TestDll函数会出现在Export Table栏中。另一个很充分的理由是,如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。
三、当使用了长字符串类型的参数、变量时要引用ShareMem。
Delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但Delphi中string类型在默认情况下长度可以达到2G。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用ShareMem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例:
uses
ShareMem,
SysUtils,
Classes;
还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点Delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为Pchar或ShortString(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。
第三章 在Delphi中静态调用DLL
调用一个DLL比写一个DLL要容易一些。首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较。同样的,我们先举一个静态调用的例子。
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.
上面的例子中我们在窗体上放置了一个编辑框(Edit)和一个按钮(Button),并且书写了很少的代码来测试我们刚刚编写的Delphi.dll。大家可以看到我们唯一做的工作是将TestDll函数的说明部分放在了implementation中,并且用external语句指定了Delphi.dll的位置。(本例中调用程序和Delphi.dll在同一个目录中。)让人兴奋的是,我们自己编写的TestDll函数很快被Delphi认出来了。您可做这样一个实验:输入“TestDll(”,很快Delphi就会用fly-by提示条提示您应该输入的参数是什么,就像我们使用Delphi中定义的其他函数一样简单。注意事项有以
下一些:
一、调用参数用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必须存在。
这一点很重要,使用静态调用方法时要求所调用的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过程中的语句用下面的代码替换掉了。
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目录下或者Windows\system目录下。这样做可以在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 一节定义所需的资料类别
3. 在 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 後改用另一个程式命名呼叫这个函式
4. 然後, 呼叫与使用就与一般的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 有一个例子, 您可以参考看看
如果要举个例子, 以下是从我以前的程式节录出来的片断:
(* 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.
;
DELPHI 中DLL开发常见问题的讨论:
http://www.delphibbs.com/delphibbs/DispQ.asp?LID=3685176
2007-4-6 19:25:51 如要返回字符串要用PChar,最好用PChar用out或var方式返回,PChar的内存分配和释放在调用函数处理:GetMem(p, Size); FreeMem(p);
而在被调用函数写的方式应该是:
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);
end;
17
7月
Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝。动态链接库就是在这种情况下出现的。动态链接库不用重复编译或链接,一旦装入内存,Dlls函数可以被系统中的任何正在运行的应用程序软件所使用,而不必再将DLLs函数的另一拷贝装入内存。 任何应用程序都可以共享由装入内存的DLLs管理的内存资源块。只包含共享数据的DLLs称为资源文件。
1.一般工程文件的头标用program关键字,而DLLs工程文件头标用library 关键字。不同的关键字通知编译器生成不同的可执行文件。用program关键字生成的是.exe文件,而用library关键字生成的是.dll文件;
2.假如DLLs要输出供其它应用程序使用的函数或过程,则必须将这些函数或过程列在exports子句中。而这些函数或过程本身必须用export编译指令进行编译。
根据DLLs完成的功能,我们把DLLs分为如下的三类:
1.完成一般功能的DLLs;2.用于数据交换的DLLs;3.用于窗体重用的DLLs。
一 Dll的制作一般分为以下几步:
1 在一个DLL工程里写一个过程或函数
2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。
二 参数传递
1 参数类型最好与window C++的参数类型一致。最好少用DELPHI的数据类型。
2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。
3 用stdcall声明后缀。
4 最好大小写敏感。
5 无须用far调用后缀,那只是为了与windows 16位程序兼容。
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:
procedurefar;stdcall;
dwReason参数有四种类型:
DLL_PROCESS_ATTACH:进程进入时 DLL_PROCESS_DETACH进程退出时
DLL_THREAD_ATTACH 线程进入时 DLL_THREAD_DETACH 线程退出时
在初始化部分写:
DLLProc := @DLLEnterPoint;
DllEnterPoint(DLL_PROCESS_ATTACH);
2 如Form上有TdcomConnection组件,就Uses Activex,在初始化时写一句CoInitialize (nil);
3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。
四 全局变量的使用
在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。
五 调用静态载入
1 客户端函数声名:
1)大小写敏感。
2)与DLL中的声明一样。如:Far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windows c++一样。
4)调用时DLL必须在windows搜索路径中,顺序是:当前目录;Path路径;windows;widows\system;windows\ssystem32;
六 调用动态载入
1 建立一种过程类型[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。如:
type
mypointer=procedure(form:Tform);Far;external;
var
Hinst:Thandle;
begin
Hinst:=loadlibrary('yproject_dll');//Load一个Dll,按文件名找。
showform:=getprocaddress(Hinst,'showform');//按函数名找,大小写敏感。如果你知道自动化对象的本质就清楚了。
showform(application.mainform);//找到函数入口指针就调用。
Freelibrary(Hinst);
end;
例:
一、开始第一个DLL
1.File->Close all->File->New﹝DLL﹞
代码: //自动产生Code如下
library Project2;
uses
SysUtils, Classes;
{$R *.RES}
begin
end.
2.加个Func进来:
代码:
library Project2;
uses
SysUtils, Classes;
Function stdcall ;
begin
if X > Y then Result := X
else Result := Y ;
end ;
//切记:Library 的名字大小写没关系,可是DLL-Func的大小写就有关系了。 在 DLL-Func-Name写成MyMax与myMAX是不同的。如果写错了,结果是此DLL的AP根本打不开。参数的大小写就没关系了。甚至不必同名。如原型中是 (X,Y:integer)但引用时写成(A,B:integer),那是没关系的。
//切记:要再加个stdcall。书上讲,如果你是用delphi写DLL,且希望不仅给delphi-AP也希望BCB/VC-AP等使用的话,那你最好加个Stdcall ; 的指示
//参数型态:delphi有很多种它自己的变量型态,这些当然不是DLL所喜欢的,Windows/DLL的母语是C。所以如果要传进传出DLL的参数,我们尽可能照规矩来用。
{$R *.RES}
begin
end.
3.将这些可共享的Func送出DLL,让外界﹝就是你的delphi-AP啦﹞使用:光如此,你的AP还不能用到这些,你还要加个Exports才行。
代码:
{$R *.RES}
exports
MyMax ;
begin
end.
4.好了,可以按 Ctrl-F9编译了。此时可不要按F9。DLL不是EXE不可单独执行的,如果你按F9,会有ErrorMsg的。这时如果DLL有Error,请修正之。再按Ctrl-F9。此时可能有Warning,不要紧,研究一下,看看就好。再按Ctrl-F9,此时就『Done , Compiled 』。同目录就会有个 *.dll 。恭喜,大功告成了。
二、进行测试:开个新application:
1.加个TButton
代码: ShowMessage ( IntToStr(MyMax(30,50)) ) ;
2.告知Exe到那里抓个Func
代码: //在Form,interface,var后加
Function stdcall ; external 'MyTestDLL.dll' ;
// MyTestDLL.dll为你前时写的DLL项目名字
// DLL名字大小写没关系。不过记得要加 extension的 .DLL。在Win95或NT,是不必加 extension,但这两种OS,可能越来越少了吧。要加extension
三、dll注意事项
在delphi 1或delphi 2环境下该调用参数是far。从delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的Win32参数传递技术来代替优化的register参数。
1.在DLL中编写的函数或过程都必须加上stdcall调用参数。
2.所写的函数和过程应该用exports语句声明为外部函数。
3.当使用了长字符串类型的参数、变量时要引用ShareMem。
一、正如大家看到的,我们在external语句中指定了所要调用的DLL文件的名称。没有写路径是因为该DLL文件和调用它的主程序在同一目录下。如果该DLL文件在C:\,则我们可将上面的引用语句写为external ’C:\delphi.dll’。注意文件的后缀.dll必须写上。
二、不能从DLL中调用全局变量
如果我们在DLL中声明了某种全局变量,如:var s:byte 。这样在DLL中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给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}
//注意,上面这行代码必须加在这个位置
functionstdcall;
begin Result:=i; end;
exports TestDll;
begin
end.
五、调用技巧
1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C++编写的DLL例子中,如果去掉extern ”C”语句,C++会编译出一些奇怪的函数名,原来的TestC函数会被命名为@TestC$s等等可笑的怪名字,这是由于C++采用了C++ name mangling技术。这个函数名在delphi中是非法的,我们可以这样解决这个问题:
改写引用函数为
functionstdcall;
external ’Cpp.dll’;name ’@TestC$s’; 其中name的作用就是重命名。
2 、可把我们编写的DLL放到Windows目录下或者Windows\system目录下。这样做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统目录中的地步吧!
六、在delphi中动态调用DLL top
动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。
#include
extern ”C” _declspec(dllexport)
int WINAPI TestC(int i)
{ return i; }
编译后生成一个DLL文件,在这里我们称该文件为Cpp.dll,该DLL中只有一个返回整数类型的函数TestC。为了方便说明,我们仍然引用上面的调用程序,只是将原来的Button1Click过程中的语句用下面的代码替换掉了。
procedure
type
TIntFunc=function(i:integer):integer;stdcall;
var
Th:Thandle; 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中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。
七、例1:在DLL建立一个TForM
1 把你的Form Uses到Dll中,你的Form用到的关联的单元也要Uses进来[这是最麻烦的一点,因为你的Form或许Uses了许多特殊的单元或函数]
2 传递一个Application参数,用它建立Form.
在DLL中建立一个TMDIChildForM
1 Dll中的MDIForm.FormStyle不用为fmMDIChild.
2 在CreateForm后写以下两句:
functionstdcall
var
Form1: TForm1;
begin
ptr:=@(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
9
7月
问题1:未启用父路径
症状举例:
Server.MapPath() 错误 'ASP 0175 : 80004005'
不允许的 Path 字符
/0709/dqyllhsub/news/OpenDatabase.asp,行 4
在 MapPath 的 Path 参数中不允许字符 '..'。
原因分析:
许多Web页面里要用到诸如../格式的语句(即回到上一层的页面,也就是父路径),而IIS6.0出于安全考虑,这一选项默认是关闭的。
解决方法:
在IIS中 属性->主目录->配置->选项中。把”启用父路径“前面打上勾。确认刷新。
问题2:ASP的Web扩展配置不当(同样适用于ASP.NET、CGI)
症状举例:
HTTP 错误 404 - 文件或目录未找到。
原因分析:
在IIS6.0中新增了web程序扩展这一选项,你可以在其中对ASP、ASP.NET、CGI、IDC等程序进行允许或禁止,默认情况下ASP等程序是禁止的。
解决方法:
在IIS中的Web服务扩展中选中Active Server Pages,点击“允许”。
问题3:身份认证配置不当
症状举例:
HTTP 错误 401.2 - 未经授权:访问由于服务器配置被拒绝。
原因分析:IIS 支持以下几种 Web 身份验证方法:
匿名身份验证
IIS 创建 IUSR_计算机名称 帐户(其中 计算机名称 是正在运行 IIS 的服务器的名称),用来在匿名用户请求 Web 内容时对他们进行身份验证。此帐户授予用户本地登录权限。你可以将匿名用户访问重置为使用任何有效的 Windows 帐户。
基本身份验证
使用基本身份验证可限制对 NTFS 格式 Web 服务器上的文件的访问。使用基本身份验证,用户必须输入凭据,而且访问是基于用户 ID 的。用户 ID 和密码都以明文形式在网络间进行发送。
Windows 集成身份验证
Windows 集成身份验证比基本身份验证安全,而且在用户具有 Windows 域帐户的内部网环境中能很好地发挥作用。在集成的 Windows 身份验证中,浏览器尝试使用当前用户在域登录过程中使用的凭据,如果尝试失败,就会提示该用户输入用户名和密码。如果你使用集成的 Windows 身份验证,则用户的密码将不传送到服务器。如果该用户作为域用户登录到本地计算机,则他在访问此域中的网络计算机时不必再次进行身份验证。
摘要身份验证
摘要身份验证克服了基本身份验证的许多缺点。在使用摘要身份验证时,密码不是以明文形式发送的。另外,你可以通过代理服务器使用摘要身份验证。摘要身份验证使用一种挑战/响应机制(集成 Windows 身份验证使用的机制),其中的密码是以加密形式发送的。
.NET Passport 身份验证
Microsoft .NET Passport 是一项用户身份验证服务,它允许单一签入安全性,可使用户在访问启用了 .NET Passport 的 Web 站点和服务时更加安全。启用了 .NET Passport 的站点会依* .NET Passport 中央服务器来对用户进行身份验证。但是,该中心服务器不会授权或拒绝特定用户访问各个启用了 .NET Passport 的站点。
解决方法:
根据需要配置不同的身份认证(一般为匿名身份认证,这是大多数站点使用的认证方法)。认证选项在IIS的属性->安全性->身份验证和访问控制下配置。
问题4:IP限制配置不当
症状举例:
HTTP 错误 403.6 - 禁止访问:客户端的 IP 地址被拒绝。
原因分析:
IIS提供了IP限制的机制,你可以通过配置来限制某㊣P不能访问站点,或者限制仅仅只有某些IP可以访问站点,而如果客户端在被你阻止的IP范围内,或者不在你允许的范围内,则会出现错误提示。
解决方法:
进入IIS的属性->安全性->IP地址和域名限制。如果要限制某些IP地址的访问,需要选择授权访问,点添加选择不允许的IP地址。反之则可以只允许某些IP地址的访问。
问题5:IUSR账号被禁用
症状举例:
HTTP 错误 401.1 - 未经授权:访问由于凭据无效被拒绝。
原因分析:
由于用户匿名访问使用的账号是IUSR_机器名,因此如果此账号被禁用,将造成用户无法访问。
解决办法:
控制面板->管理工具->计算机管理->本地用户和组,将IUSR_机器名账号启用。
问题6:NTFS权限设置不当
症状举例:
HTTP 错误 401.3 - 未经授权:访问由于 ACL 对所请求资源的设置被拒绝。
原因分析:
Web客户端的用户隶属于user组,因此,如果该文件的NTFS权限不足(例如没有读权限),则会导致页面无法访问。
解决办法:
进入该文件夹的安全选项卡,配置user的权限,至少要给读权限。关于NTFS权限设置这里不再馈述。
问题7:IWAM账号不同步
症状举例:
HTTP 500 - 内部服务器错误
原因分析:
IWAM账号是安装IIS时系统自动建立的一个内置账号。IWAM账号建立后被Active Directory、IIS metabase数据库和COM+应用程序三方共同使用,账号密码被三方分别保存,并由操作系统负责这三方保存的IWAM密码的同步工作。系统对IWAM账号的密码同步工作有时会失效,导致IWAM账号所用密码不统一。
解决办法:
如果存在AD,选择开始->程序->管理工具->Active Directory用户和计算机。为IWAM账号设置密码。
运行c:\\Inetpub\\AdminScripts>adsutil SET w3svc/WAMUserPass +密码 同步IIS metabase数据库密码
运行cscript c:\\inetpub\\adminscripts\\synciwam.vbs -v 同步IWAM账号在COM+应用程序中的密码
问题8:MIME设置问题导致某些类型文件无法下载(以ISO为例)
症状举例:
HTTP 错误 404 - 文件或目录未找到。
原因分析:
IIS6.0取消了对某些MIME类型的支持,例如ISO,致使客户端下载出错。
解决方法:
在IIS中 属性->HTTP头->MIME类型->新建。在随后的对话框中,扩展名填入.ISO,MIME类型是application。
另外,防火墙阻止,ODBC配置错误,Web服务器性能限制,线程限制等因素也是造成IIS服务器无法访问的可能原因,这里就不再一一馈述了。
13
6月
在textarea中加入OnKeyUp事件中加入Resize方法。定义textarea的cols=60
13
6月
//关闭,父窗口弹出对话框,子窗口直接关闭
this.Response.Write("<script language=javascript>window.close();</script>");
//关闭,父窗口和子窗口都不弹出对话框,直接关闭
this.Response.Write("<script>");
this.Response.Write("{top.opener =null;top.close();}");
this.Response.Write("</script>");
//弹出窗口刷新当前页面width=200 height=200菜单。菜单栏,工具条,地址栏,状态栏全没有
this.Response.Write("<script language=javascript>window.open('rows.aspx','newwindow','width=200,height=200')</script>");
//弹出窗口刷新当前页面
this.Response.Write("<script language=javascript>window.open('rows.aspx')</script>");
this.Response.Write("<script>window.open('WebForm2.aspx','_blank');</script>");
//弹出提示窗口跳到webform2.aspx页(在一个IE窗口中)
this.Response.Write(" <script language=javascript>alert('注册成功');window.window.location.href='WebForm2.aspx';</script> ");
//关闭当前子窗口,刷新父窗口
this.Response.Write("<script>window.opener.location.href=window.opener.location.href;window.close();</script>");
this.Response.Write("<script>window.opener.location.replace(window.opener.document.referrer);window.close();</script>");
//子窗口刷新父窗口
this.Response.Write("<script>window.opener.location.href=window.opener.location.href;</script>");
this.Response.Write("<script>window.opener.location.href='WebForm1.aspx';</script>");
//弹出提示窗口.确定后弹出子窗口(WebForm2.aspx)
this.Response.Write("<script language='javascript'>alert('发表成功!');window.open('WebForm2.aspx')</script>");
//弹出提示窗口,确定后,刷新父窗口
this.Response.Write("<script>alert('发表成功!');window.opener.location.href=window.opener.location.href;</script>");
//弹出相同的一页
<INPUT type="button" value="Button" onclick="javascript:window.open(window.location.href)">
//
Response.Write("parent.mainFrameBottom.location.href='yourwebform.aspx?temp=" +str+"';");
<SCRIPT LANGUAGE="javascript">
<!--
window.open ('page.html', 'newwindow', 'height=100, width=400, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=n o, status=no') //这句要写成一行
-->
</SCRIPT>
参数解释:
<SCRIPT LANGUAGE="javascript"> js脚本开始;
window.open 弹出新窗口的命令;
'page.html' 弹出窗口的文件名;
'newwindow' 弹出窗口的名字(不是文件名),非必须,可用空''代替;
height=100 窗口高度;
width=400 窗口宽度;
top=0 窗口距离屏幕上方的象素值;
left=0 窗口距离屏幕左侧的象素值;
toolbar=no 是否显示工具栏,yes为显示;
menubar,scrollbars 表示菜单栏和滚动栏。
resizable=no 是否允许改变窗口大小,yes为允许;
location=no 是否显示地址栏,yes为允许;
status=no 是否显示状态栏内的信息(通常是文件已经打开),yes为允许;
</SCRIPT> js脚本结束
'newwin':隐藏菜单栏地址栏工具条
width=50:宽度
height=50:高度
scrollbars=yes/no:滚动条
top=50:窗口距离屏幕上方
left=50:窗口距离屏幕左侧
例:window.open('detail.aspx?ID="+e.Item.Cells[1].Text+"','newwin','width=750,height=600,scrollbars=yes,top=50,left=50');");
this.Response.Write("<Script>window.open('WebForm2.aspx','','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,width=750,height=470,left=80,top=40');</script>");[/quote]例:
this.Response.Write("<script>alert('发表成功!');window.opener.location.href=window.opener.location.href;</script>");
this.Response.Write("<script>");
this.Response.Write("{top.opener =null;top.close();}");
this.Response.Write("</script>");
例: linkcolumn1.DataNavigateUrlFormatString="javascript:varwin=window.open('edit_usr.aspx?actid={0}','newwin','width=750,height=600,scrollbars=yes,top=50,left=50');window.close()";
this.Response.Write("<Script>window.open('WebForm7.aspx','','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,width=750,height=470,left=80,top=40');</script>");
弹出跟你当前的窗口有没有菜单工具栏没有关系,你只要在页面中写一个脚本它就弹出了.比如
<a href=http://www.liugang.info/admin.php# onclick="window.open('xxx.aspx','窗口名称','参数');">xxxxx</a>
以下列出一些弹出窗口的参数,你可自行设定,参数之间用逗号分隔
可选。字符串--列出对象表并用逗号分开。每一项都有自己的值,他们将被分开(如:"fullscreen=yes, toolbar=yes")。下面是被支持的各种特性。
channelmode = { yes | no | 1 | 0 } 是否在窗口中显示阶梯模式。默认为no。
directories = { yes | no | 1 | 0 } 是否在窗口中显示各种按钮。默认为yes。
fullscreen = { yes | no | 1 | 0 } 是否用全屏方式显示浏览器。默认为no。使用这一特性时需要非常小心。因为这一属性可能会隐藏浏览器的标题栏和菜单,你必须提供一个按钮或者其他提示来帮助使用者关闭这一浏览窗口。ALT+F4可以关闭窗口。一个全屏窗口必须使用阶梯(channelmode)模式。
height = number 指定窗口的高度,单位是像素。最小值是100。
left = number 指定窗口距左边框的距离,单位是像素。值必须大于或者等于0。
location = { yes | no | 1 | 0 } 指定是否在窗口中显示地址栏。默认为yes。
menubar = { yes | no | 1 | 0 } 指定是否在窗口中显示菜单栏。默认为yes。
resizable = { yes | no | 1 | 0 } 指定是否在窗口中显示可供用户调整大小的句柄。默认为yes。
scrollbars = { yes | no | 1 | 0 } 指定是否在窗口中显示横向或者纵向滚动条。默认为yes。
status = { yes | no | 1 | 0 } 指定是否在窗口中显示状态栏。默认为yes。
titlebar = { yes | no | 1 | 0 } 指定是否在窗口中显示标题栏。在非调用HTML Application或者一个对话框的情况下,这一项将被忽略。默认为yes。
toolbar = { yes | no | 1 | 0 } 指定是否在窗口中显示工具栏,包括如前进、后退、停止等按钮。默认为yes。
top = number 指定窗口顶部的位置,单位是像素。值必须大于或者等于0。
width = number 指定窗口的宽度,单位是像素。最小值是100。
【1、最基本的弹出窗口代码】
<SCRIPT LANGUAGE="javascript">
<!--
window.open ('page.html')
-->
</SCRIPT>
因为这是一段javascripts代码,所以它们应该放在之间。是对一些版本低的浏览器起作用,在这些老浏览器中不会将标签中的代码作为文本显示出来。要养成这个好习惯啊。window.open ('page.html') 用于控制弹出新的窗口page.html,如果page.html不与主窗口在同一路径下,前面应写明路径,绝对路径(http://)和相对路径(../)均可。用单引号和双引号都可以,只是不要混用。这一段代码可以加入HTML的任意位置,和之间可以,间也可以,越前越早执行,尤其是页面代码长,又想使页面早点弹出就尽量往前放。
【2、经过设置后的弹出窗口】
下面再说一说弹出窗口的设置。只要再往上面的代码中加一点东西就可以了。我们来定制这个弹出的窗口的外观,尺寸大小,弹出的位置以适应该页面的具体情况。
<SCRIPT LANGUAGE="javascript">
<!--
window.open ('page.html', 'newwindow', 'height=100, width=400, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=n o, status=no') //这句要写成一行
-->
</SCRIPT>
参数解释:
<SCRIPT LANGUAGE="javascript"> js脚本开始;
window.open 弹出新窗口的命令;
'page.html' 弹出窗口的文件名;
'newwindow' 弹出窗口的名字(不是文件名),非必须,可用空''代替;
height=100 窗口高度;
width=400 窗口宽度;
top=0 窗口距离屏幕上方的象素值;
left=0 窗口距离屏幕左侧的象素值;
toolbar=no 是否显示工具栏,yes为显示;
menubar,scrollbars 表示菜单栏和滚动栏。
resizable=no 是否允许改变窗口大小,yes为允许;
location=no 是否显示地址栏,yes为允许;
status=no 是否显示状态栏内的信息(通常是文件已经打开),yes为允许;
</SCRIPT> js脚本结束
【3、用函数控制弹出窗口】
下面是一个完整的代码。
<html>
<head>
<script LANGUAGE="JavaScript">
<!--
function openwin() {
window.open ("page.html", "newwindow", "height=100, width=400, toolbar =no, menubar=no, scrollbars=no, resizable=no, location=no, status=no") //写成一行
}
//-->
</script>
</head>
<body onload="openwin()">
任意的页面内容...
</body>
</html>
这里定义了一个函数openwin(),函数内容就是打开一个窗口。在调用它之前没有任何用途。怎么调用呢?
方法一:
<body onload="openwin()"> 浏览器读页面时弹出窗口;
方法二:
<body onunload="openwin()"> 浏览器离开页面时弹出窗口;
方法三:用一个连接调用:
<a href="http://www.liugang.info/admin.php#" onclick="openwin()">打开一个窗口</a> 注意:使用的“#”是虚连接。
方法四:用一个按钮调用:
<input type="button" onclick="openwin()" value="打开窗口">
【4、同时弹出2个窗口】
对源代码稍微改动一下:
<script LANGUAGE="JavaScript">
<!--
function openwin() {
window.open ("page.html", "newwindow", "height=100, width=100, top=0, left=0,toolbar=no, menubar=no, scrollbars=no, resizable=no, location=n o, status=no")//写成一行
window.open ("page2.html", "newwindow2", "height=100, width=100, top=1 00, left=100,toolbar=no, menubar=no, scrollbars=no, resizable=no, loca tion=no, status=no")//写成一行
}
//-->
</script>
为避免弹出的2个窗口覆盖,用top和left控制一下弹出的位置不要相互覆盖即可 。最后用上面说过的四种方法调用即可。
注意:2个窗口的name(newwindows和newwindow2)不要相同,或者干脆全部为空。
【5、主窗口打开文件1.htm,同时弹出小窗口page.html】
如下代码加入主窗口区:
<script language="javascript">
<!--
function openwin() {
window.open("page.html","","width=200,height=200")
}
//-->
</script>
加入<body>区:
<a href="1.htm" onclick="openwin()">open</a>即可。
【6、弹出的窗口之定时关闭控制】
下面我们再对弹出的窗口进行一些控制,效果就更好了。如果我们再将一小段 代码加入弹出的页面(注意是加入page.html的HTML中,不是主页面中),让它10秒后自动关闭是不是更酷了?
首先,将如下代码加入page.html文件的区:
<script language="JavaScript">
function closeit()
{
setTimeout("self.close()",10000) //毫秒
}
</script> 然后,再用 这一句话代替page.html中原有的这一句就可以了。(这一句话千万不要忘记写啊!这一句的作用是调用关闭窗口的代码,10秒钟后就自行关闭该窗口。)
【7、在弹出窗口中加上一个关闭按钮】
<FORM>
<INPUT TYPE='BUTTON' VALUE='关闭' onClick='window.close()'>
</FORM> 呵呵,现在更加完美了!
【8、内包含的弹出窗口-一个页面两个窗口】
上面的例子都包含两个窗口,一个是主窗口,另一个是弹出的小窗口。通过下面的例子,你可以在一个页面内完成上面的效果。
<html>
<head>
<SCRIPT LANGUAGE="JavaScript">
function openwin()
{
OpenWindow=window.open("", "newwin", "height=250, width=250,toolbar=no ,scrollbars="+scroll+",menubar=no");
//写成一行
OpenWindow.document.write("<TITLE>例子</TITLE>")
OpenWindow.document.write("<BODY BGCOLOR=#ffffff>")
OpenWindow.document.write("<h1>Hello!</h1>")
OpenWindow.document.write("New window opened!")
OpenWindow.document.write("</BODY>")
OpenWindow.document.write("</HTML>")
OpenWindow.document.close()
}
</SCRIPT>
</head>
<body>
<a href="http://www.liugang.info/admin.php#" onclick="openwin()">打开一个窗口</a>
<input type="button" onclick="openwin()" value="打开窗口">
</body>
</html> 看看OpenWindow.document.write()里面的代码不就是标准的HTML吗?只要按照格式写更多的行即可。千万注意多一个标签或少一个标签就会出现错误。记得用 OpenWindow.document.close()结束啊。
【9、终极应用--弹出的窗口之Cookie控制】
回想一下,上面的弹出窗口虽然酷,但是有一点小毛病,比如你将上面的脚本放在一个需要频繁经过的页面里(例如首页),那么每次刷新这个页面,窗口都会弹出一次,我们使用cookie来控制一下就可以了。
首先,将如下代码加入主页面HTML的区:
<script>
function openwin(){
window.open("page.html","","width=200,height=200")
}
function get_cookie(Name) {
var search = Name + "="
var returnvalue = "";
if (document.cookie.length > 0) {
offset = document.cookie.indexOf(search)
if (offset != -1) {
offset += search.length
end = document.cookie.indexOf(";", offset);
if (end == -1)
end = document.cookie.length;
returnvalue=unescape(document.cookie.substring(offset, end))
}
}
return returnvalue;
}
function loadpopup(){
if (get_cookie('popped')==''){
openwin()
document.cookie="popped=yes"
}
}
</script> 然后,用
<body onload="loadpopup()">(注意不是openwin而是loadpop啊!)替换主页面中原有的这一句即可。你可以试着刷新一下这个页面或重新进入该页面,窗口再也不会弹出了。真正的Pop-Only-Once!





