Jump to content
Baxing

TWSocket problem on Delphi Intraweb

Recommended Posts

Hello,

 

I have tested connecting to Web Socker Server with Component TWSocket on Delphi IntraWeb Application.

But an error occurred as attached picture. The command for connecting to Socket server is used within the TIWButton's OnAsyncClick event with the following commands:

 

  with sktClient do
  begin
    Proto := 'tcp';
    Port := 'localhost';
    Addr := 'telnet';
    LineMode := True;
    LineEnd := #13#10;
    Connect;
  end

 

which this command can be used normally on VCL..

 

Please guide me for use ICS Sockets components normally on Delphi IntraWeb. Because now I can't connect and send/receive any data if using ICS Sockets components on Delphi IntraWeb application.

 

 

Thanks

ics intraweb error.jpg

Edited by Baxing

Share this post


Link to post

I have zero knowledge about IntraWeb but I call tell you the requirement for ICS : You need a message pump to have the events triggered. If IntraWeb lacks a message pump, you may pump all your ICS stuff within a single thread having his own message pump.

Share this post


Link to post

Know nothing about IntraWeb, what IP addresses did you configure it to listen on? 

 

There are several ICS samples that illustrate simple TCP connect.  For simple telnet, you would be better off using TIcsIpStrmLog than TWSocket, much faster and easier to use, see sample OverbyteIcsIpStmLogTst,dpr. 

 

Angus

 

Share this post


Link to post

I want to send data between multi client and server privately with TCP for some specific work. In the beginning I tested via localhost as the first step. Before putting the server part on the real server and in production I will use SSL as well.

 

I think using TWSocket(TSslWSocket) for Client (on Delphi Intrraweb App.) and TWSocketServer(TSslWSocketServer) for Server (on VCL) is the answer.

 

How to use message pump?  Is there a usage example to suggest me?
or

Using TICsIpStrmLog can do as I want please tell me.

 

I've just used the ICS for the first time and tested it on VCL from the available samples and enough to be quite as I want. But encountered a problem when using it with IntraWeb.


Please help guide me again.

Share this post


Link to post

TICsIpStrmLog can be configured as a server or client, it is a much easier to use version of TWSocket/TWSocketServer, and supports SSL/TLS.  The sample application I mentioned can run as both client and server at the same time, sending data to itself.  Or you can run two instances on separate PCs sending data to each other. Once connected, there is one event to send a string of data, and another event that receives strings, very simple to use.   Unfortunately, the sample looks complicates because it illustrates all the features of the component.

 

Angus

Share this post


Link to post

I'll try to test it according to your suggestion. and try to send data from server to specified cleint.

Share this post


Link to post

Now, I using IntraWeb version 15.2.37 on Delphi 10.3.3 (and using ICS Version 8.68)

Share this post


Link to post

Can you confirm if IntraWeb has a message pump? To check, use Classes.AllocateHWnd to create a hidden window handle and attache a WndProc to it. Then from a button in your user interface, PostMessage a message to that window handle and from the WndProc, check if the message is received. First check in a normal VCL application to be sure you understand how it works. Then check within an IntraWeb application.

Share this post


Link to post

I can't do it, Please give me a sample code for VCL and I will try it in the IntraWeb app.

Share this post


Link to post

Here is a sample simple application:

image.png.4846b6edf071cfc31fc86876f0ba9c0d.png

 

Delphi unit :

 

