全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:6573
推到 Plurk!
推到 Facebook!

form position 的設定

答題得分者是:歸木淡
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#1 引用回覆 回覆 發表時間:2009-09-04 16:42:54 IP:61.67.xxx.xxx 未訂閱
請問各位:

一般 MDI 的 FORM 都有一個 FORM.POSITION 可以指定顯示在螢幕上的參數, 那如果是
Application.MessageBox() 這個函數所建立的 Dialog 是否有可能也可以指定位置

ps:我知道有其他的函數有這個功能, 如 MessageDlgPos , 或者自己寫一支也可以, 但這不是我想知道的答案
因為手上有一支程式大量使用Application.MessageBox() 功能, 我只想知道有沒有最快的方式可以達成
另外寫一支同名的 Application.MessageBox, 再用 overload 方式取代也不是我想要知道的,
謝謝各位!
pedro
尊榮會員


發表:152
回覆:1187
積分:892
註冊:2002-06-12

發送簡訊給我
#2 引用回覆 回覆 發表時間:2009-09-04 17:06:09 IP:60.248.xxx.xxx 未訂閱
您好

根據,以下所講的
http://www.swissdelphicenter.ch/torry/showcode.php?id=352

我試了一下,在我的環境有用,修改mx及my變數,訊息提示位置就不一樣,不知道在你的環境可不可以用?

[code delphi]
const
mbMessage = WM_USER 1024;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure ChangeMessageBoxPosition(var Msg: TMessage); message mbMessage; public
{ Public declarations }
end;

var
Form1: TForm1;
msgCaption: PChar; // variable to hold the caption

const
mx=200;
my=100;

implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.ChangeMessageBoxPosition(var Msg: TMessage);
var
MbHwnd: longword;
MbRect: TRect;
x, y, w, h: integer;
begin
MbHwnd := FindWindow(MAKEINTRESOURCE(WC_DIALOG), msgCaption);
if (MbHwnd <> 0) then
begin
GetWindowRect(MBHWnd, MBRect);
with MbRect do
begin
w := Right - Left;
h := Bottom - Top;
end;
// center horzontal
x := mx;
// keep on screen
if x < 0 then
x := 0
else if x w > Screen.Width then x := Screen.Width - w;
//center vertical
y := my;
// keep on screen
if y < 0 then y := 0
else if y h > Screen.Height then y := Screen.Height - h;
// set new windows position
SetWindowPos(MBHWnd, 0, x, y, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOZORDER);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
PostMessage(Handle, mbMessage, 100, 0);
msgCaption := 'Confirm';
if Application.MessageBox('Has our MessageBox moved ?',msgCaption,MB_ICONQUESTION or MB_YESNO)=IDYES then
begin
end;
end;

[/code]

編輯記錄
pedro 重新編輯於 2009-09-04 17:07:19, 註解 無‧
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#3 引用回覆 回覆 發表時間:2009-09-05 00:51:58 IP:61.67.xxx.xxx 未訂閱
感謝 P兄提供的方式, 我按表操課, 試了一下, 確實可以改變 Application.MessageBox 的位置
但現在有一個問題是
這支程式有數十支MessageBox, caption 不盡相同, 這樣的做法, 我必須在每一個Application 前加上 PosMesage 的動作
我嘗試放在一啟動(oncreate)或 (onactive)上, 就沒有辦法調位置了, 是否能有下一次動作, 之後的Application.MessageBox都可以變成我想要的位置的方式?
歸木淡
中階會員


發表:1
回覆:49
積分:75
註冊:2005-09-07

發送簡訊給我
#4 引用回覆 回覆 發表時間:2009-09-06 13:18:07 IP:99.137.xxx.xxx 訂閱
試了一個小時, 用hook可以做到, 但是我覺得這並不利於程序的維護, 其實全Replace 不是更好嗎?

如果你真的想用這方法, 我可以將之封裝成一個unit再貼上來.
pedro
尊榮會員


發表:152
回覆:1187
積分:892
註冊:2002-06-12

發送簡訊給我
#5 引用回覆 回覆 發表時間:2009-09-06 22:08:36 IP:61.217.xxx.xxx 未訂閱
的確在整合入現有的程式有點麻煩
我嘗試放在ApplicationEvent裡
或如您所述放在oncreate及onshow都不行

還要再想想
或試一下歸木淡的建議

===================引 用 P.D. 文 章===================
感謝 P兄提供的方式, 我按表操課, 試了一下, 確實可以改變 Application.MessageBox 的位置
但現在有一個問題是
這支程式有數十支MessageBox, caption 不盡相同, 這樣的做法, 我必須在每一個Application 前加上?PosMesage 的動作
我嘗試放在一啟動(oncreate)或 (onactive)上, 就沒有辦法調位置了, 是否能有下一次動作, 之後的Application.MessageBox都可以變成我想要的位置的方式?
編輯記錄
pedro 重新編輯於 2009-09-06 22:09:47, 註解 無‧
歸木淡
中階會員


