Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Message to change an edit field
#1
Probably something simple I missed - but I did search here first - how to update the contents of an IWedit box with the results of another program running on the host? The context is this: my Intraweb application is acting as a remote front end to an otherwise standalone application running on the same computer. Click a button on the Intraweb web application and using pipes, that click reaches the main app, which comes up with a response that wants to appear in the IWedit field. It's like the pipe works one way, but not the other.
What is the technique then to fill out fields on your Intraweb page? Thanks.
Edit - I should say I'm using 14.2.10 and Rio 10.3.1
Reply
#2
If the IWEdit is named Edit1, you just do:

Edit1.Text := 'Whatever you want';
Reply
#3
Thank you, Kudzu, but I guess I didn't explain the scenario well enough. The "Whatever you want" is coming from a separate non-IntraWeb application running on the same computer as the IntraWeb one. I am able to have the Intraweb UI sense, for example, a button push and send a "Which one" message through a Windows pipe to the listening subroutine in the other program. That program generates a response and sends it back through the pipe. That response is not being seen by the IntraWeb application.
I think it's perhaps a two part solution. One, to somehow receive an interprocess message from another app, and two, to refresh the IntraWeb web page to the user with the contents of the message.
Reply
#4
If the issue is with the pipe, that seems like a general Delphi question rather than an IntraWeb one.

Can you get it to return data via the pipe in a normal application?
Reply
#5
Yes, it works great both directions in a little test app. The Intraweb app sends nicely, but does not receive. Would you like to have me make any files available? BTW, I'm not saying there's anything wrong with Intraweb. The pipe client code may be in the wrong place, or we need to use something else for IPC.
Reply
#6
As a rule I don't have time to dig into general Delphi issues but I can try to help. If you want to post the pipe handling code as text in a message I can give it a glance though.

If its a simple console you are calling out too, there are tons of code snippets out there. I can even send you one we use for 17 (its used from within the main thread of the IDE, but it should work fine in a thread)

The "normal" app in which it works, is it a console or VCL forms app? There are a few possibilities that are most likely:

1) IntraWeb is threaded because its a server. So your code will be running in a secondary thread, not the main thread.

2) Because you are running in threads, there is no main message queue. If your code depends on this, this will cause issues. Most piping code should not need the message queue though.

3) Permissions - This can affect both the hosted app, but also inheritance of handles which are used for pipes.
Reply
#7
A little delay in my response as I waited until my key for 15.0.18 came through and I could retest with it. Same result - we send from IntraWeb OK, but nothing is coming in.
The app I'm trying to send from is a vcl app. Here is the pipe receive code in IntraWeb that never gets invoked:
procedure TIWForm2.PipeClient1PipeMessage(Sender: TObject; Pipe: HPIPE;
Stream: TStream);
var
Msg : String;
I: integer;
begin
SetLength(Msg, Stream.Size div SizeOf(Char));
Stream.Position := 0;
Stream.Read(Msg[1], Stream.Size);
if length(Msg)>0 then begin
...do stuff
end;

