Send F4 to an input pipe CMD process in Delphi - delphi

How can I get Delphi to pass F4 to the input pipe to a CMD process? I've been using code which is very similar to the answer here: Pipes in Delphi for Command Prompt
For simpliity I have only 4 objects on the form:
1. CommandText (a TMemo which displays a representation of the CMD window).
2. CommandRun (a TEdit where the CMD line to be run is entered).
3. SendBtn (a TButton used to send CommandRun.Text to CMD).
3. ExitBtn (a TButton used to exit the program).
Here are my Write Pipe and Send procedures:
Write Pipe
procedure WritePipeOut(OutputPipe: THandle; InString: string);
// writes Instring to the pipe handle described by OutputPipe
var
byteswritten: DWord;
AnsiBuf: AnsiString;
begin
// most console programs require CR/LF after their input.
if InString = 'f4' then AnsiBuf := #113#13#10
else AnsiBuf := AnsiString(InString) + #13#10;
WriteFile(InputPipeWrite, AnsiBuf[1], Length(AnsiBuf), byteswritten, nil);
end;
Send
procedure TForm1.SendBtnClick(Sender: TObject);
begin
WritePipeOut(OutputPipeWrite, CommandRun.Text);
CommandRun.Text := '';
CommandRun.SetFocus;
end;
Commands like DIR are passing through properly and the results are successfully being 'echoed' to CommandText.
My problem is that I am launching a program via the pipe. In a normal CMD window the program can only be stopped by pressing the F4 key.
I need to replicate this F4 press via the pipe and just cannot work out how to achieve this. I would be very grateful for any guidance.

F4 isn't something that would be transmitted through the standard input. This console application is almost certainly reading the keyboard input directly.
You will need to look for another way to solve this. I suggest the following:
Make sure that the cmd window has input focus and fake the input of an F4 key. I would imagine that moving the focus to the cmd window would be something that you would be reluctant to do.
Use another means to stop this process. Whether or not the process supports anything other than an F4 keypress to the console window cannot be determined from here since we know nothing about the process in question.
Forcefully terminate the process, with a call to TerminateProcess.

Related

Cant seem to recive a reply vir com port