發表:1
回覆:49
積分:75
註冊:2005-09-07

發送簡訊給我
#6 引用回覆 回覆 發表時間:2009-09-06 23:58:34 IP:99.137.xxx.xxx 訂閱
封裝好了, 在project中uses wcMessageboxEx;即可.
剛開始時處理的是HCBT_CREATEWND, 浪費了不少時間, 後來發現應該是HCBT_ACTIVATE
[code delphi]
unit wcMessageboxEx;

interface

uses
Windows, Forms;

procedure InstallHook;
procedure UninstallHook;

var
MessageBoxLeft: integer = 100;
MessageBoxTop: integer = 100;
AllowReposition: boolean = true;

implementation

var
hCBTHook: HHOOK = 0;

function GetWindowClassName(hwnd: THandle): string;
var
ps: array[0..254] of Char;
begin
GetClassName(hwnd, ps, 255);
result:=ps;
end;

function MessageBoxHookProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
if AllowReposition and
(Code = HCBT_ACTIVATE) and
(GetParent(wParam) = Application.Handle) and
(GetWindowClassName(wParam) = '#32770') then
SetWindowPos(wParam, HWND_TOP,
MessageBoxLeft, MessageBoxTop, 0, 0,
SWP_NOACTIVATE SWP_NOOWNERZORDER SWP_NOSIZE SWP_NOZORDER);
Result:=CallNextHookEx(hCBTHook, Code, wParam, lParam);
end;

procedure InstallHook;
begin
if hCBTHook = 0 then
hCBTHook := SetWindowsHookEx(WH_CBT, @MessageBoxHookProc,
Hinstance, GetCurrentThreadId());
end;

procedure UninstallHook;
begin
if hCBTHook <> 0 then
begin
UnHookWindowsHookEx(hCBTHook);
hCBTHook := 0;
end;
end;

initialization
InstallHook;
finalization
UninstallHook;
end.
[/code]
關鍵的兩行是
[code delphi]
(GetParent(wParam) = Application.Handle) and
(GetWindowClassName(wParam) = '#32770') then[/code]
這兩行, 沒有前者則所有的prgram的messagebox都會被改, 沒有一句則dialogs.Showmessage也會被改變.
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#7 引用回覆 回覆 發表時間:2009-09-07 03:26:40 IP:59.124.xxx.xxx 訂閱
這時候只能說可惜Delphi沒有macro?:P
PD前輩最近問的兩個問題,其實用macro還蠻快就解決的XD
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
RootKit
資深會員


發表:16
回覆:358
積分:419
註冊:2008-01-02

發送簡訊給我
#8 引用回覆 回覆 發表時間:2009-09-07 16:42:42 IP:61.222.xxx.xxx 訂閱
 HOOK 可能很受傷
也有可能誤改其他的對話視窗,像儲存開啟......

另一種至換函數作法,直接建立單元引用或放在MainForm

http://www.swissdelphicenter.com/torry/showcode.php?id=1422


[code delphi]
type
TSaveRedir = packed record
Addr: Pointer;
Bytes: array[0..4] of Byte;
end;
PSaveRedir = ^TSaveRedir;

procedure RedirectCall(FromAddr, ToAddr: Pointer; SaveRedir: PSaveRedir);
var
OldProtect: Cardinal;
NewCode: packed record
JMP: Byte;
Distance: Integer;
end;
begin
if not VirtualProtect(FromAddr, 5, PAGE_EXECUTE_READWRITE, OldProtect) then
RaiseLastWin32Error;
if Assigned(SaveRedir) then
begin
SaveRedir^.Addr := FromAddr;
Move(FromAddr^, SaveRedir^.Bytes, 5);
end;
NewCode.JMP := $E9;
NewCode.Distance := PChar(ToAddr) - PChar(FromAddr) - 5;
Move(NewCode, FromAddr^, 5);
if not VirtualProtect(FromAddr, 5, OldProtect, OldProtect) then
RaiseLastWin32Error;
end;

procedure UndoRedirectCall(const SaveRedir: TSaveRedir);
var
OldProtect: Cardinal;
begin
if not VirtualProtect(SaveRedir.Addr, 5, PAGE_EXECUTE_READWRITE, OldProtect) then
RaiseLastWin32Error;
Move(SaveRedir.Bytes, SaveRedir.Addr^, 5);
if not VirtualProtect(SaveRedir.Addr, 5, OldProtect, OldProtect) then
RaiseLastWin32Error;
end;


// Example: Replace Application.MessageBox with your own.