I also put a package of code up on my web site www.baxeldata.com/special/Package.zip that has the source code of the IntraWeb app, a test pipe server, the compiled IntraWeb app, and the pipe unit code. The pipe package is the francois-piette update to the Russell Libby work you may be familiar with. To test, you may start the pipe server and then start CPLremote and a browser. You will see the two programs connect and if you click something like 'Play' and a number, and then 'Send', you will see that message arrive at the pipe server. Enter a message on the server like 'read30' and Send (or Broadcast), which CPLremote should understand, nothing arrives. Puttting a breakpoint at the SetLength(Msg... line never gets called.

If as you say, you have a pipe code snippet that you know works both ways, please share and I will try to build it in the code. I hope we can resolve this as controlling a running app via IntraWeb is, or should be, a valuable feature of your product.
Thank you.
Reply
#8
"running app via IntraWeb is, or should be, a valuable feature of your product"

That isn't the issue. The issue I can fully assure you has nothing to do with IW. IW does not interfere with pipes. It has to do with permissions of your app, threading, or the like. This is standard Delphi and can affect any Delphi app that is deployed in the same manner.

This is code we currently use to call out to a console app and get the response. I have not tested it in threads as it isnt needed for the same purpose. Pipes have a lot of issues though, and this code works around the common pitfalls.

As I stated previously I will try to help within limits as this is not an IW issue. But you may consider also asking on a general Delphi forum such as EMBTs forums. The issue here though is that you need to ask in a more generic way "im running in a thread, etc"... rather than "It an IW app", because it being an IW app is not the defining issue in this case.

When you are testing in your IW app, how are you deploying? Is it just an SA exe? If its a service or ISAPI there are extra permissions issues.

unit IW17.Design.ConsoleRunner;

interface

type
T17ConsoleRunner = class
public type
TTextProc = reference to procedure(const aText: string);
public
class function Run(const aCommand: string; aParams: string; aCallBack: TTextProc = nil): string;
end;

implementation

uses
Winapi.Windows, System.SysUtils, System.IOUtils, Vcl.Forms;

function CreateEnvironmentBlock(var lpEnvironment: Pointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall; external 'Userenv.dll';
function DestroyEnvironmentBlock(pEnvironment: Pointer): BOOL; stdcall; external 'Userenv.dll';

class function T17ConsoleRunner.Run(const aCommand: string;
aParams: string; aCallBack: TTextProc = nil): string;
const
xBufSize = 2048;
var
xExitCode: Cardinal;
xSecurity: TSecurityAttributes;
xReadHandle, xWriteHandle: THandle;
xStartupInfo: TStartupInfo;
xProcessInfo: TProcessInformation;
xBuffer1, xBuffer2: array [0 .. xBufSize] of AnsiChar;
xRead, xRunning, xBytesAvail: DWORD;
xResult: TStringBuilder;
xWorkingDir: string;
xEnvironment: Pointer;
begin
xExitCode := 0;
xResult := TStringBuilder.Create; try
xSecurity.nLength := SizeOf(TSecurityAttributes);
xSecurity.bInheritHandle := true;
xSecurity.lpSecurityDescriptor := nil;

Win32Check(CreatePipe(xReadHandle, xWriteHandle, @xSecurity, 0));
try
FillChar(xStartupInfo, SizeOf(TStartupInfo), #0);
xStartupInfo.cb := SizeOf(TStartupInfo);
xStartupInfo.hStdInput := xReadHandle;
xStartupInfo.hStdOutput := xWriteHandle;
xStartupInfo.hStdError := xWriteHandle;
xStartupInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
xStartupInfo.wShowWindow := SW_HIDE;

// We need this to load system paths else child processes called by any
// exe we call will fail.
Win32Check(CreateEnvironmentBlock(xEnvironment, 0, True)); try
xWorkingDir := TPath.GetDirectoryName(aCommand);
Win32Check(CreateProcess(nil, PChar(aCommand + ' ' + aParams), @xSecurity
, @xSecurity, true, CREATE_UNICODE_ENVIRONMENT, xEnvironment, PChar(xWorkingDir)
, xStartupInfo, xProcessInfo));
try
repeat
xRunning := WaitForSingleObject(xProcessInfo.hProcess, 100);
PeekNamedPipe(xReadHandle, nil, 0, nil, @xBytesAvail, nil);
if xBytesAvail > 0 then begin
repeat
Win32Check(ReadFile(xReadHandle, xBuffer1[0], xBufSize - 1, xRead, nil));
xBuffer1[xRead] := #0;
OemToCharA(xBuffer1, xBuffer2);

// To avoid using up unnecessary RAM, we only collect if no callback
if Assigned(aCallBack) then begin
aCallBack(string(xBuffer2));
end else begin
xResult.Append(string(xBuffer2));
end;
until xRead < xBufSize;
end;
Application.ProcessMessages;
until xRunning <> WAIT_TIMEOUT;
Win32Check(GetExitCodeProcess(xProcessInfo.hProcess, xExitCode));
finally
CloseHandle(xProcessInfo.hProcess);
CloseHandle(xProcessInfo.hThread);
end;
finally
CloseHandle(xReadHandle);
CloseHandle(xWriteHandle);
end;
finally
DestroyEnvironmentBlock(xEnvironment);
end;

Result := xResult.ToString;
if xExitCode <> 0 then raise Exception.Create(Result);
finally xResult.Free; end;
end;

end.
Reply
#9
Thank you for the snippet. It will take me a bit to digest it as I'm unfamiliar with Environment. I am building a stand-alone app, to answer that question. My work-around right now is to have the app I want to 'talk' to receive a command message via a pipe from the IW app - that works fine - and have the controlled app write its response to a text file. For commands where the IW app is expecting a response, a timer is set to look for that text file. If there, then read it and take appropriate action. A little clumsy, but works.
Reply
#10
Somehow one of my posts got lost... so reposting.. maybe this is what you are trying to do?

https://github.com/Atozed/IntraWeb/tree/.../CGIRunner
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)