我正在尝试扩展第三方应用程序,以便除了使用Windows窗体GUI(需要混合模式)之外,还可以通过命令行调用它。这是一个相当简单的程序,基本上加载一个文件然后你点击它开始发送UDP网络数据包的按钮。
我需要从另一个调用应用程序并希望传入一个参数,并且需要能够将ExitCode返回给调用应用程序。根据我的阅读,为了做到这一点,你需要添加编译器指令{APPTYPE CONSOLE}。
我做了这个,我的应用程序按照我的意愿工作,除了发送网络数据包减慢到爬行。 我发现每当我在表格上移动鼠标时。网络传输速率显着提高。我怀疑存在某种类型的Windows消息队列问题,移动鼠标是否会导致中断,从而导致消息队列被处理?
我已经google了一下,尝试在Timer中调用Application.ProcessMessages和PeekMessages,间隔为1毫秒,这根本没有帮助。我在这个user manual for some other application中发现它表示在APPTYPE CONSOLE和GUI类型中都支持Indy 10。坦率地说,这让我感到困惑,因为我认为所有网络库都可以在两种模式下工作......但就像我说我不熟悉Delphi一样。
我很肯定该问题在我的应用程序中被隔离到一行,即是否包含{APPTYPE CONSOLE}。
有人有什么想法吗?
版本信息:
Delphi 7 Personal(Build 4.453)
Indy 9.0.4
答案 0 :(得分:6)
如果您希望将{APPTYPE CONSOLE}
添加到应用程序中,即使您希望执行混合模式,那么即使应用程序处于GUI模式,您也必须使用控制台。你当然可以关闭控制台,但这会引起一些闪烁,对我来说感觉有些骚动。
如果没有控制台程序,你应该能够做你想做的事。一个小的测试程序证明可以从GUI程序中读取退出代码:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ExitCode := 42;
Timer1.Interval := 1000;
Timer1.Enabled := TRUE;
end;
如果使用以下cmd文件执行此操作:
@echo off
start /WAIT project1.exe
echo %ERRORLEVEL%
程序显示其主窗体1秒钟,关闭,脚本将42打印到控制台窗口。
现在用于捕获输出 - 如果允许使用临时文件,那么从GUI程序执行此操作实际上比从控制台程序执行此操作更容易。您需要使用命令行参数启动程序,所以为什么不给它一个临时文件的名称,等待应用程序完成,读入文件并在之后删除它?
答案 1 :(得分:2)
如果您希望应用程序返回“错误”代码,则无需将其作为控制台应用程序。您只需要设置ExitCode,例如
ExitCode := 10;
在批处理文件中
@Echo off
project1
echo %errorlevel%
将显示应用程序,然后显示10。
注意:也可以使用AllocConsole从Windows API动态创建控制台窗口,或使用AttachConsole进行附加。
我为此创建了一个对象包装器,但不再提供代码。从内存中它不支持重定向(因为我不需要它)。
答案 2 :(得分:1)
如果我理解正确,那么您希望您的应用有两种模式:
最简单的方法是,如果您可以集中逻辑,那么可以从一个方法调用它(在我的示例中是 CoreLogic )。
以下应用程序应该可以正常工作。
两招:
一些注意事项:
希望这会有所帮助: - )
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.
答案 3 :(得分:0)
1ms的定时器只会每40毫秒触发一次(由于Windows限制),所以它无济于事。我已经看到了你用混合控制台和GUI应用程序描述的效果,另一个是它们没有正确地最小化。
您可以使用CreateConsole API调用(不确定名称是否正确),而不是在项目中启用控制台,以便在程序启动后创建一个。我在一个(!)程序中看到没有任何不良影响。
但只有在您想要写入控制台时才需要这样做。如果您只想处理命令行参数并返回退出代码,则不需要控制台。只需为参数评估ParamCount / ParamStr函数,并设置ExitCode作为返回值。
答案 4 :(得分:0)
如果你的控制台应用程序中的某些线程调用Synchronize(我猜Indy的东西实际上是这样做的),你必须做一些准备工作:
为 WakeMainThread 变量指定方法。此方法必须具有TNotifyEvent的签名。
在此方法内部调用 CheckSynchronize 。
有关其他信息,请参阅这两个项目的Delphi帮助。