function MyNewMessageBox(Self: TApplication; const Text, Caption: PChar;
Flags: Longint): Integer;
begin
ShowMessage('New Messagebox');
//....
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Application.MessageBox('You'll never see this Text /
Diesen Text wirst du nie sehen', '...', MB_OK);
end;

var
S: TSaveRedir;

initialization
RedirectCall(@TApplication.MessageBox, @MyNewMessageBox, @S);

finalization
UndoRedirectCall(S);

[/code]

參考。

編輯記錄
RootKit 重新編輯於 2009-09-07 16:43:54, 註解 無‧
RootKit 重新編輯於 2009-09-07 16:44:43, 註解 無‧
RootKit 重新編輯於 2009-09-07 16:47:01, 註解 無‧
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#9 引用回覆 回覆 發表時間:2009-09-08 00:06:42 IP:61.219.xxx.xxx 訂閱
個人推rookit所講的這種方式。看到DELPHI裡有人寫出這樣低階的程式,真的感動!   忍不住想補充一下…當然直接抄就可以用了…

這樣的方式應該是最佳的,因為真的是有深入的內含在其中…

首先要了解的是在呼叫vcl函式時,比如說 foo ( ) ,它的組語是像
call 0x00044123 其中後面的位址就是 foo 的位址所在。但是…

0x00044123 這個地方放的卻不是foo裡的程式碼而是再跳到另一個真正的 foo 的程式碼,這種再跳的概念又稱 thunk。
那到底這個址方放的是啥東西… 就是一個5bytes的機器碼。以組語來說像是
JMP DWORD PTR [XXXXXXXX]
以機器言語來說就是 E9 XXXXXXXX 其中 XXXXXXXX這個數值表示真的放foo程式碼的地方。 E9這個值(又稱opcode)就是對應到組語的 jmp near rel32的格式。

我把概念圖放上來參考
thunk

了解thunk的原理後,大概可以知道那程式的寫法是如何的原理。
1/ VirtualProtect 這個api是用來解除 虛擬記憶體的寫入等權限,要打開才好在MEMORY中插入機器碼
2/ TSaveRedir的第一個成員就是為了要放thunk的位址,而後面的5bytes就是要放該thunk裡面的機器碼(即JMP DWORD PTR [XXXXXXXX] )
3/ 把原來的先存入TSaveRedir
4/ 找出舊的thunk位址與新的程式位址的差距
5/ 填入E9 位移 的資料在舊的thunk裡

於是…當你呼叫舊的時候候,會先到舊的thunk位址,然後該位置的內容是 E9 位移 (位移是新thunk與舊thunk的差),cpu讀到這裡就jump到新函式的真實程式所在位址

所以整個換的過程,就是讓舊thunk跳至自製程式段。

知道為何是5 了吧? 因為 jmp near 的opcode是 一個byte 即 0xE9 而後面的位移是32位元,所以是4個byte,共5byte


結論: 重點:別以為呼叫vcl function時,是直接到該function的位址執行程式碼。其實他是先跳到一個thunk上,然後再跳一次的!

注意: 經過我實做為bcb的程式過程,發現一般的function是不用thunk的… (難怪昨天我回憶以前好像是這樣)。以前我的自學得到的是加載dll時是用thunk的方式,然後找import table的資料。然而,bpl,它也是dll那類的動態函式庫,所以也用了thunk。


------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2009-09-08 00:16:42, 註解 無‧
aftcast 重新編輯於 2009-09-08 18:42:25, 註解 更正一般函式沒使用thunk‧
taishyang
站務副站長


發表:377
回覆:5490
積分:4563
註冊:2002-10-08

發送簡訊給我
#10 引用回覆 回覆 發表時間:2009-09-08 10:23:14 IP:122.116.xxx.xxx 訂閱
這篇是近幾個月來最精彩的一篇文章了
各個高手出手解題讓我忍不住設定為黃金文章!


P.S:不知有沒有前輩可以翻譯成BCB版本^^?
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#11 引用回覆 回覆 發表時間:2009-09-08 18:24:40 IP:210.64.xxx.xxx 訂閱
有個問題,我希望有人接手下去… 

我一時無法把method的pointer轉成一般的pointer。 delphi真的太方便了,一個@ 就搞定…

下面的程式必需把中斷設在application->messageboxa地方,然後移一下cursor看到它的位址,然後把值設進p裡,然重run一次!

ps 會看到我努力的想把method的指標做轉換的失敗過程 ^_^
[code cpp]
//---------------------------------------------------------------------------
#include
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

#pragma pack(push, 1)
typedef struct tagSaveRedir
{
void *thunk_addr;
BYTE machine_code[5];
}TSaveRedir;

typedef struct tagThunkCode
{
BYTE JMP;
int Displacement;
}TThunkCode;
#pragma pack(pop)

void RedirectCall(void *FromAddr, void *ToAddr, TSaveRedir &SaveRedir)
{
DWORD OldProtect;
TThunkCode NewCode;

if (!VirtualProtect(FromAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtect))
RaiseLastWin32Error;

SaveRedir.thunk_addr = FromAddr;
::memcpy(&SaveRedir.machine_code,FromAddr,5);


NewCode.JMP = 0xE9;
NewCode.Displacement = (char*)ToAddr - (char*)FromAddr - 5;
::memcpy(FromAddr,&NewCode,5);
if(!VirtualProtect(FromAddr, 5, OldProtect, &OldProtect))
RaiseLastWin32Error;
}
void UndoRedirectCall(TSaveRedir &SaveRedir)
{
DWORD OldProtect;
if (!VirtualProtect(SaveRedir.thunk_addr, 5, PAGE_EXECUTE_READWRITE, &OldProtect))
RaiseLastWin32Error;
::memcpy(SaveRedir.thunk_addr,SaveRedir.machine_code,5);

if (!VirtualProtect(SaveRedir.thunk_addr, 5, OldProtect, &OldProtect))
RaiseLastWin32Error;
}
// Example: Replace Application.MessageBox with your own.
int __fastcall MyNewMessageBox(TApplication *self, char const *Text, char const *Caption,long Flags)
{
ShowMessage("New Messagebox");
return 0;
//....
}

TSaveRedir S;
void *p = (void*)0x004025B0;
//typedef int (_fastcall TApplication::*MP)(const char *,const char *,int);
//
//void *t = (void*) ap->MethodAddress("MessageBoxA");

void start(void)
{
//MP p = TApplication::MessageBoxA;
//int pp = p;
RedirectCall(p,(void*)MyNewMessageBox,S);
//RedirectCall(p,(void*)MyNewMessageBox,S);
}
void unload(void)
{
UndoRedirectCall(S);
}

#pragma startup start
#pragma exit unload
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBoxA("OLD_ONE","OLD_CAP",MB_OK);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
RedirectCall(p,(void*)MyNewMessageBox,S);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
UndoRedirectCall(S);
}
//---------------------------------------------------------------------------

