I'm trying to use Overbyte ICS TWsocket in a Delphi console application (service).
I have set NOFORMS in a conditionals.
But at Connect the connection doesn't go to the wsConnected it hangs in a wsConnecting state and the operation finishes with Async socket error 10053.
I've tried to use in a OnMessagePump the ProcessMessages(), MessageLoop() but it didn't change anything.
Here is a part of a code
inherited Create(nil);
Self.OnDataAvailable := MyOnReceive;
Self.OnChangeState := MyOnStateChange;
Self.OnMessagePump := MyMessagePump;
Addr := AIpAddress;
Port := IntToStr(AConnectionInfo.Port);
Proto := AConnectionInfo.Proto;
ComponentOptions := [wsoTcpNoDelay];
How to use TWsocket in a console application with NOFORMS correctly?
Related
I'm trying to use Indy's IdFTP to send and receive some files via FTP.
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
var
TimeoutFTP: integer;
begin
Result := false;
with TIdFTP.Create(Fm) do
try
try
TimeoutFTP := 2000;
Host := Servidor;
Port := 21;
UserName := Usuario;
PassWord := Contra;
Passive := Pasivo;
Connect(true, TimeoutFTP);
if not Connected then
begin
Error := true;
end
else
begin
TransferType := ftASCII;
if Binario then
TransferType := ftBinary;
OnWorkEnd := FinDeTransmision;
if Descargar then
Get(Remoto , Local, True)
else
Put(InterpretarRutaEspecial(Local), Remoto, True);
if Descargar and Borrar then
Delete(Remoto);
Disconnect;
Result := true;
Fm.Hide;
end;
Except on E: Exception do
Mensaje := E.Message;
end;
finally
Free;
end;
if not Result then
ErrorTransmision;
end;
Whenever I try to do a PUT/GET on Active mode I get the following error: EIdProtocolReplyError: 'Failed to establish connection". It works fine on Passive mode.
The thing is that I want to use Indy (used elsewhere in the project) but the previous version of the code, using OverbyteIcsFtpCli works fine both in Active and Passive mode.
This is the code using OverbyteIcsFtpCli:
function TDatosFTP.TransfiereFTP(Fm: TForm): boolean;
begin
with TFtpClient.Create(Fm) do
try
HostName := Servidor;
Port := '21';
UserName := Usuario;
PassWord := Contra;
HostDirName := '';
HostFileName := Origen;
LocalFileName := InterpretarRutaEspecial(Destino);
Binary := Binario;
Passive := Pasivo;
OnRequestDone := FinDeTransmision;
if Descargar then
Result := Receive
else
Result := Transmit;
OnRequestDone := nil;
if Descargar and Borrar then
Delete;
Result := Result and not Error;
Fm.Hide;
if not Result then
ErrorTransmision;
finally
Free;
end;
end;
So I took a look under the hood using wireshark and I found that Indy's FTP is not answering some messages from the server.
This is the file-transmission handshake with OverBytes' FTP:
I've highlighted in yellow the two packets sent between server and client that start the data transmission.
Now let's see what happens with Indy's FTP:
The server is sending the packet to start the file transmission but IdFTP is not answering.
I've seen this question but this two tests where ran in the same computer, same network connection, same firewall, etc. Also this one, but I want the FTP to work both in active and passive modes.
What's happening?
In an Active mode transfer, an FTP server creates an outgoing TCP connection to the receiver.
Your Wireshark captures clearly show that the FTP server in question is creating that transfer connection BEFORE sending a response to the RETR command to let your client know that the connection is proceeding. TFtpClient is accepting that connection before receiving the RETR response. But TIdFTP waits for the RETR response before it will then accept the transfer connection (this also applies to TIdFTP's handling of STOR/STOU/APPE commands, too).
LPortSv.BeginListen; // <-- opens a listening port for transfer
...
SendPort(LPortSv.Binding); // <-- sends the PORT command
...
SendCmd(ACommand, [125, 150, 154]); // <-- sends the RETR command and waits for a response!
...
LPortSv.Listen(ListenTimeout); // <-- accepts the transfer connection
...
Re-reading RFC 959, it says the following:
The passive data transfer process (this may be a user-DTP or a second server-DTP) shall "listen" on the data port prior to sending a transfer request command. The FTP request command determines the direction of the data transfer. The server, upon receiving the transfer request, will initiate the data connection to the port. When the connection is established, the data transfer begins between DTP's, and the server-PI sends a confirming reply to the user-PI.
ICS is asynchronous, so this situation is not a big deal for it to handle. But Indy uses blocking sockets, so TIdFTP will need to be updated to account for this situation, likely by monitoring both command and transfer ports simultaneously so it can act accordingly regardless of the order in which the transfer connection and the command response arrive in.
I have opened a ticket in Indy's issue tracker for this:
#300: TIdFTP fails on Active mode transfer connection with vsFTPd
UPDATE: the fix has been merged into the main code now.
I have an application that listens on a serial port. It uses TComPort to connect to the serial port, and a timer to listen. Basically, all we're doing is listening for data, specifically barcodes. However, we seem to have a problem. The data "comes across" in the form #0#0#0#0.... when a barcode is scanned.
The code is as follows:
if Comport2.Active then
begin
sStr := Comport2.ReadAnsiString;
if Length(sStr) <> 0 then
begin
MatCodeEdit.Text := '';
MatCodeEdit.Text := sStr;
end;
end;
The serial port is connected with the following parameters:
Comport2.DataBits := db8;
Comport2.Parity := paNone;
Comport2.StopBits := sb1;
No errors occur on connection. The above code has, actually, succeeded in reading the contents of the serial port transmission. But, currently seems only to receive the #0#0#0... string.
Does anyone have any ideas about this?
Using the Enterprise version of XE2, which includes native dbExpress ODBC support, I have successfully created a "Connection" where the Database Name is the name of the System DSN, and the Password is the MS Access database password.
I can click Test Connection on the Modify Connection page, and the Database Explorer reports Test connection succeeded.
Dragging and dropping the connection from the Data Explorer creates a TSQLConnection with the Driver property set to ODBC and the Params collection being:
drivername=ODBC
database=myaccessdbDSN
password=accessdbpwd
While this is great for testing, I'd rather not require an ODBC System DSN to be created for it to work.
From what I remember, a DSN can just be replaced by a ConnectionString of the correct format, and can usually be figured out by looking at the text of the DSN. Unfortunately, the Data Explorer in XE2 only works with System DSN's, and not File DSN's.
What should the ConnectionString of the TSQLConnection be when using ODBC to connect to a MS Access database file?
EDIT 1
Given a form with a databound grid, a TDataSource, a TSQLQuery with appropriate SQL command, and a private TSQLConnection (not form component), the following works:
FSQLConnection := TSQLConnection.Create(nil);
FSQLConnection.DriverName := 'ODBC';
FSQLConnection.LoginPrompt := false;
FSQLConnection.Params.Values['Database'] := 'myaccessdbDSN';
FSQLConnection.Params.Values['Password'] := 'accessdbpwd';
FSQLConnection.Connected := true;
SQLQuery1.SQLConnection := FSQLConnection;
SQLQuery1.Active := true;
I see the results of the query in the grid.
What I need to do is replace the 'myaccessdbDSN' with a connection string. If I do the following, which seems to be the way it should work, I get a "Connection Could Not Open" error from the ODBC Driver Manager.
FSQLConnection := TSQLConnection.Create(nil);
FSQLConnection.DriverName := 'ODBC';
FSQLConnection.LoginPrompt := false;
FSQLConnection.Params.Values['ConnectionString'] := 'Provider=Microsoft.ACE.OLEDB.12.0;'+
'Data Source=myaccessdb.mdb;'+
'Jet OLEDB:Database Password=accessdbpwd;';
FSQLConnection.Connected := true;
SQLQuery1.SQLConnection := FSQLConnection;
SQLQuery1.Active := true;
Please note that normally, when setting the DriverName property at runtime, you have to set some of the other properties that are normally defined in the dbxdrivers.ini file. If the DriverName is set to 'MSSQL', these are:
GetDriverFunc=getSQLDriverMSSQL
LibraryName=dbxmss.dll
VendorLib=sqlncli10.dll
But when the DriverName is set to 'ODBC', these are blank. Is this a fault with the dbxDriver for ODBC or not?
I've looked through the help at http://docwiki.embarcadero.com/RADStudio/Berlin/en/Setting_Up_TSQLConnection
but it does not tell me anything I haven't already tried, and it does fill me with confidence when the sentence
To display the Connection Editor, double-click the TSQLConnection
component.
does not work in XE2, XE5, Seattle or Berlin.
EDIT 2
The whole reason I am looking at this is because the Open ODBC dbxExpress driver no longer works in Berlin. The following will work in XE2:
FSQLConnection.DriverName := 'DbxOpenOdbc';
FSQLConnection.GetDriverFunc := 'getSQLDriverODBCW';
FSQLConnection.LibraryName := 'dbxoodbc.dll';
FSQLConnection.VendorLib := 'odbcjt32.dll';
FSQLConnection.Params.Values['DriverPackageLoader'] := 'TDBXDynalinkDriverLoaderOpenOdbc';
FSQLConnection.Params.Values['IsolationLevel'] := 'ReadCommitted';
FSQLConnection.Params.Values['RowSetSize'] := '20';
FSQLConnection.Params.Values['Database'] := '?';
FSQLConnection.Params.Values['ConnectionString'] := 'DRIVER={Microsoft Access Driver (*.mdb)};'+
'DBQ=myaccessdb.mdb;'+
'UID=;'+
'PWD=accessdbpwd;'+
'DriverId=25;'+
'FIL=MS Access;';
But when the same code is run in Berlin, I get a "Duplicates not allowed" error, from the AddCommandFactory, which is related to this code in the Dbx34Drv unit, as below:
constructor TDbxOpenOdbcDriver.Create(DriverDef: TDBXDriverDef);
begin
{$IF CompilerVersion > 18.50}
inherited Create(DriverDef, TDBXDynalinkDriverLoader);
InitDriverProperties(TDBXOpenOdbcProperties.Create(DriverDef.FDBXContext));
// '' makes this the default command factory.
AddCommandFactory('', CreateCommandOpenOdbc);
//AddCommandFactory(TDBXCommandTypes.DbxMetaData
{$ELSE}
raise EDbxOODBCDriverError.Create('Not Implemented: TDbxOpenOdbcDriver.Create(DriverDef: TDBXDriverDef)');
{$IFEND}
end;
As the Open ODBC dbxExpress driver has not been maintained since 2013, and that XE2 and Berlin Enterprise come with their own ODBC dbxExpress driver (Data.DBXOdbc), I am wanting to change the properties and parameters to get the code that is working in XE2 to work with the native ODBC driver, and then check that it works in Berlin.
The thread at https://sourceforge.net/p/open-dbexpress/discussion/119359/thread/703de7d9/ indicates it should just be a matter of replacing the Open ODBC values with regular ODBC ones.
The table at http://docwiki.embarcadero.com/RADStudio/Berlin/en/DbExpress_Supported_Database_Management_Systems does not list any libraries for "Odbc", so I assume it would be blank.
EDIT 3 (AND ANSWER)
After some faffing about with the code, I have got the following to work in both XE2 Enterprise and Berlin Enterprise in order to open a MS Access database with a TSQLConnection object using the native ODBC dbxExpress driver.
FSQLConnectionAccess.DriverName := 'ODBC';
FSQLConnectionAccess.GetDriverFunc := 'getSQLDriverODBCW';
FSQLConnectionAccess.LibraryName := '';
FSQLConnectionAccess.VendorLib := 'odbcjt32.dll';
FSQLConnectionAccess.Params.Values['DriverPackageLoader'] := 'TDBXOdbcDriverLoader';
FSQLConnectionAccess.Params.Values['IsolationLevel'] := 'ReadCommitted';
FSQLConnectionAccess.Params.Values['RowSetSize'] := '20';
FSQLConnectionAccess.Params.Values['Database'] := '?';
FSQLConnectionAccess.Params.Values['ConnectionString'] := 'DRIVER={Microsoft Access Driver (*.mdb)};'+
'DBQ=myaccessdb.mdb;'+
'UID=;'+
'PWD=accessdbpwd;'+
'DriverId=25;'+
'FIL=MS Access;';
Note how the connection string used is nothing like the ones shown in http://www.connectionstrings.com, which is the main reason I don't use that site for Delphi development.
A similar change is required if you were using the DevArt SQL Server driver and want to change to the native MS SQL Server one:
// FSQLConnectionSQL.DriverName := 'DevArtSQLServer';
// FSQLConnectionSQL.GetDriverFunc := 'getSQLDriverSQLServer';
// FSQLConnectionSQL.LibraryName := 'dbexpsda40.dll';
// FSQLConnectionSQL.VendorLib := 'sqloledb.dll';
becomes
FSQLConnectionSQL.DriverName := 'MSSQL';
FSQLConnectionSQL.GetDriverFunc := 'getSQLDriverMSSQL';
FSQLConnectionSQL.LibraryName := 'dbxmss.dll';
FSQLConnectionSQL.VendorLib := 'sqlncli10.dll';
The reason we had been using Open ODBC and DevArt SQL Server drivers is that we originally had XE2 Professional, which did not include ODBC or SQL drivers as standard. Now that we are using Enterprise, that is no longer an issue. The only thing left to do is determine if the old and new drivers behave differently.
The answer to the original question on how to setup a DSN-less connection to a MS Access database in XE2 Enterprise using the ODBC driver is:
FSQLConnectionAccess.DriverName := 'ODBC';
FSQLConnectionAccess.GetDriverFunc := 'getSQLDriverODBCW';
FSQLConnectionAccess.LibraryName := '';
FSQLConnectionAccess.VendorLib := 'odbcjt32.dll';
FSQLConnectionAccess.Params.Values['DriverPackageLoader'] := 'TDBXOdbcDriverLoader';
FSQLConnectionAccess.Params.Values['IsolationLevel'] := 'ReadCommitted';
FSQLConnectionAccess.Params.Values['RowSetSize'] := '20';
FSQLConnectionAccess.Params.Values['Database'] := '?';
FSQLConnectionAccess.Params.Values['ConnectionString'] := 'DRIVER={Microsoft Access Driver (*.mdb)};'+
'DBQ=myaccessdb.mdb;'+
'UID=;'+
'PWD=accessdbpwd;'+
'DriverId=25;'+
'FIL=MS Access;';
Why not modify source?
Like below
constructor TDbxOpenOdbcDriver.Create(DriverDef: TDBXDriverDef);
begin
{$IF CompilerVersion > 18.50}
inherited Create(DriverDef, TDBXDynalinkDriverLoader);
InitDriverProperties(TDBXOpenOdbcProperties.Create(DriverDef.FDBXContext));
// '' makes this the default command factory.
//AddCommandFactory('', CreateCommandOpenOdbc);
AddCommandFactory('Dbxoodbc', CreateCommandOpenOdbc);
//AddCommandFactory(TDBXCommandTypes.DbxMetaData
{$ELSE}
raise EDbxOODBCDriverError.Create('Not Implemented: DbxOpenOdbcDriver.Create(DriverDef: TDBXDriverDef)');
{$IFEND}
end;
I have client-server system that uses DataSnap. I want to log the client application data so I use the TDSServer - OnConnect Event. In this event I can access what I want with the following code:
IP:= DSConnectEventObject.ChannelInfo.ClientInfo.IpAddress
ClientPort:= DSConnectEventObject.ChannelInfo.ClientInfo.ClientPort
Protocol:= DSConnectEventObject.ChannelInfo.ClientInfo.Protocol
AppName:= DSConnectEventObject.ChannelInfo.ClientInfo.AppName
first 3 lines are OK but AppName is empty!!!
(I run server and client on the same computer i.e. localhost)
I have been unable to find any online information about how to specify the AppName when the client connects via TCP/IP. If you look at the code
procedure TDSTCPChannel.Open;
var
ClientInfo: TDBXClientInfo;
begin
inherited;
FreeAndNil(FChannelInfo);
FChannelInfo := TDBXSocketChannelInfo.Create(IntPtr(FContext.Connection), FContext.Connection.Socket.Binding.PeerIP);
ClientInfo := FChannelInfo.ClientInfo;
ClientInfo.IpAddress := FContext.Connection.Socket.Binding.PeerIP;
ClientInfo.ClientPort := IntToStr(FContext.Connection.Socket.Binding.PeerPort);
ClientInfo.Protocol := 'tcp/ip';
FChannelInfo.ClientInfo := ClientInfo;
end;
in DataSnap.DSTCPServerTransport.Pas it is evident that the ClientInfo.AppName is not set.
However, the following work-around works with the Seattle demo DataSnap Basic Server + Client:
In the client, add a Param 'AppName' to the SqlConnection1 component's Params and
set its value to something like 'MyTestApp'. Recompile the client.
Open the server in the IDE and modify the ServerContainerForm's code as shown below.
Code:
uses
[...], DBXTransport;
procedure TForm8.DSServer1Connect(DSConnectEventObject: TDSConnectEventObject);
var
S : String; // added
Info : TDBXClientInfo; // added
begin
ActiveConnections.Insert;
if DSConnectEventObject.ChannelInfo <> nil then
begin
ActiveConnections['ID'] := DSConnectEventObject.ChannelInfo.Id;
ActiveConnections['Info'] := DSConnectEventObject.ChannelInfo.Info;
end;
ActiveConnections['UserName'] := DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName];
ActiveConnections['ServerConnection'] := DSConnectEventObject.ConnectProperties[TDBXPropertyNames.ServerConnection];
ActiveConnections.Post;
InsertEvent('Connect');
// following added to get AppName from client
S := DSConnectEventObject.ConnectProperties['AppName'];
Info := DSConnectEventObject.ChannelInfo.ClientInfo;
Info.AppName := S;
DSConnectEventObject.ChannelInfo.ClientInfo := Info;
Caption := DSConnectEventObject.ChannelInfo.ClientInfo.AppName;
end;
As you can see, it works by picking up the value set for AppName in the client's
SqlConnection1.Params in the call to `DSConnectEventObject.ConnectProperties['AppName']'
and then display it on the Caption of the ServerContainerForm.
Obviously, you could pass any other name/value pair by adding them to the SqlConnection's Params on the client and then pick them up on the server by calling DSConnectEventObject.ConnectProperties[].
I am in some strange situation. I made DataSnap REST server and client. All REST server's methods are call by client through TRESTClient. My REST Server is Apache Module. Also I used TSQLConnection & TDSClientCallbackChannelManager for Peer-to-Peer callback in cleint. I set TDSServer ChannelResponseTimeout = 0 and TDSHTTPWebDispatcher SessionTimeout = 0. Still my client timed out after few seconds. I set TDSClientCallbackChannelManager CommunicationTimeout=0 and ConnectionTimeout=0. The error I am getting in TWinHTTPClient.DoExecuteRequest method of System.Net.HttpClient.Win. Strange thing is on debug mode I got AV but in exe mode I don't receive any AV but none of my callback is working though the REST methods are executing. I also tried set LifeCyle of TDSServerClass to Session & Invocation, both gives timed out. Below is some code for DSClientCallback & TSQLConnection:
SQLConnection.Params.Values['HostName'] := SERVERIP;
SQLConnection.Params.Values['Port'] := SERVER_PORT.ToString;
SQLConnection.Params.Values['ConnectionTimeout'] := '0';
SQLConnection.Connected := True;
ClientCallbackManager.CommunicationTimeout := '0';
ClientCallbackManager.ConnectionTimeout := '0';
ClientCallbackManager.DSHostname := SERVERIP;
ClientCallbackManager.DSPort := SERVER_PORT.ToString;
fClientCallbackId := TDSTunnelSession.GenerateSessionId;
ClientCallbackManager.DSPath := 'mypath';
ClientCallbackManager.ManagerId := TDSTunnelSession.GenerateSessionId;
fClientId := ClientCallbackManager.ManagerId;
ClientCallbackManager.RegisterCallback(fClientCallbackId,
'mychannel', TServerCallback.Create);
What I am doing wrong or missing? Please help. I also post this to Embarcadero Datasnap Forum without any response https://forums.embarcadero.com/thread.jspa?threadID=229678&tstart=0