unit WinHandleTestMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    CreateHWNDButton: TButton;
    Memo1: TMemo;
    PostMessageButton: TButton;
    DestroyHWNDButton: TButton;
    procedure CreateHWNDButtonClick(Sender: TObject);
    procedure DestroyHWNDButtonClick(Sender: TObject);
    procedure PostMessageButtonClick(Sender: TObject);
  private
    FWinHandle : HWND;
    procedure WndProc(var Msg: TMessage);
  public
    constructor Create(AOwner : TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TForm1.Create(AOwner: TComponent);
begin
    inherited;
    FWinHandle := INVALID_HANDLE_VALUE;
end;

procedure TForm1.CreateHWNDButtonClick(Sender: TObject);
begin
    if FWinHandle <> INVALID_HANDLE_VALUE then begin
       System.Classes.DeallocateHWnd(FWinHandle);
       FWinHandle := INVALID_HANDLE_VALUE;
       Memo1.Lines.Add('Window handle destroyed')
    end;
    FWinHandle := System.Classes.AllocateHwnd(WndProc);
    if FWinHandle = INVALID_HANDLE_VALUE then
       Memo1.Lines.Add('Error creating window handle')
    else
       Memo1.Lines.Add(Format('Window handle create %d', [FWinHandle]));
end;

procedure TForm1.DestroyHWNDButtonClick(Sender: TObject);
begin
    if FWinHandle = INVALID_HANDLE_VALUE then
        Memo1.Lines.Add('Window handle not created yet')
    else begin
       System.Classes.DeallocateHWnd(FWinHandle);
       FWinHandle := INVALID_HANDLE_VALUE;
       Memo1.Lines.Add('Window handle destroyed')
    end;
end;

procedure TForm1.PostMessageButtonClick(Sender: TObject);
begin
    if FWinHandle = INVALID_HANDLE_VALUE then
        Memo1.Lines.Add('Window handle not created')
    else if not PostMessage(FWinHandle, WM_USER, 1234, 5678) then
        Memo1.Lines.Add('PostMessage failed');
end;

procedure TForm1.WndProc(var Msg: TMessage);
begin
    Memo1.Lines.Add(Format('MSG=%d', [Msg.Msg]));
end;

end.

 

The VCL form:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 289
  ClientWidth = 382
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -12
  Font.Name = 'Segoe UI'
  Font.Style = []
  PixelsPerInch = 96
  TextHeight = 15
  object CreateHWNDButton: TButton
    Left = 40
    Top = 32
    Width = 98
    Height = 25
    Caption = 'Create HWND'
    TabOrder = 0
    OnClick = CreateHWNDButtonClick
  end
  object Memo1: TMemo
    Left = 40
    Top = 72
    Width = 297
    Height = 185
    Lines.Strings = (
      'Memo1')
    TabOrder = 1
  end
  object PostMessageButton: TButton
    Left = 144
    Top = 32
    Width = 89
    Height = 25
    Caption = 'PostMessage'
    TabOrder = 2
    OnClick = PostMessageButtonClick
  end
  object DestroyHWNDButton: TButton
    Left = 240
    Top = 32
    Width = 97
    Height = 25
    Caption = 'Destroy HWND'
    TabOrder = 3
    OnClick = DestroyHWNDButtonClick
  end
end

 

Share this post


Link to post

As Chris Rutkow said above, you code:

 

with sktClient do
  begin
    Proto := 'tcp';
    Port := 'localhost';
    Addr := 'telnet';
    LineMode := True;
    LineEnd := #13#10;
    Connect;
  end

 

Should become:

with sktClient do begin
    Proto    := 'tcp';
    Port     := 'telnet';
    Addr     := '127.0.0.1';   // Using dotted IP is faster
    LineMode := True;
    LineEnd  := #13#10;
    Connect;
end;

 

Share this post


Link to post

Hello, Chris RutkowFPiette

 

I've changed that setting port and addr but still having the same problem. (Invalid argument (#10022 in WSAAsyncSelect)

 

 

I found that if I put the connection code to event OnFormShow, it will be able to connected the server normally.

But can not be used on event AsyncClick of any control, which in use is required on this event.

 

Thanks

Share this post


Link to post

Hello FPiette,

 

This is result of you sample code on VCL Application when press Create Hwnd and Post Message button

 

image.png.9dcc9a071f46137b97908dcbfbc01cb6.png

 

and IntraWeb Application when press Create Hwnd and Post Message button

 

image.thumb.png.f1ab888de90d3c405e7f59aec2d1350f.png

Share this post


Link to post

Probably no message pump. Let's add the error number to know more.

Replace

        Memo1.Lines.Add('PostMessage failed');

by

        Memo1.Lines.Add(Format('PostMessage failed with error %d', [GetLastError]));

then try again and tell us the error code PostMessage returns.

Edited by FPiette

Share this post


Link to post
1 hour ago, FPiette said:

Probably no message pump. Let's add the error number to know more.

Replace


        Memo1.Lines.Add('PostMessage failed');

by


        Memo1.Lines.Add(Format('PostMessage failed with error %d', [GetLastError]));

then try again and tell us the error code PostMessage returns.

This Error code :

 

PostMessage failed with error 1400

Share this post


Link to post

Windows Error code 1400 is ERROR_INVALID_WINDOW_HANDLE this means PostMessage has not received the handle created. Make sure the is no typo in the code you copied from my example and once more change the line to :

 

Memo1.Lines.Add(Format('PostMessage failed with error %d (HWND=%d)', [GetLastError, FWinHandle]));

The handle value should be the same as the one displayed after  call to AllocateHWnd.

One possible mistake you have done is not passing FWinHandle to PostMessage.

 

Share this post


Link to post

I show you for coding from your sample in my IntraWeb application

 

unit Unit1;

interface

uses
  Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, IWCompMemo,
  Vcl.Controls, IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl, IWControl,
  IWCompButton,
  Winapi.Windows, Winapi.Messages;

type
  TIWForm1 = class(TIWAppForm)
    BTNCreateHwnd: TIWButton;
    BTNPostMessage: TIWButton;
    BTNDestroyHwnd: TIWButton;
    MMO1: TIWMemo;
    procedure BTNCreateHwndAsyncClick(Sender: TObject;
      EventParams: TStringList);
    procedure BTNDestroyHwndAsyncClick(Sender: TObject;
      EventParams: TStringList);
    procedure BTNPostMessageAsyncClick(Sender: TObject;
      EventParams: TStringList);
  private
    FWinHandle : HWND;

    procedure WndProc(var Msg: TMessage);
  public
    constructor Create(AOwner : TComponent); override;
  end;

implementation

{$R *.dfm}


{ TIWForm1 }

procedure TIWForm1.BTNCreateHwndAsyncClick(Sender: TObject;
  EventParams: TStringList);
begin
  if FWinHandle <> INVALID_HANDLE_VALUE then
  begin
    System.Classes.DeallocateHWnd(FWinHandle);
    FWinHandle := INVALID_HANDLE_VALUE;
    mmo1.Lines.Add('Window handle destroyed')
  end;
  FWinHandle := System.Classes.AllocateHwnd(WndProc);
  if FWinHandle = INVALID_HANDLE_VALUE then
    mmo1.Lines.Add('Error creating window handle')
  else
    mmo1.Lines.Add(Format('Window handle create %d', [FWinHandle]));
end;

procedure TIWForm1.BTNDestroyHwndAsyncClick(Sender: TObject;
  EventParams: TStringList);
begin
  if FWinHandle = INVALID_HANDLE_VALUE then
    mmo1.Lines.Add('Window handle not created yet')
  else
  begin
    System.Classes.DeallocateHWnd(FWinHandle);
    FWinHandle := INVALID_HANDLE_VALUE;
    mmo1.Lines.Add('Window handle destroyed')
  end;
end;

procedure TIWForm1.BTNPostMessageAsyncClick(Sender: TObject;
  EventParams: TStringList);
begin
  if FWinHandle = INVALID_HANDLE_VALUE then
    mmo1.Lines.Add('Window handle not created')
  else if not PostMessage(FWinHandle, WM_USER, 1234, 5678) then
//    mmo1.Lines.Add('PostMessage failed');
//    mmo1.Lines.Add(Format('PostMessage failed with error %d', [GetLastError]));
    mmo1.Lines.Add(Format('PostMessage failed with error %d (HWND=%d)', [GetLastError, FWinHandle]));
end;

constructor TIWForm1.Create(AOwner: TComponent);
begin
  inherited;

  FWinHandle := INVALID_HANDLE_VALUE;
end;

procedure TIWForm1.WndProc(var Msg: TMessage);
begin
  mmo1.Lines.Add(Format('MSG=%d', [Msg.Msg]));
end;

initialization
  TIWForm1.SetAsMainForm;

end.

 

And this is result to show GetLastError, FWinHandle (click CreateHwnd and then click PostMessage buttons)

 

image.thumb.png.a6eae7f9c555c77e5b737a7290255ea7.png

 

 

Thank you

Share this post


Link to post

So the handle is correct and yet the error 1400 (Invalid handle) is triggered.

Maybe PostMessage is not the one we think it is. Try with a fully qualified name:

procedure TForm1.PostMessageButtonClick(Sender: TObject);
begin
    if FWinHandle = INVALID_HANDLE_VALUE then
        Memo1.Lines.Add('Window handle not created')
    else if not WinApi.Windows.PostMessage(FWinHandle, WM_USER, 1234, 5678) then
        Memo1.Lines.Add(Format('PostMessage failed with error %d (HWND=%d)',
                               [GetLastError, FWinHandle]));
end;

 

Share this post


Link to post
1 hour ago, FPiette said:

So the handle is correct and yet the error 1400 (Invalid handle) is triggered.

Maybe PostMessage is not the one we think it is. Try with a fully qualified name:


procedure TForm1.PostMessageButtonClick(Sender: TObject);
begin
    if FWinHandle = INVALID_HANDLE_VALUE then
        Memo1.Lines.Add('Window handle not created')
    else if not WinApi.Windows.PostMessage(FWinHandle, WM_USER, 1234, 5678) then
        Memo1.Lines.Add(Format('PostMessage failed with error %d (HWND=%d)',
                               [GetLastError, FWinHandle]));
end;

 

Hello

 

It give same result

image.thumb.png.80b00e08bb4aadaf1468fbccd48da562.png

 

Share this post


Link to post

Now I'm convinced that this is an issue with IntraWeb. The test program has nothing to do with ICS. It only make use of the same fundamental Windows functions that ICS uses. If the test program doesn't work, then ICS won't work either.

 

I suggest you contact IntraWeb support with the test programs (Both VCL and IntraWeb) so that they can fix their code.

Share this post


Link to post
18 hours ago, FPiette said:

Now I'm convinced that this is an issue with IntraWeb.

I don't agree, since the failure is coming straight from the Win32 API itself.  But there is definitely something fishy going on here.  The only thing that makes sense to me is if maybe the window created by AllocateHWnd() was silently destroyed, but the FWinHandle variable wasn't updated to reflect that.  What does IsWindow(FWinHandle) report after AllocateHWnd() returns, and when PostMessage() fails?  Even if there were no message *pump* to process messages, there would still be a message *queue* (a thread's message *queue* is created the first time the thread calls any function in user32.dll) when the window is created.  PostMessage() fails with error 1400 only if the provided HWND is not a valid window, not if the window is valid but no message pump is running.  PostMessage() has no concept of whether a pump is running or how often, it only cares if the queue exists and is not full.

Quote

The test program has nothing to do with ICS.

And the failing code has nothing to do with IntraWeb itself, but rather with the Win32 API directly.

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×