[/code]
------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2009-09-08 18:29:57, 註解 無‧
aftcast 重新編輯於 2009-09-08 18:34:32, 註解 無‧
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#12 引用回覆 回覆 發表時間:2009-09-08 22:06:05 IP:61.67.xxx.xxx 未訂閱
感謝各位大內高手出手相救, rootkit 兄的程式, 老實說小弟能力有限, 實無法看懂, 但我實做是確實是可以把程式中的 Application.MessageBox 給換掉, 不過這並不是我想要的, 因為我不是要換掉程式中的 MessageBox 的訊息, 而是要改變 MessageBox 的位置, 或許能力還不夠, 利用R兄的程式, 我無法改成我想要達到的目的, 
再者 淡兄的程式我也實作, 也的確可以操作, 同時也能改變 MessageBox 的位置, 而且直接 uses unit , 在其他的程式中也確實都不用改到任何程式碼, 就令 MessageBox 位置變到指定的位置了, 不過還有一個問題, 因為我這支程式(其實不是我寫的, 有source code), 其中的Application.MessageBox 很多, 我接手的客戶要求Application.MessageBox 的訊息必須出現在該Form呼叫的center,
例如, Form1 中呼叫 Application.MessageBox, 則這個 MessageBox就一定要出現在 Form1 的中間, 又如果 Form1 移動了位置, MessageBox 還是要出現在 新的Form1 的中間, 所以這個位置是隨時會變動的, 以淡兄的程式, 我還在嘗試要如何能以這個 UNIT 取得呼叫該 MessageBox 的 Form 是那一組, 及該 Form 的 width, height 及目前的 left, Top 位置, 這樣我才能算出其正中間的位置

我使用 P 兄的程式來改, 雖然每一個 FORM 上都要加上這段碼, 但至少是可以達到我想要的, 只是相對程式碼多了很多
以下是我改 P兄的程式碼, 有些非相關的我就省略掉了, 這只是其中一支 Pas, 其中還有近100支form 要改

ps. 以下程式碼中

[code delphi]
unit P_LoginIB;
interface
uses
Windows, SysUtils, Forms, IBCustomDataSet, IBQuery, IBDatabase, Db,
Delay, DBTables, xTable, DiskInfo, Controls, StdCtrls, Mask, Classes,
ini, CL_crypt32, DosMove, ExtCtrls, Graphics, Messages;

procedure ChangeMessageBoxPosition(var Msg: TMessage); message mbMessage;
public
{ Public declarations }
end;

