调用RecreateWnd后,窗口类样式CS_NOCLOSE不起作用

时间:2017-06-13 12:42:24

标签: delphi winapi

我需要一个表单来禁用菜单关闭按钮(并且还禁用Alt-F4关闭)所以我使用了CS_NOCLOSE类样式。如果我在CreateParams

中设置它,它会按预期工作
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited;  
  Params.WindowClass.style := Params.WindowClass.style or CS_NOCLOSE;
end;

关闭按钮已禁用,您无法使用ALT + F4关闭窗口(我有自己的关闭按钮)。

现在我添加了一个标记:FNoCloseButton,最初设置为False

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited;
  if FNoCloseButton then
    Params.WindowClass.style := Params.WindowClass.style or CS_NOCLOSE;
end;

创建表单后,我有:

procedure TForm1.Button1Click(Sender: TObject);
begin
  FNoCloseButton := True;
  RecreateWnd;
end;

单击Button1后,将重新创建窗口,但CS_NOCLOSE现在无效,并被忽略。

为什么会这样?为什么我不能在创建后更改Window类样式? (我想我可以,因为SetClassLong api存在)

我还尝试了SetClassLong

procedure TForm1.Button2Click(Sender: TObject);
begin
  SetClassLong(Self.Handle, GCL_STYLE, GetClassLong(Self.Handle, GCL_STYLE) or CS_NOCLOSE);
  DrawMenuBar(Self.Handle); // Must call this to invalidate
end;

有效。关闭被禁用(加上Alt-F4),但系统菜单项"关闭"是可见的,我可以点击它关闭窗口。所以SetClassLong的行为有点不同。

我错过了什么?

1 个答案:

答案 0 :(得分:2)

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited;
  if FNoCloseButton then
    Params.WindowClass.style := Params.WindowClass.style or CS_NOCLOSE;
end;

在上面的代码中修改窗口类信息的行没有任何效果,因为类已经在第一次运行代码时注册了;当FNoCloseButton为假时。

调用RecreateWindow后,VCL会销毁并创建窗口,但不会尝试重新注册因ERROR_CLASS_ALREADY_EXISTS而失败的类。您可能会争辩说,在销毁窗口时不取消注册类是一个设计错误,但事实并非如此。不要忘记,您可以在VCL应用程序的生命周期的不同时间生成一个实例或多个表单类实例。


对于解决方案,如果您可以确保您正在销毁的窗口是同类中的唯一实例,则可以自行取消注册该课程。然后VCL,查询类信息并发现它没有注册,将在创建窗口之前为您注册。否则你必须使用SetClassLong[Ptr],因为你已经在做了。

type
  TForm1 = class(TForm)
    ..
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure DestroyHandle; override;
    ...

..

procedure TForm1.DestroyHandle;
begin
  inherited;
  if not winapi.windows.UnregisterClass(PChar(ClassName), HInstance) then
    RaiseLastOSError;
end;