Im trying to send a command to a dev. board and then receive a reply
E.G
(me) set attrib=yes
(Dev. Board) O.K
or
(Dev. Board) E.R.R
But it doesn't bounce back anything ... not an O.K or an E.R.R
while the board is booting echo is on .. so if I send the commands while the board is booting it
it will bounce back an 'set attrib=yes' and once booted an 'E.R.R' because you cant send commands while booting.
my best guess is that it isn't reading the reply in time or trying to read it too soon.
procedure TReaderProgrammer.Button2Click(Sender: TObject);
Var
Buffer: AnsiString;
RxData: string;
Count : integer;
begin
if ComPort1.Connected then
begin
ComPort1.WriteStr(edtSendText.Text+#13);
comport1.ReadStr(RxData,Count);
Buffer := Buffer + Rxdata;
memoRxData.Text := memoRxData.Text+Buffer+#13+#10;
end;
end;
Here are several open questions in the air, so I have to make some assumptions that might be wrong, but let's see.
I don't know what comm port library you are using, so I'm assuming it is the CPort library from SourceForge. I have never used it myself, but I have read that it is made Unicode aware, such that you can call the write methods with a unicodestring parameter which will be converted by the library to ansistring before sending. Similarily when receiving ansistring from the outer world, the library will convert to unicodestring for the Read methods.
Due to the asynchronous nature of serial communication, it is important to understand that when you send something using the write method, the method returns immediately while the library and OS spit out the characters one at at time at a pace defined by the baud rate. As a result your first code never received anything, because you were already attempting to read from the comm port before the external device even received the first character.
It is good to see that you have now taken the first step to success by implementing an event handler for (presumably library event) OnRxChar.
The OnRxChar probably fires for each character (or couple of characters). You need to have a buffer that is persistent between these events. A local var (as you have it now and which is allocated on the stack) in the event handler is not persistent, it is lost every time the event handler exits.
You should declare the Buffer variable as a field of TReaderProgrammer. I don't know why you defined the buffer to be AnsiString, but I suggest you try with string (ref discussion above regarding Unicode awareness).
type
TReaderProgrammer = class
..
RxBuffer: string;
..
end;
The buffer needs to be cleared when you send a new command to the external device in order for it to be ready to receive new data as a response to your command.
EDIT: Alternatively you can clear the RxBuffer immediately when you have received and processed a full response.
The TReaderProgrammer.ComPort1RxChar should look like this sofar:
procedure TReaderProgrammer.ComPort1RxChar(Sender: TObject; Count: Integer);
var
RxData : string;
begin
(Sender as TComPort).ReadStr(RxData,Count);
RxBuffer := RxBuffer + Rxdata;
...
end;
The rest of the event handler is, I guess, probably just to see progress of reception, so nothing more about that.

Detect key(s) held down in another process

I have a Delphi 2007 Win32 executable which sends keystrokes to other applications. My app is invoked from within these target applications by a hotkey like F11 or Shift+F11.
I want users to be able to hold down a key to abort the keystroke sending (say, if they realize they invoked my app in the wrong location). I had thought Shift, Ctrl, and Alt were good candidates because, alone, those key presses aren't likely to disrupt anything in the target application. (Escape, for instance, is a bad choice, as it might cause the target application to close one or more windows.)
I wrote the function farther below and call it periodically as follows, while sending keystrokes with the intent of detecting keys held down.
if wsAnyKeysDownInWindow( TgtWindow, [VK_Escape, VK_Menu{Alt}, VK_Control, VK_Shift] ) then
Abort;
Problem is, my app sends keystroke combinations like Shift+Tab and Ctrl+Home, which (I think) makes this approach fail--it always detects a down state for Shift and/or Ctrl. (I also tried a similar function which called SetKeyboardState just prior to beginning to send keystrokes, to set the key states' high-order (down) bit but that didn't help.)
Anyone think of a workable approach, short of hooking the keyboard?
function wsAnyKeysDownInWindow(Handle: HWnd; VKeys: array of byte): boolean;
{ Checks whether each of the VKeys set of virtual keys is down in Handle,
a window created by another process. }
var
OtherThreadID : integer;
State: TKeyboardState;
AKey: byte;
begin
Result := False;
if not IsWindow(Handle) then
exit;
OtherThreadID := GetWindowThreadProcessID( Handle, nil);
if AttachThreadInput( GetCurrentThreadID, OtherThreadID, True ) then try
GetKeyboardState(State);
for AKey in VKeys do
if (State[AKey] and 128) <> 0 then begin //If high-order bit is set, key is down
Result := True;
exit;
end;
finally
AttachThreadInput( GetCurrentThreadID, OtherThreadID, False );
end;
end;
Consider using Scroll Lock for this. It is rarely used (only of Excel comes to my mind), and you will have even visual indicator if keys are being sent or not.
BTW, Alt is not a good choice for another reason - it invokes the main menu in an application (if there is one, of course).

Sending commands directly to Zebra EPL

I am trying to send commands directly to a Zebra TLP2844 printer. I followed the suggestion made here and my final code came to be as follows:
var
cm: String;
p: TPrinter;
i: integer;
begin
p := Printer;
p.BeginDoc;
for i := 0 to memo1.Lines.Count-2 do
begin
cm := memo1.Lines[i];
if Escape(p.Canvas.Handle,
PASSTHROUGH,
Length(cm),
PAnsiChar(cm),
nil) = 0 then
ShowMessage('Command error: ' + IntToStr(GetLastError));
end;
p.EndDoc;
end;
The content of memo1 is (first line is empty) as purposed here:
N
q609
Q203,26
B26,26,0,UA0,2,2,152,B,"603679025109"
A253,26,0,3,1,1,N,"SKU 6205518 MFG 6354"
A253,56,0,3,1,1,N,"2XIST TROPICAL BEACH"
A253,86,0,3,1,1,N,"STRIPE SQUARE CUT TRUNK"
A253,116,0,3,1,1,N,"BRICK"
A253,146,0,3,1,1,N,"X-LARGE"
P1,1
The commands don't seem to be properly received or interpreted by the printer. I checked that the printer is in Page Mode (EPL2), with the suggested code I am able to open the printer handle. But nothing is printed, only a new line of labels is feeded.
I tried to completely change the commands to something obviously wrong and the behaviour is the same.
What else should I be looking to get things printed?
Most printers that take raw commands require a prefix (starting sequence of characters) and suffix (ending sequence of chars) wrapping each command. I don't know what the prefix and suffix are for the Zebra, but the documentation should tell you.
Just add a pair of constants to define the prefix and suffix, and add them to your command before sending it.
The other issue might be that you're reading the content of your commands from a TMemo, which in Delphi 2009 and higher contains Unicode strings. You're then casting them down to PAnsiChar, which may be truncating the content. Do the conversion ahead of time by defining cm as an AnsiString, and then assigning to it first (as you are) before typecasting to pass to the Escape function. I've done this in my code to illustrate it.
var
cm: AnsiString;
p: TPrinter;
i: integer;
const
ZPrefix = AnsiString('$('); // Replace values for each of these with what
ZSuffix = AnsiString(')$'); // your documentation says you should use
begin
p := Printer;
p.BeginDoc;
for i := 0 to memo1.Lines.Count-2 do
begin
cm := ZPrefix + memo1.Lines[i] + ZSuffix;
if Escape(p.Canvas.Handle,
PASSTHROUGH,
Length(cm),
PAnsiChar(cm),
nil) = 0 then
ShowMessage('Command error: ' + IntToStr(GetLastError));
end;
p.EndDoc;
end;
I program in php which is like C
I can send things to the printer just fine
my code looks like your code the only thing is I am not sure how your programming language handles the newline in php it's \n at the end of each line
if the newline is not there the print job will not print
and if the " are not sent it will not print
your EPL looks fine and should print
there is somewhere on the zebra web site a download where you can send commands to a printer which is hooked up to your computer by USB cable
think it is called Zebra Setup Utilities

Added the {APPTYPE CONSOLE} directive and now my application runs very slowly. Moving the mouse makes it run faster

I am trying to extend a 3rd party application so that it can be invoked via command line in addition to using the windows form GUI (mixed mode is desired). It's a fairly simple program which basically loads a file and then you click a button it starts sending UDP network packets.
I need to invoke the application from another and would like to pass in an argument and need to be able to return the ExitCode to the calling app. From what i've read, in order to do so you need to add the compiler directive {APPTYPE CONSOLE}.
I did this and my application worked as I wanted it to except sending the network packets slowed down to a crawl. I found that whenever I moved my mouse around on the form. That the network transfer rate increased significantly. I suspect there is some type of Windows Message queue problem and moving mouse is causing interrupts which in turn is causing the message queue to be processed?
I have googled around and tried calling Application.ProcessMessages and PeekMessages in a Timer with a 1ms interval and that didn't help at all. I found in this user manual for some other application it says that Indy 10 is supported in both APPTYPE CONSOLE and GUI types. Quite frankly this just confuses me as I would have assumed that all network library would work in both modes... but like I said I'm not familiar with Delphi.
I am positive that the issue is isolated to a single line in my application and that is whether or not {APPTYPE CONSOLE} is included or not.
Anyone have any ideas?
Version Info:
Delphi 7 Personal (Build 4.453)
Indy 9.0.4
If you add {APPTYPE CONSOLE} to your application even though you desire mixed mode execution, then you will have to live with a console even when the application is in GUI mode. You can of course close the console, but this will cause some flicker and feels a bit hackish to me.
You should be able to do what you want without a console program. A small test program proves that the exit code can be read from a GUI program:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ExitCode := 42;
Timer1.Interval := 1000;
Timer1.Enabled := TRUE;
end;
If this is executed with the following cmd file:
#echo off
start /WAIT project1.exe
echo %ERRORLEVEL%
the program shows its main form for 1 second, closes, and the script prints 42 to the console window.
Now for capturing the output - doing this from a GUI program is actually easier than doing it from a console program, if you allow for the use of a temporary file. You need to start the program with a command line parameter anyway, so why not give it the name of a temporary file, wait for the application to finish, read in the file and delete it afterwards?
If you want an application to return an "error" code there is no need to make it a console application. You only need to set the ExitCode, e.g.
ExitCode := 10;
in a batch file
#Echo off
project1
echo %errorlevel%
Will display the application, then display 10 when.
Note: It is also possible to create a console window dynamically from the windows API using AllocConsole or to attach using AttachConsole.
I created an object wrapper for this once, but no longer have the code available. From memory it didn't support redirection (because I didn't need it).
If I understand you correctly, then you want your app to have two modes:
If no argument is passed, run in GUI mode
Run in non-GUI mode otherwise
The easiest is if you can centralize your logic so it can be called from one method (CoreLogic in my example).
The below app then should work fine.
Two tricks:
Application.ShowMainForm := False; that will not make the MainForm show at all.
ExitCode := 327; which will set your return code (like mghie and Gerry already mentioned).
A few notes:
because the CoreLogic does not process any windows messages, anything in your application that depends on Windows messages being processed will stall.
if you need windows message processing, then just all Application.ProcessMessages() inside your CoreLogic
if you need your form to be visible, then you change the logic inside your MainForm to test for the commandline parameters, and exit when it's work as been done (by calling Application.Terminate()). The best place to put that logic in is the event method for the MainForm.OnShow event.
Hope this helps :-)
program VCLAppThatDoesNotShowMainForm;
uses
Forms,
MainFormUnit in 'MainFormUnit.pas' {MainForm},
Windows;
{$R *.res}
procedure CoreLogic;
begin
Sleep(1000);
ExitCode := 327;
end;
procedure TestParams;
begin
if ParamCount > 0 then
begin
MessageBox(0, CmdLine, PChar(Application.Title), MB_ICONINFORMATION or MB_OK);
CoreLogic();
Application.ShowMainForm := False;
end;
end;
begin
Application.Initialize();
Application.MainFormOnTaskbar := True;
TestParams();
Application.CreateForm(TMainForm, MainForm);
Application.Run();
end.
A timer with 1ms will only fire about every 40 ms (due to Windows limitations), so it won't help. I have seen effects like you describe with mixed console and GUI apps, another is that they don't minimize properly.
Instead of enabling the console in the project, you could probably use the CreateConsole API call (Not sure whether the name is correct) to create one after the programm was started. I have seen no adverse effects in the one (!) program I have done this.
But this is only necessary if you want to write to the console. If you only want to process command line parameters and return an exit code, you do not need a console. Just evaluate the ParamCount/ParamStr functions for the parameters and set ExitCode for the return value.
If some threads in your console application call Synchronize (and I guess the Indy stuff is actually doing that), you have to make some preparations:
Assign a method to the WakeMainThread variable. This method must have the signature of TNotifyEvent.
Inside this method call CheckSynchronize.
For additional information see the Delphi help for these two items.

