delphi – 如何获取dbExpress连接错误的Winsock错误代码?
如果出现连接问题,dbExpress驱动程序将抛出TDBXError但不包含套接字错误代码.消息很简单:
有没有办法在发生此类异常时检索底层套接字错误? 堆栈跟踪是: main thread ($5934): 0061aa59 +051 example.exe DBXCommon 447 +0 TDBXContext.Error 00817f14 +10c example.exe DBXDynalink 796 +21 TDBXMethodTable.RaiseError 00818553 +013 example.exe DBXDynalink 949 +1 TDBXDynalinkConnection.CheckResult 00818744 +050 example.exe DBXDynalink 1048 +4 TDBXDynalinkConnection.DerivedOpen 0061750f +007 example.exe DBXCommon 447 +0 TDBXConnection.Open 00612fed +0f5 example.exe DBXCommon 447 +0 TDBXConnectionFactory.GetConnection 00612ef1 +005 example.exe DBXCommon 447 +0 TDBXConnectionFactory.GetConnection 0062c08f +26f example.exe SqlExpr TSQLConnection.DoConnect 005d9019 +039 example.exe DB TCustomConnection.SetConnected 005d8fd4 +004 example.exe DB TCustomConnection.Open 0062b98f +01b example.exe SqlExpr TSQLConnection.CheckConnection 0062ebdf +01f example.exe SqlExpr TCustomSQLDataSet.CheckConnection 0062efe4 +04c example.exe SqlExpr TCustomSQLDataSet.OpenCursor 005e91d5 +055 example.exe DB TDataSet.SetActive 005e901c +004 example.exe DB TDataSet.Open 解决方法
(在这个答案中使用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); 这是输出:
正如您所注意到的那样,问题中的错误信息完全相同.请注意,文本中没有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); 这是返回的错误:
这次连接失败,因为没有特定端口的侦听器.我不知道为什么客户端库无法解析10060,但这是你的winsock错误. localhost,非监听端口的情况:
这里我们有一个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,PAnsiChar(AnsiString(Connection.Params.Values['Database'])),@Handle,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. 哪个输出:
第一段是dbx报告的内容.第二段是我们从’gds32.dll’获得的,包括注入api错误代码和文本,否则它们是相同的. 以上是一个粗略的演示,正确的打字使用’interbase.h’.有关选择可能的api错误的详细信息,请参阅“Parsing the Status Vector”或一般“Handling Error Conditions”. 在任何情况下,可以看出,能够完全获取特定信息取决于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 // (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |