3

我正在尝试扩展第 3 方应用程序,以便除了使用 Windows 窗体 GUI(需要混合模式)之外,还可以通过命令行调用它。这是一个相当简单的程序,它基本上加载一个文件,然后单击一个按钮它开始发送 UDP 网络数据包。

我需要从另一个调用应用程序并希望传入一个参数并且需要能够将 ExitCode 返回给调用应用程序。从我读过的内容来看,为了做到这一点,您需要添加编译器指令 {APPTYPE CONSOLE}。

我这样做了,我的应用程序按照我的意愿工作,只是发送网络数据包的速度变慢了。 我发现每当我在表格上移动鼠标时。即网络传输率显着提高。我怀疑存在某种类型的 Windows 消息队列问题,移动鼠标会导致中断,进而导致消息队列被处理?

我四处搜索并尝试在 Timer 中以 1ms 的间隔调用 Application.ProcessMessages 和 PeekMessages,但这根本没有帮助。我在这个用户手册中找到了一些其他应用程序,它说 APPTYPE CONSOLE 和 GUI 类型都支持 Indy 10。坦率地说,这只是让我感到困惑,因为我会假设所有网络库都可以在这两种模式下工作......但就像我说的我不熟悉 Delphi。

我很肯定这个问题被隔离在我的应用程序中的一行中,即是否包含 {APPTYPE CONSOLE}。

有人有想法么?

版本信息:
Delphi 7 Personal (Build 4.453)
Indy 9.0.4

4

5 回答 5

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 程序执行此操作实际上比从控制台程序执行此操作更容易。无论如何您都需要使用命令行参数启动程序,所以为什么不给它一个临时文件的名称,等待应用程序完成,读入文件然后删除它呢?

于 2009-07-29T07:41:22.777 回答
2

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).

于 2009-07-29T03:25:44.197 回答
1

如果我对您的理解正确,那么您希望您的应用程序具有两种模式:

  1. 如果未传递任何参数,则在 GUI 模式下运行
  2. 否则以非 GUI 模式运行

最简单的是,如果您可以集中您的逻辑,以便可以从一种方法(在我的示例中为CoreLogic )调用它。

下面的应用程序应该可以正常工作。

两个技巧:

  1. Application.ShowMainForm := False; 这根本不会使 MainForm 显示。
  2. 退出代码:= 327;这将设置您的返回码(如已经提到的mghieGerry )。

几点注意事项:

  • 因为 CoreLogic 不处理任何 Windows 消息,所以应用程序中依赖于正在处理的 Windows 消息的任何内容都会停止。
  • 如果您需要 Windows 消息处理,那么您的 CoreLogic 中的所有Application.ProcessMessages()
  • 如果您需要您的表单可见,那么您可以更改 MainForm 中的逻辑以测试命令行参数,并在完成工作时退出(通过调用Application.Terminate())。放置该逻辑的最佳位置是 MainForm.OnShow 事件的事件方法。

希望这可以帮助 :-)

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.
于 2009-07-30T08:53:35.497 回答
0

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.

于 2009-07-29T11:45:24.423 回答
0

一个 1 毫秒的计时器只会每 40 毫秒触发一次(由于 Windows 的限制),所以它无济于事。我已经看到了像您描述的混合控制台和 GUI 应用程序的效果,另一个是它们没有正确最小化。

除了在项目中启用控制台之外,您还可以在程序启动后使用 CreateConsole API 调用(不确定名称是否正确)来创建控制台。我在我所做的一个(!)程序中没有看到任何不利影响。

但这仅在您想写入控制台时才有必要。如果您只想处理命令行参数并返回退出代码,则不需要控制台。只需评估参数的 ParamCount/ParamStr 函数并为返回值设置 ExitCode。

于 2009-07-29T05:48:24.970 回答