determine if another application is busy

How do I check if another application is busy?
I have a program that sends text to a console. The text that I will send contains #13 char (e.g. ls#13cd documents#13dir). In other words I want to send many commands at one time and the console will process them one by one. I am sending the text character by character. Sometimes the console only executes ls and cd documents. I think maybe this is because my program continuously sends character even if the console is busy, in which case the console does not receive incoming characters.
This is my code:
procedure TForm1.SendTextToAppO(Str: String; AHandle: Integer);
var
iWindow, iPoint, i: Integer;
SPass: PChar;
sList: TStringList;
begin
sList := TStringList.Create;
ExtractStrings([#13],[' '],PChar(Str),sList);
iWindow := AHandle;// AHandle is the handle of the console
iPoint := ChildWindowFromPoint(iWindow, Point(50,50));
for i:=0 to sList.Count-1 do begin
SPass := PChar(sList[i]);
try
while(SPass^ <> #$00) do begin
SendMessage(iPoint,WM_CHAR,Ord(SPass^),0);
Inc(SPass);
end;
SendMessage(iPoint,WM_KEYDOWN,VK_RETURN,0);
except
// do nothing;
end;
end;
end;
I am using Delphi 7.
If I interpret you question correctly you are sending the text to some sort of shell/command line interpreter and you want it to execute your commands.
Usually command line interpreters output a certain prompt (like $ on a Linux system or C:\ for DOS) that indicate that they can accept new commands. You need to read the output to wait for the appropriate prompt before you send another command. If you don't your sent text will be consumed as input by the currently running command (like you experienced).
lothar is on the right track; what you want to do is, instead of using ShellExecute, use CreateProcess. Look around Stack Overflow and Google for "Console Redirection" - that'll get you what you're looking for.
I think I understand what's going on, not that I have a fix for it:
You send a command to the console. While the command is running that program will receive the keys you send.

Resources