var
Form_LoginIB: TForm_LoginIB;

implementation
uses DLLver, U_VarDef, U_Default, UT_SYS, P_Serial
{$R *.DFM}
PostMessage(Handle, mbMessage, 0, 0);
Application.MessageBox('兩次均輸入錯誤' #13 '系統結束',msgCaption,MB_OK); ==>這是原來設計的MessageBox
U_VarDef.isNormalClose:= False;
U_VarDef.stopRun:= True;
....
Form_LoginIB.Close;
end
[/code]

編輯記錄
P.D. 重新編輯於 2009-09-08 22:07:09, 註解 無‧
P.D. 重新編輯於 2009-09-08 22:10:32, 註解 無‧
P.D. 重新編輯於 2009-09-08 22:13:29, 註解 無‧
P.D. 重新編輯於 2009-09-08 22:15:13, 註解 無‧
歸木淡
中階會員


發表:1
回覆:49
積分:75
註冊:2005-09-07

發送簡訊給我
#13 引用回覆 回覆 發表時間:2009-09-08 23:00:25 IP:99.137.xxx.xxx 訂閱
>> 取得呼叫該 MessageBox 的 Form 是那一組 

是Screen.ActiveCustomForm
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#14 引用回覆 回覆 發表時間:2009-09-09 00:01:55 IP:122.120.xxx.xxx 訂閱
TO: taishyang  
最後我忍不住用硬幹的方法來把 method 指標轉一般指標。因為我們知道無論是哪一種,指標就是指標就是32位元數。型別是讓高階語言在compile時做匹配,必免錯誤。於是…我就用組語來處理囉,試了果然可以,參考看看。

此外,這篇本來是pd兄為了要解決messagebox的位置問題,卻有變稍偏了… 感覺用這方法似乎還要再多動一點手腳才可以答成他要的結果。我一時興奮都把問題的要求要點給忘了@@ 最後pd兄有解答就好… 我算是有點插花 ^_^

不過透過這樣的順便機會,也希望讓想要了解原理的人,可以借此增進功力了! (程式中有用一些少見的技巧,比如pragma的一些應用等…)


[code cpp]
#include
#pragma hdrstop
#include "HookFunctionMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
#pragma pack(push, 1)
typedef struct tagSaveRedir
{
void *thunk_addr;
BYTE machine_code[5];
}TSaveRedir;
typedef struct tagThunkCode
{
BYTE JMP;
int Displacement;
}TThunkCode;
#pragma pack(pop)
void RedirectCall(void *FromAddr, void *ToAddr, TSaveRedir &SaveRedir)
{
DWORD OldProtect;
TThunkCode NewCode;
if (!VirtualProtect(FromAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtect))
RaiseLastWin32Error;
SaveRedir.thunk_addr = FromAddr;
::memcpy(&SaveRedir.machine_code,FromAddr,5);

NewCode.JMP = 0xE9;
NewCode.Displacement = (char*)ToAddr - (char*)FromAddr - 5;
::memcpy(FromAddr,&NewCode,5);
if(!VirtualProtect(FromAddr, 5, OldProtect, &OldProtect))
RaiseLastWin32Error;
}
void UndoRedirectCall(TSaveRedir &SaveRedir)
{
DWORD OldProtect;
if (!VirtualProtect(SaveRedir.thunk_addr, 5, PAGE_EXECUTE_READWRITE, &OldProtect))
RaiseLastWin32Error;
::memcpy(SaveRedir.thunk_addr,SaveRedir.machine_code,5);

if (!VirtualProtect(SaveRedir.thunk_addr, 5, OldProtect, &OldProtect))
RaiseLastWin32Error;
}
int __fastcall MyNewMessageBox(TApplication *self, char const *Text, char const *Caption,long Flags)
{
ShowMessage("New Messagebox");
return 0;
//....
}

TSaveRedir S;
void *p;
typedef int (_fastcall TApplication::*MP)(const char *,const char *,int);
MP mp = TApplication::MessageBoxA;

void start(void)
{
asm {
push eax;
mov eax, DWORD ptr[mp];
mov p,eax;
pop eax;
}
RedirectCall(p,(void*)MyNewMessageBox,S);
}
void unload(void)
{
UndoRedirectCall(S);
}

#pragma startup start
#pragma exit unload
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBoxA("OLD_ONE","OLD_CAP",MB_OK);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
RedirectCall(p,(void*)MyNewMessageBox,S);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
UndoRedirectCall(S);
}
//---------------------------------------------------------------------------

[/code]

------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
taishyang
站務副站長


發表:377
回覆:5490
積分:4563
註冊:2002-10-08

發送簡訊給我
#15 引用回覆 回覆 發表時間:2009-09-09 01:00:22 IP:218.168.xxx.xxx 訂閱
To PD前輩:這似乎是站上的bug,我請yckuo前輩看看 ^^
To
aftcast前輩: 帥呆了~


