Delphi开发Web程序常见问题 Web程序开发对开发工具提出了极大挑战，Delphi和其他开发工具不同，因为它是一个开放系统，只要灵活使用一些控件，即可开发出各种类型的系统，不论N－Tie程序、多线程程序、分布计算程序（包括DCOM和CORBAR）、TCP程序、Web程序、ActiveX、中间件、推程序（Push），甚至你可以用它来写汇编程序。 Delphi将ISAPI/NSAPI/CGI/WCGI等巧妙地封装成一个类，用户只要在编译时选择编译结果，就可以得到不同的系统。 在Delphi 4中Inprise公司进一步加强了对Web程序开发的支持，可以开发出更好更强的系统。以下是开发Web应用程序中的几个常见问题，可以供大家参考。如果没有特别申明，则表示程序可以运行于Delphi 5.0下。
1.从Web Server Application返回一幅图像
Web Server Application不仅可以生成复杂的页面文档，也可以根据用户请求返回不同的图像。当然有比较简单的方法，根据输入参数不同，〈img src...〉标记也指向不同的URL地址。这里我们不用这个办法，而是利用DLL返回图像。
当然要首先建立一个页面容器（page producer），内容如下：
〈html〉
〈body〉This is a test〈BR〉〈img src=″/scripts/mydll.dll/picture″〉〈/body〉
〈/html〉
接下来我们设定对应于PathInfo的动作事件，返回图像结果，源代码如下：
（注意：单元声明中要包含JPEG单元）
procedure TWebModule1.WebModule1WebAction- Item1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
jpg align=left: TJpegImage; S: TMemoryStream;
begin
jpg align=left := TJpegImage.Create;
try
jpg align=left.LoadFromFile(′test′);
S := TMemoryStream.Create;
try
jpg align=left.SaveToStream(S);
S.Position := 0;
Response.ContentType := ′image/jpeg′;
Response.ContentStream := S;
// 必须在流释放前完成
Response. SendResponse;
finally
S.Free;
end;
finally
jpg align=left.Free;
end;
end;
实际上用这种方法和前面提到的简单做法相比，具有更安全和更灵活的特点。在某些地方灵活使用，以此为基础稍加修改可以产生一般开发工具难以实现的效果。
2.在ISAPI/NSAPI动态连接库（DLL）中使用本地数据库驱动程序
这是因为DAO 3.0 或者DAO 3.5是所谓thread－safe（线程安全程序），而一个Web服务器（例如IIS）会随用户请求产生多个线程，同时把ISAPI对应的DLL也列为线程。这时ISAPI就会通过BDE通知DAO，告诉它不符合线程安全规定。
解决的方法有很多，如果你一定要访问Access 95/97库，那么可以通过ODBC访问。ODBC不会经过DAO，而且也是一个线程安全程序。此外还有一些第三方的控件集，通过他们可以直接由BDE访问Access 95/97，效率更高。
3.用户访问我Web服务器上的ISAPI.DLL，可是报告：“Invalid filename”（无效的文件名），然而文件的确存在。（数据库在Novell服务器上）
你没有设定对应于你IUSR_XXX账户的驱动器路径映射（MAPING）。因为Novell不是采用FAT，所以要手工添加路径映射。当然，可以做成开机登录脚本。请牢记，如果你运行IIS作为Web服务器，而又涉及Novell，无论作为文件服务器或数据库服务器，都要定义好路径映射。
“Invalid configuration parameter for alias {alias_name}”（无效的别名配置），当设置一个ODBC DSN，并通过它访问ISAPI/NSAPI服务器时就出现这样一个错误。
如果要为访问的用户（IIS用户）建立一个ODBC别名，那么要注意创建一个SYSTEM DSN（系统DNS），而不要创建“用户DNS”，虽然“用户DNS”是缺省设定。
4.取得客户机（访问机器）的名称和IP地址
实现这个功能用TCP控件来做非常容易。从Internet页面上选取一个TCP控件，然后就可以得到你所需要的：
Memo1.Lines.Add(TCP1.LocalHostName);
Memo1.Lines.Add(TCP1.LocalIp);
当然，如果你不希望这样做，还有比较复杂的办法：
uses Winsock;
procedure TForm1.FormCreate(Sender: TObject);
var
wVersionRequested : WORD;
wsaData : TWSAData;
begin
{创建 WinSock}
wVersionRequested := MAKEWORD(1, 1);
WSAStartup(wVersionRequested, wsaData);
end;
procedure TForm1.Button1Click(Sender: TObject);
var p : PHostEnt; s : array[0..128] of char; p2 : pchar;
begin
{得到计算机名称}
GetHostName(@s,128);
p:=GetHostByName(@s);
Memo1.Lines.Add(p^.h_Name);
{得到机器IP地址}
p2 := iNet_ntoa(PInAddr(p^.h_addr_list^)^);
Memo1.Lines.Add(p2);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{释放 WinSock}
WSACleanup;
end;
这是一个调用了Winsock的独立单元，你可以把它直接嵌入到你的程序中去。
5.在Delphi 3中为何不能创建真正的多线程DLL
虽然Delphi 3中的ISAPI DLL向导已经为创建多线程DLL生成了大量代码，可是还是有一个严重的缺陷：没有申明本应用程序是一个多线程的程序。所以需要你添加一句话：
IsMultiThread := TRUE;
把这句话放在DPR程序begin－end块的开始处，使之成为第一句。
6.检测现在是否和Internet连接
最简单的办法是用一个TCP元件得到自己当前的IP，通过判断IP得知是否连入Internet。例如：
if TCP1.LocalIp = '0.0.0.0' then
ShowMessage('目前没有连入Internet!');
需要注意的是:因为Internet和Intranet没本质区别，所以一般不能判定是和Internet连接还是仅仅连入Intranet。当然。你也可以再加一个Ping元件，去连接一个比较稳定、速度比较快的站点，如果连通则表明已经接入Internet。不过这种办法通用性不好。
7.打印一个Web页面
可以选用HTML控件的AutoPrint方法。例如：
uses Printers;
procedure TForm1.Button1Click(Sender: TObject);
var
OldCur: TCursor;
begin
OldCur := Screen.Cursor;
with Printer do begin
BeginDoc;
HTML1.AutoPrint(handle);
Title := HTML1.URL;
EndDoc;
end;
Screen.Cursor := OldCur;
end;
此外还可以利用其PrintPage方法。不过这里推荐采用AutoPrint，因为这样控制更灵活，可以过滤一些你不希望打印的内容。
最后我还要向你推荐几套Delphi编写Web程序的增强控件集，它们是：
1.CGI Expert 4由Lars German公司出品，编写shell－CGI、win－CGI、ISAPI和NSAPI程序特别方便。
2.NetMaster的Internet控件集。Delphi 4中的Internet控件集就是它们的杰作，和Delphi结合得非常好。你可以去下载它们的最新版，功能更强大，而且对Delphi用户是免费的。
3.Dart Communications出品的PowerTCP的确非常强大，如果编写专业级TCP应用程序必不可少。有很多功能实际是由DLL提供的。

（岳庆生）