(此答案中使用了 Delphi XE2 和 'gds32.dll' 10.0.1.335 (ansi))
dbExpress 是一个高级框架,它将提供者特定的操作委托给它的驱动程序。所有这些提供程序特定的驱动程序都公开了相同的基本通用功能,因此无法检索超出此通用功能的特定信息。
关于错误报告,驱动程序导出两个函数。无论是 mysql 还是 mssql 或任何其他驱动程序,这些函数都是DBXBase_GetErrorMessageLength
和DBXBase_GetErrorMessage
. 驱动程序从客户端库中获取大部分问题,并通过这些函数将这些信息报告给框架。除了已经报告的内容之外,无法获得有关连接到数据库服务器或任何其他操作出现问题的更多具体细节。
因此,是否包含 Winsock 错误信息取决于客户端库。对于问题中包含的错误来自interbase驱动程序的情况,此信息已包含(如果有)。
这是一个示例案例,尝试连接到现有服务器,以及连接到被侦听但不被数据库服务器侦听的端口。
Connection := TSQLConnection.Create(nil);
Connection.DriverName := 'Interbase';
Connection.Params.Values['Database'] := 'existingserver/80:database';
try
Connection.Open;
except
on E: TDBxError do begin
writeln(E.Message + sLineBreak);
这是输出:
无法完成对主机“existingserver:80”的网络请求。
无法建立连接。
正如您所注意到的,这与问题中的错误消息完全相同。注意文本中没有winsock错误。那是因为没有winsock错误,只要网络协议方面,其实是连接成功的。
这是对现有服务器的尝试,但对未侦听端口的尝试。
Connection := TSQLConnection.Create(nil);
Connection.DriverName := 'Interbase';
Connection.Params.Values['Database'] := 'existingserver/81:database';
try
Connection.Open;
except
on E: TDBxError do begin
writeln(E.Message + sLineBreak);
这是返回的错误:
无法完成对主机“existingserver:81”的网络请求。
无法建立连接。
未知 Win32 错误 10060
这次连接失败,因为没有特定端口的侦听器。我不知道为什么客户端库无法解析 10060,但这是您的 winsock 错误。
本地主机的案例,非监听端口:
无法完成对主机“localhost:3051”的网络请求。
无法建立连接。
由于目标机器主动拒绝,无法建立连接。
这里我们有一个 10061。
当尝试连接到不可解析的主机时,gds32.dll 不会报告任何 api 错误。我不知道该库如何解析主机或为什么它不包含错误代码,但错误消息很详细:
无法完成对主机“不存在的服务器”的网络请求。
未能找到主机。
在主机文件或域名服务中找不到指定的名称。
如果我们直接使用客户端库,我们只能得到 api 错误。检查以下程序,其中尝试连接到现有服务器和关闭的端口。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
system.sysutils,
data.dbxcommon,
data.sqlexpr,
data.dbxinterbase;
function isc_attach_database(status_vector: PLongint; db_name_length: Smallint;
db_name: PAnsiChar; db_handle: PPointer; parm_buffer_length: Smallint;
parm_buffer: PAnsiChar): Longint; stdcall; external 'gds32.dll';
function isc_interprete(buffer: PAnsiChar; var status_vector: Pointer): Longint;
stdcall; external 'gds32.dll';
var
Connection: TSQLConnection;
StatusVector: array[0..19] of Longint;
Handle: PPointer;
Error: array[0..255] of AnsiChar;
IntrStatus: Pointer;
s: string;
begin
try
Connection := TSQLConnection.Create(nil);
Connection.DriverName := 'Interbase';
Connection.Params.Values['Database'] := 'server/3051:database'; // closed port
try
Connection.Open;
except
on E: TDBxError do begin
writeln(E.Message + sLineBreak);
if E.ErrorCode = TDBXErrorCodes.ConnectionFailed then begin
Handle := nil;
if isc_attach_database(@StatusVector, 0,
PAnsiChar(AnsiString(Connection.Params.Values['Database'])),
@Handle, 0, nil) <> 0 then begin
IntrStatus := @StatusVector;
s := '';
while isc_interprete(Error, IntrStatus) <> 0 do begin
s := s + AnsiString(Error) + sLineBreak;
if PLongint(IntrStatus)^ = 17 then // isc_arg_win32
s := s + ' --below is an api error--' + sLineBreak +
Format('%d: %s', [PLongint(Longint(IntrStatus) + 4)^,
SysErrorMessage(PLongint(Longint(IntrStatus) + 4)^)]) +
sLineBreak;
end;
Writeln(s);
end;
end else
raise;
end;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
哪个输出:
无法完成对主机“sever:3051”的网络请求。
无法建立连接。
未知 Win32 错误 10060
无法完成对主机“server:3051”的网络请求。
无法建立连接。
--下面是一个api错误-- 10060: 连接尝试失败,因为连接的一方
在一段时间后
没有正确响应,或者建立连接失败,因为连接
的主机没有响应
未知的Win32错误10060
第一段是 dbx 报告的内容。第二段是我们从'gds32.dll'中得到的,包括api错误代码和文本的注入,否则它们是相同的。
以上是一个粗略的演示,为了正确输入,请使用“interbase.h”。有关选择可能的 api 错误的详细信息,请参阅“解析状态向量”或一般的“处理错误条件”。
无论如何,可以看出,能否获得特定信息完全取决于 dbx 用来连接数据库服务器的客户端库。
对于一般情况,要独立于正在使用的数据库服务器获取winsock错误信息,您可以做的是在尝试打开数据库连接之前尝试将套接字连接到服务器,并且只有当这成功关闭您的测试连接然后继续附加到数据库。您可以使用任何库或裸 API 来执行此操作。
这是一个简单的例子:
function TestConnect(server: string; port: Integer): Boolean;
procedure WinsockError(step: string);
begin
raise Exception.Create(Format('"%s" fail. %d: %s:',
[step, WSAGetLastError, SysErrorMessage(WSAGetLastError)]));
end;
var
Error: Integer;
Data: TWSAData;
Socket: TSocket;
SockAddr: TSockAddrIn;
Host: PHostEnt;
begin
Result := False;
Error := WSAStartup(MakeWord(1, 1), Data);
if Error = 0 then begin
try
Socket := winsock.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if Socket <> INVALID_SOCKET then begin
try
Host := gethostbyname(PAnsiChar(AnsiString(server)));
if Host <> nil then begin
SockAddr.sin_family := AF_INET;
SockAddr.sin_addr.S_addr := Longint(PLongint(Host^.h_addr_list^)^);
SockAddr.sin_port := htons(port);
if connect(Socket, SockAddr, SizeOf(SockAddr)) <> 0 then
WinsockError('connect')
else
Result := True;
end else
WinsockError('gethostbyname');
finally
closesocket(Socket);
end;
end else
WinsockError('socket');
finally
WSACleanup;
end;
end else
raise Exception.Create('winsock initialization fail');
end;
你可以使用这样的东西:
if TestConnect('server', 3050) then
//