===================引 用 P.D. 文 章===================
ps. 以下程式碼中

P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#16 引用回覆 回覆 發表時間:2009-09-09 02:16:47 IP:61.67.xxx.xxx 未訂閱


其實我想要做到的就是上面圖的部份
form1 call application.messagebox,
messagebox 要放在 form1 的正中間
form1 用 screen.activeform 可以取得 width, height
利用 clienttoscreen 可以取得 form1 目前的位置
但現在 messagebox 我無法得知其 width, height, 故無法計算出中間值
abs(Screen.ActiveForm.Width-w) div 2) // w 就是 messagebox 的width
但又因為每一組 MessageBox 都會因為 Message的內容不同會所有大小不同
不知要如何取得?
之前利用 PosMessage() 可以取得, 但以 淡兄的程式則應如何才能得到 MessageBox 的 Rect 值?
GrandRURU
站務副站長


發表:240
回覆:1680
積分:1874
註冊:2005-06-21

發送簡訊給我
#17 引用回覆 回覆 發表時間:2009-09-09 08:41:26 IP:203.75.xxx.xxx 未訂閱
aftcast前輩帥呆了+1
想不到居然連組語都出來了!

果然是個大難題呀……orz
純路過,樓上請繼續 XD
歸木淡
中階會員


發表:1
回覆:49
積分:75
註冊:2005-09-07

發送簡訊給我
#18 引用回覆 回覆 發表時間:2009-09-09 09:19:17 IP:99.137.xxx.xxx 訂閱
[code delphi]
unit wcMessageboxEx;

interface

uses
Windows, Forms;

procedure InstallHook;
procedure UninstallHook;

type
tMessageBoxPosition = (mbpDefault, mbpFixed, mbpCenterOfActiveform);

var
MessageBoxLeft: integer = 100;
MessageBoxTop: integer = 100;
MessageBoxPosition: tMessageBoxPosition = mbpCenterOfActiveform;

implementation

var
hCBTHook: HHOOK = 0;

function GetWindowClassName(hwnd: THandle): string;
var
ps: array[0..254] of Char;
begin
GetClassName(hwnd, ps, 255);
result:=ps;
end;

function MessageBoxHookProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
x, y: integer;
r: tRect;
begin
if (Code = HCBT_ACTIVATE) and
(MessageBoxPosition <> mbpDefault) and
(GetParent(wParam) = Application.Handle) and
(GetWindowClassName(wParam) = '#32770') then
begin
if (MessageBoxPosition = mbpCenterOfActiveform) and
Assigned(Screen.ActiveCustomForm) then
begin
GetWindowRect(wParam, r);
with Screen.ActiveCustomForm.BoundsRect do
begin
x := (Right Left - (r.Right-r.Left)) div 2;
y := (Bottom Top - (r.Bottom-r.Top)) div 2;
end;
SetWindowPos(wParam, HWND_TOP,
x, y, 0, 0,
SWP_NOACTIVATE SWP_NOOWNERZORDER SWP_NOSIZE SWP_NOZORDER);
end else
SetWindowPos(wParam, HWND_TOP,
MessageBoxLeft, MessageBoxTop, 0, 0,
SWP_NOACTIVATE SWP_NOOWNERZORDER SWP_NOSIZE SWP_NOZORDER);
end;
Result:=CallNextHookEx(hCBTHook, Code, wParam, lParam);
end;

procedure InstallHook;
begin
if hCBTHook = 0 then
hCBTHook := SetWindowsHookEx(WH_CBT, @MessageBoxHookProc,
Hinstance, GetCurrentThreadId());
end;

procedure UninstallHook;
begin
if hCBTHook <> 0 then
begin
UnHookWindowsHookEx(hCBTHook);
hCBTHook := 0;
end;
end;

initialization
InstallHook;
finalization
UninstallHook;
end.
[/code]
yckuo
高階會員


發表:55
回覆:389
積分:238
註冊:2003-03-07

發送簡訊給我
#19 引用回覆 回覆 發表時間:2009-09-09 12:54:30 IP:59.126.xxx.xxx 未訂閱
===================引 用 P.D. 文 章===================
ps. 以下程式碼中

嚴格來說那個並非 BUG, 而是套用程式碼區塊後他只解譯該選用格式的程式碼,而不會理會 HTML碼.
因此若要在程式碼中變色得靠 // 這符號, 後面加注說明, 這樣他會自行變色, 就像一般程式碼編輯器一樣.

