SetWindowPos不起作用原因

时间:2021-06-18 20:36:01
新建个工程,在TForm1.Create里面写了句代码:
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE)

如果.dpr单元是
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
SetWindowPos置顶就起作用,
后来我改.dpr单元成
  Form1:= TForm1.Create(nil);
  Form1.ShowModal;
  Form1.Free;
SetWindowPos就没起作用了,但是返回值还是true,不明白什么原因,求指教,谢谢!

6 个解决方案

#1


Form1:= TForm1.Create(nil);
这样建立,  application 就管不上Tform1了

Form1:= TForm1.Create(nil);
  Form1.ShowModal;
  Form1.Free;
这已经是模态窗口了

#2


SetWindowPos起不起作用和application有关?不太明白

#3


Application在响应WM_ACTIVATEAPP时,调用了NormalizeTopMosts,而它调用DoNormalizeTopMosts,其内部EnumWindows的回调函数GetTopMostWindows中,判断了Application.MainForm = nil,如果是nil则将窗口句柄加入Application.FTopMostList,EnumWindows返回后,调用SetWindowPos改变FTopMostList中的窗口的Z-Order。

在Forms.pas可找到相关代码:
function GetTopMostWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
begin
  Result := True;
  if GetWindow(Handle, GW_OWNER) = Application.Handle then
    if (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) and
      (( Application.MainForm = nil) or PTopMostEnumInfo(Info)^.IncludeMain or
      (Handle <> Application.MainForm.Handle)) then
      Application.FTopMostList.Add(Pointer(Handle))
    else
    begin
      PTopMostEnumInfo(Info)^.TopWindow := Handle;
      Result := False;
    end;
end;

procedure TApplication.DoNormalizeTopMosts(IncludeMain: Boolean);
var
  I: Integer;
  Info: TTopMostEnumInfo;