所以請勿在程式碼中加入變色的 HTML 碼,改用正常程式寫法的加註說明.
------
yckuo
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#20 引用回覆 回覆 發表時間:2009-09-09 15:08:22 IP:210.64.xxx.xxx 訂閱
再另提一個解決的方法給pd兄參考。即把pedro兄與rootkit二人所提的方式合而為一。我以bcb實做,delphi太久太久沒使用了,也沒裝…

主要是在hook function的地方去實做 postmessage 與 再度呼叫 messagebox,當然,其實也可以只在裡面寫一行MessageDlgPos來取代,也可以在那裡寫n多行…看怎用。

bcb要達成delphi的同等功能常常要轉n個灣才可以,所以程式碼會比較多,也比較tricky。


[code cpp]
protected:
void __fastcall ChangeMessageBoxPosition(TMessage& msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_USER,TMessage,ChangeMessageBoxPosition)
END_MESSAGE_MAP(TForm)

[/code]



[code cpp]
TForm1 *Form1;

#pragma pack(push, 1)
typedef struct tagSaveRedir
{
void *thunk_addr;
BYTE machine_code[5];
}TSaveRedir;
typedef struct tagThunkCode
{
BYTE JMP;
int Displacement;
}TThunkCode;
#pragma pack(pop)
TSaveRedir S;
void *p;
void RedirectCall(void *FromAddr, void *ToAddr, TSaveRedir &SaveRedir)
{
DWORD OldProtect;
TThunkCode NewCode;
if (!VirtualProtect(FromAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtect))
RaiseLastWin32Error;
SaveRedir.thunk_addr = FromAddr;
::memcpy(&SaveRedir.machine_code,FromAddr,5);

NewCode.JMP = 0xE9;
NewCode.Displacement = (char*)ToAddr - (char*)FromAddr - 5;
::memcpy(FromAddr,&NewCode,5);
if(!VirtualProtect(FromAddr, 5, OldProtect, &OldProtect))
RaiseLastWin32Error;
}
void UndoRedirectCall(TSaveRedir &SaveRedir)
{
DWORD OldProtect;
if (!VirtualProtect(SaveRedir.thunk_addr, 5, PAGE_EXECUTE_READWRITE, &OldProtect))
RaiseLastWin32Error;
::memcpy(SaveRedir.thunk_addr,SaveRedir.machine_code,5);

if (!VirtualProtect(SaveRedir.thunk_addr, 5, OldProtect, &OldProtect))
RaiseLastWin32Error;
}
//---------------------------------------------------------------------------
int __fastcall MyNewMessageBox(TApplication *self, char const *Text, char const *Caption,long Flags)
{
UndoRedirectCall(S);
PostMessage(Form1->Handle, WM_USER, 100, 0);
Application->MessageBoxA(Text,Caption,Flags);
// ShowMessage("ShowMessage");
// MessageDlgPos(Text,mtCustom, TMsgDlgButtons()< RedirectCall(p,(void*)MyNewMessageBox,S);
return 0;
}

typedef int (_fastcall TApplication::*MP)(const char *,const char *,int);
MP mp = TApplication::MessageBoxA;

void start(void)
{
asm {
push eax;
mov eax, DWORD ptr[mp];
mov p,eax;
pop eax;
}
RedirectCall(p,(void*)MyNewMessageBox,S);
}
void unload(void)
{
UndoRedirectCall(S);
}

#pragma startup start
#pragma exit unload
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBoxA("二次輸入均錯誤","系統訊息",MB_OK);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ChangeMessageBoxPosition(TMessage& msg)
{
HANDLE MbHwnd;
RECT MbRect;
int x, y, w, h;
int mx =50, my=50;
MbHwnd = FindWindow(MAKEINTRESOURCE(WC_DIALOG), "系統訊息");
if (MbHwnd)
{
GetWindowRect(MbHwnd, &MbRect);
w = MbRect.right - MbRect.left;
h = MbRect.bottom - MbRect.top;
// center horzontal
x = mx;
// keep on screen
if (x < 0)
x = 0;
else if (x w > Screen->Width)
x = Screen->Width - w;
//center vertical
y = my;
// keep on screen
if (y < 0)
y = 0;
else if (y h > Screen->Height)
y = Screen->Height - h;
// set new windows position
SetWindowPos(MbHwnd, 0, x, y, 0, 0, SWP_NOACTIVATE
| SWP_NOSIZE | SWP_NOZORDER);
}
}


[/code]


此外,我覺得歸木淡兄寫的也非常的好,而且它用的是thread hooks,而非全系統的資源,效能不會有什麼大問題。選哪一種都很不錯,最重要的是大家都因此而學到不少額外的東西!
------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2009-09-09 15:21:12, 註解 無‧
RootKit
資深會員


發表:16
回覆:358
積分:419
註冊:2008-01-02