begin
  if Application.Handle <> 0 then
  begin
    if FTopMostLevel = 0 then
    begin
      Info.TopWindow := Handle;
      Info.IncludeMain := IncludeMain;
       EnumWindows( @GetTopMostWindows, Longint(@Info));
      if FTopMostList.Count <> 0 then
      begin
        Info.TopWindow := GetWindow(Info.TopWindow, GW_HWNDPREV);
        if GetWindowLong(Info.TopWindow, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0 then
          Info.TopWindow := HWND_NOTOPMOST;
        for I := FTopMostList.Count - 1 downto 0 do
           SetWindowPos(HWND(FTopMostList[I]), Info.TopWindow, 0, 0, 0, 0,
            SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOOWNERZORDER);
      end;
    end;
    Inc(FTopMostLevel);
  end;
end;

#4


总之就是在你调用SetWindowPos置顶后,Application又调用SetWindowPos取消了置顶。

#5


引用 3 楼 s11ss 的回复:
Application在响应WM_ACTIVATEAPP时,调用了NormalizeTopMosts,而它调用DoNormalizeTopMosts,其内部EnumWindows的回调函数GetTopMostWindows中,判断了Application.MainForm = nil,如果是nil则将窗口句柄加入Application.FTopMostList,EnumWindows返回后,调用SetWindowPos改变FTopMostList中的窗口的Z-Order。

在Forms.pas可找到相关代码:
function GetTopMostWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
begin
  Result := True;
  if GetWindow(Handle, GW_OWNER) = Application.Handle then
    if (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) and
      (( Application.MainForm = nil) or PTopMostEnumInfo(Info)^.IncludeMain or
      (Handle <> Application.MainForm.Handle)) then
      Application.FTopMostList.Add(Pointer(Handle))
    else
    begin
      PTopMostEnumInfo(Info)^.TopWindow := Handle;
      Result := False;
    end;
end;

procedure TApplication.DoNormalizeTopMosts(IncludeMain: Boolean);
var
  I: Integer;
  Info: TTopMostEnumInfo;
begin
  if Application.Handle <> 0 then
  begin
    if FTopMostLevel = 0 then
    begin
      Info.TopWindow := Handle;
      Info.IncludeMain := IncludeMain;
       EnumWindows( @GetTopMostWindows, Longint(@Info));
      if FTopMostList.Count <> 0 then
      begin
        Info.TopWindow := GetWindow(Info.TopWindow, GW_HWNDPREV);
        if GetWindowLong(Info.TopWindow, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0 then
          Info.TopWindow := HWND_NOTOPMOST;
        for I := FTopMostList.Count - 1 downto 0 do
           SetWindowPos(HWND(FTopMostList[I]), Info.TopWindow, 0, 0, 0, 0,
            SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOOWNERZORDER);
      end;
    end;
    Inc(FTopMostLevel);
  end;
end;


感谢s11ss指点,看了下这里面的代码好像只有mainform才能置顶?不是mainform要置顶的话还有什么办法吗?

#6


Application在响应WM_ACTIVATEAPP时,调用了NormalizeTopMosts之后,还会调用PostMessage(FHandle, CM_DEACTIVATE, 0, 0),会触发Application的OnDeactivate事件,在此事件中重新置顶即可。

procedure TForm1.doit(Sender: TObject);
begin
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
application.OnDeactivate := doit;
end;

#1


Form1:= TForm1.Create(nil);
这样建立,  application 就管不上Tform1了

Form1:= TForm1.Create(nil);
  Form1.ShowModal;
  Form1.Free;
这已经是模态窗口了

#2


SetWindowPos起不起作用和application有关?不太明白

#3


Application在响应WM_ACTIVATEAPP时,调用了NormalizeTopMosts,而它调用DoNormalizeTopMosts,其内部EnumWindows的回调函数GetTopMostWindows中,判断了Application.MainForm = nil,如果是nil则将窗口句柄加入Application.FTopMostList,EnumWindows返回后,调用SetWindowPos改变FTopMostList中的窗口的Z-Order。

在Forms.pas可找到相关代码:
function GetTopMostWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
begin
  Result := True;
  if GetWindow(Handle, GW_OWNER) = Application.Handle then
    if (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) and
      (( Application.MainForm = nil) or PTopMostEnumInfo(Info)^.IncludeMain or
      (Handle <> Application.MainForm.Handle)) then
      Application.FTopMostList.Add(Pointer(Handle))
    else
    begin
      PTopMostEnumInfo(Info)^.TopWindow := Handle;
      Result := False;
    end;
end;

procedure TApplication.DoNormalizeTopMosts(IncludeMain: Boolean);
var
  I: Integer;
  Info: TTopMostEnumInfo;
begin
  if Application.Handle <> 0 then
  begin
    if FTopMostLevel = 0 then
    begin
      Info.TopWindow := Handle;
      Info.IncludeMain := IncludeMain;
       EnumWindows( @GetTopMostWindows, Longint(@Info));
      if FTopMostList.Count <> 0 then
      begin
        Info.TopWindow := GetWindow(Info.TopWindow, GW_HWNDPREV);
        if GetWindowLong(Info.TopWindow, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0 then
          Info.TopWindow := HWND_NOTOPMOST;
        for I := FTopMostList.Count - 1 downto 0 do
           SetWindowPos(HWND(FTopMostList[I]), Info.TopWindow, 0, 0, 0, 0,
            SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOOWNERZORDER);
      end;
    end;
    Inc(FTopMostLevel);
  end;
end;

#4


总之就是在你调用SetWindowPos置顶后,Application又调用SetWindowPos取消了置顶。

#5


引用 3 楼 s11ss 的回复:
Application在响应WM_ACTIVATEAPP时,调用了NormalizeTopMosts,而它调用DoNormalizeTopMosts,其内部EnumWindows的回调函数GetTopMostWindows中,判断了Application.MainForm = nil,如果是nil则将窗口句柄加入Application.FTopMostList,EnumWindows返回后,调用SetWindowPos改变FTopMostList中的窗口的Z-Order。

在Forms.pas可找到相关代码:
function GetTopMostWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;
begin
  Result := True;
  if GetWindow(Handle, GW_OWNER) = Application.Handle then
    if (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) and
      (( Application.MainForm = nil) or PTopMostEnumInfo(Info)^.IncludeMain or
      (Handle <> Application.MainForm.Handle)) then
      Application.FTopMostList.Add(Pointer(Handle))
    else
    begin
      PTopMostEnumInfo(Info)^.TopWindow := Handle;
      Result := False;
    end;
end;

procedure TApplication.DoNormalizeTopMosts(IncludeMain: Boolean);
var
  I: Integer;
  Info: TTopMostEnumInfo;
begin
  if Application.Handle <> 0 then
  begin
    if FTopMostLevel = 0 then
    begin
      Info.TopWindow := Handle;
      Info.IncludeMain := IncludeMain;
       EnumWindows( @GetTopMostWindows, Longint(@Info));
      if FTopMostList.Count <> 0 then
      begin
        Info.TopWindow := GetWindow(Info.TopWindow, GW_HWNDPREV);
        if GetWindowLong(Info.TopWindow, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0 then
          Info.TopWindow := HWND_NOTOPMOST;
        for I := FTopMostList.Count - 1 downto 0 do
           SetWindowPos(HWND(FTopMostList[I]), Info.TopWindow, 0, 0, 0, 0,
            SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOOWNERZORDER);
      end;
    end;
    Inc(FTopMostLevel);
  end;
end;


感谢s11ss指点,看了下这里面的代码好像只有mainform才能置顶?不是mainform要置顶的话还有什么办法吗?

#6


Application在响应WM_ACTIVATEAPP时,调用了NormalizeTopMosts之后,还会调用PostMessage(FHandle, CM_DEACTIVATE, 0, 0),会触发Application的OnDeactivate事件,在此事件中重新置顶即可。

procedure TForm1.doit(Sender: TObject);
begin
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);
application.OnDeactivate := doit;
end;