發送簡訊給我
#21 引用回覆 回覆 發表時間:2009-09-09 15:19:07 IP:61.222.xxx.xxx 訂閱
aftcast 兄,實在太棒了。底子深厚。

補充一下:

事實上,當然使用 Hook 會比較方便一點避免二次取得 Dialog Handle
不過,若我會不放心使用。畢竟一個如果比較複雜的專案,沒來一通都改實在有點風險。

另外 在MyNewMessageBox 中可以依據 Flags 用 MessageDlgPos內容處理或二次取得Handle
是比較精確的作法。當然見仁見智了。...


P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#22 引用回覆 回覆 發表時間:2009-09-09 17:33:07 IP:61.67.xxx.xxx 未訂閱
很感謝淡兄提供的程式, 雖然裡面很多我還是無法看懂的地方, 不過現在先應急把程式照抄上去, 果然確實完全達成我想要的效果, 但是在此也感謝P兄首帖回覆的方式, 也是可以達到需求, 還有其他回帖的前輩們, 一併致謝了, 基於答題的精神, 我還是把得分給淡兄了, 其他前輩抱歉啦!
===================引 用 歸木淡 文 章===================
[code delphi]
unit wcMessageboxEx;

interface

uses
? Windows, Forms;

procedure InstallHook;
procedure UninstallHook;

type
? tMessageBoxPosition = (mbpDefault, mbpFixed, mbpCenterOfActiveform);

var
? MessageBoxLeft: integer = 100;
? MessageBoxTop: integer = 100;
? MessageBoxPosition: tMessageBoxPosition = mbpCenterOfActiveform;

implementation

var
? hCBTHook: HHOOK = 0;

function GetWindowClassName(hwnd: THandle): string;
var
? ps: array[0..254] of Char;
begin
? GetClassName(hwnd, ps, 255);
? result:=ps;
end;

function MessageBoxHookProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
? x, y: integer;
? r: tRect;
begin
? if (Code = HCBT_ACTIVATE) and
? ? ?(MessageBoxPosition <> mbpDefault) and
? ? ?(GetParent(wParam) = Application.Handle) and
? ? ?(GetWindowClassName(wParam) = '#32770') then
? begin
? ? if (MessageBoxPosition = mbpCenterOfActiveform) and
? ? ? ?Assigned(Screen.ActiveCustomForm) then
? ? begin
? ? ? GetWindowRect(wParam, r);
? ? ? with Screen.ActiveCustomForm.BoundsRect do
? ? ? begin
? ? ? ? x := (Right Left - (r.Right-r.Left)) div 2;
? ? ? ? y := (Bottom Top - (r.Bottom-r.Top)) div 2;
? ? ? end;
? ? ? SetWindowPos(wParam, HWND_TOP,
? ? ? ? x, y, 0, 0,
? ? ? ? SWP_NOACTIVATE SWP_NOOWNERZORDER SWP_NOSIZE SWP_NOZORDER);
? ? end else
? ? ? SetWindowPos(wParam, HWND_TOP,
? ? ? ? MessageBoxLeft, MessageBoxTop, 0, 0,
? ? ? ? SWP_NOACTIVATE SWP_NOOWNERZORDER SWP_NOSIZE SWP_NOZORDER);
? end;
? Result:=CallNextHookEx(hCBTHook, Code, wParam, lParam);
end;

procedure InstallHook;
begin
? if hCBTHook = 0 then
? ? hCBTHook := SetWindowsHookEx(WH_CBT, @MessageBoxHookProc,
? ? ? ? ? Hinstance, GetCurrentThreadId());
end;

procedure UninstallHook;
begin
? if hCBTHook <> 0 then
? begin
? ? UnHookWindowsHookEx(hCBTHook);
? ? hCBTHook := 0;
? end;
end;

initialization
? InstallHook;
finalization
? UninstallHook;
end.
[/code]
pedro
尊榮會員


發表:152
回覆:1187
積分:892
註冊:2002-06-12

發送簡訊給我
#23 引用回覆 回覆 發表時間:2009-09-09 21:32:12 IP:59.112.xxx.xxx 未訂閱
原本看標題以為問題很簡單,看似簡單的問題,也是這麼的難哩 
到後來看歸木淡、aftcast、RootKit們回應,使出本領,越來越深入
才知道已經超出我能力範圍太多太多了



===================引 用 P.D. 文 章===================
很感謝淡兄提供的程式, 雖然裡面很多我還是無法看懂的地方, 不過現在先應急把程式照抄上去, 果然確實完全達成我想要的效果, 但是在此也感謝P兄首帖回覆的方式, 也是可以達到需求, 還有其他回帖的前輩們, 一併致謝了, 基於答題的精神, 我還是把得分給淡兄了, 其他前輩抱歉啦!

系統時間:2024-03-29 18:13:22
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!