Cześc. Wiecie, że rzadko zadaje pytania, ale chciałem się Was poradzić. Czy w ogóle takie coś jak temacie jest możłiwe? Oczywiście w Delphi albo i w innym dowolnym jęzku programowania. Googlowałem, ale nie znalazłem nic konkretnego. Kombinowałem na różne sposoby. Mam wprawdzie już dawno napisany prosty program działąjący na zasadzie Hooka na środkowy przycisk myszki, po którego naciśnięciu pojawia się moje menu tylko na standardowych kontrolkach edycyjnych (o nazwie klasy zawierającej słowo Edit
), które pozwala na szybkie zmienienie wielkości liter. Jednak pomyślałem, że fajniej było by dopisać moje pozycje do tego menu od standardowych kontrolek edycyjnych (te które zawiera pozycje Kopiuj
, Wklej
, Wytnij
i inne). Niestety z tego co widziałem chociaż po źródle pluginów do WinAMP'a oraz informacjami z MSDN'a to menu mozna modyfikować można przed jego pokazaniem tylko przy obsłudze komunikatu WM_INITMENUPOPUP, bo wtedy parametr WParam zawiera uchwyt do pokazywanego Menu. Niestety taki kod zarówno dla Edit1.Handle
jak i Form1.Handle
nie pokazał mi MessageBoxa:
var
POldEditProc : Pointer;
function NewEditProc(WindowHandle : hWnd; TheMessage : Longint; ParamW : Longint;
ParamL : Longint) : Longint stdcall;
begin
if TheMessage = WM_INITMENUPOPUP then
begin
MessageBox(Application.Handle, 'WM_INITMENUPOPUP', 'Test', MB_OK);
end;
Result := CallWindowProc(POldEditProc, WindowHandle, TheMessage, ParamW, ParamL);
end;
procedure TForm1.FormCreate(Sender : TObject);
begin
POldEditProc := Pointer(SetWindowLong(Edit1.Handle, GWL_WNDPROC, Longint(@NewEditProc)));
end;
Nie działa też taki kod:
const
MENU_ID = 3999;
MenuItemText = 'Nowa pozycja - test' + #9 + 'Ctrl + Q';
var
HM : HMENU;
MyMenuItemInfo : MENUITEMINFO;
begin
HM := GetMenu(Edit1.Handle);
MessageBox(Application.Handle, PChar(IntToStr(HM)), 'Test', MB_OK);
MyMenuItemInfo.cbSize := SizeOf(MENUITEMINFO);
MyMenuItemInfo.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA;
MyMenuItemInfo.wID := MENU_ID - 1;
MyMenuItemInfo.fType := MFT_SEPARATOR;
InsertMenuItem(HM, 127, False, MyMenuItemInfo);
MyMenuItemInfo.wID := MENU_ID;
MyMenuItemInfo.fType := MFT_STRING;
MyMenuItemInfo.dwTypeData := MenuItemText;
MyMenuItemInfo.cch := SizeOf(MenuItemText);
InsertMenuItem(HM, 128, True, MyMenuItemInfo);
end;
Nawet moja ulubiona metoda (w przypadkach gdzie jest pomocna, jak podpięcie się pod funckję obsługi komunikatów innej aplikacji) wstrzyknięcia w obcy proces dllki (dla testów robiłem to na Total Commanderze i polu edycyjnym do podawania poleceń) też nic mi nie dała. Ponieważ również tam WM_INITMENUPOPUP nie zachodziło, a co ciekawe uchwyt tam ustalany w taki sposób jak poniżej zwraca zawsze 1001 i chyba dla innego komunikatu niż WM_INITMENUPOPUP nie będzie on prawidłowy. W zmiennej globalnej EditControlH jest zawarty pobrany uchyt kontrolki edycyjnej na której wcześniej kilknąłem środkowym przyciskiem myszki co przechwytuje globalny Hook. Jak widać zapisuje sobie uchwyt w pamięci jako string korzystając z funkcji CreateFileMapping
i MapViewOfFile
, a później odczytując. Co realizują moje własne uproszczone funkcje. Przykłady kodów poniżej.
Injekcja przy użyciu modułu: afxcodehook.pas
var
DestPid : DWORD;
ResPtr : Pointer;
HGlobal, HResInfo : THandle;
begin
// SetForegroundWindow(App_Handle);
// ShowPopupMenu(ArgDisableCaseMI);
HResInfo := FindResource(HInstance, Resource_Name, RT_RCDATA);
if HResInfo > 0 then
begin
HGlobal := LoadResource(HInstance, HResInfo);
ResPtr := PChar(LockResource(HGlobal));
GetWindowThreadProcessId(EditControlH, DestPid);
WriteTextAsFileMapping(FileMapping_Name, IntToStr(EditControlH));
InjectLibrary(OpenProcess(PROCESS_ALL_ACCESS, False, DestPid), ResPtr);
end;
end;
Fragment zawartości wstrzykiwanej dllki - oczywiście w WinAPI:
//...
function GetMenuStr(MenuHandle : HMENU; ItemID : UINT) : string;
var
Buf : array[0..255] of Char;
begin
GetMenuString(MenuHandle, ItemID, Buf, SizeOf(Buf), MF_BYCOMMAND);
Result := Buf;
end;
function EditFieldCapturedWinFunc(AHWnd : HWND; UMsg : UINT; AWParam : WParam; ALParam : LParam) : integer; stdcall;
const
MENU_ID = 3999;
AFileName = 'D:\log.txt';
MenuItemText = 'Nowa pozycja - test' + #9 + 'Ctrl + Q';
var
S : string;
HM : HMENU;
I : integer;
MyMenuItemInfo : MENUITEMINFO;
begin
if UMsg <> WM_PAINT then
begin
ReadTextFromFile(AFileName, S);
if S = '' then
begin
S := WinMsgToText(UMsg);
end
else
begin
S := S + #13#10 + WinMsgToText(UMsg);
end;
SaveTextToFile(AFileName, S);
end;
if UMsg = WM_CONTEXTMENU then
begin
S := '';
HM := GetMenu(AHWnd);
for I := 0 to High(WORD) do
begin
if S = '' then
begin
S := GetMenuStr(HM, I);
end
else
begin
S := S + #13#10 + GetMenuStr(HM, I);
end;
end;
SaveTextToFile('D:\menu_test.txt', S);
end;
if UMsg = WM_CONTEXTMENU then
begin
HM := GeTMenu(AHWnd);
MyMenuItemInfo.cbSize := SizeOf(MENUITEMINFO);
MyMenuItemInfo.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA;
MyMenuItemInfo.wID := MENU_ID - 1;
MyMenuItemInfo.fType := MFT_SEPARATOR;
InsertMenuItem(HM, 127, False, MyMenuItemInfo);
MyMenuItemInfo.wID := MENU_ID;
MyMenuItemInfo.fType := MFT_STRING;
MyMenuItemInfo.dwTypeData := MenuItemText;
MyMenuItemInfo.cch := SizeOf(MenuItemText);
InsertMenuItem(HM, 128, True, MyMenuItemInfo);
end;
Result := CallWindowProc(POrigEditFieldWinFunc, AHWnd, uMsg, AWParam, ALParam);
end;
procedure Apply_New_EditField_Window_Proc;
var
S : string;
begin
ReadTextFromFileMapping(FileMapping_Name, S);
MainEditH := StrToInt(S);
MessageBox(0, PChar(FormatC('%X', MainEditH)), 'Edit Handle (hex)', MB_ICONINFORMATION + MB_OK);
POrigEditFieldWinFunc := Pointer(GetWindowLong(MainEditH, GWL_WNDPROC));
POwnEditFieldWinFunc := @EditFieldCapturedWinFunc;
SetWindowLong(MainEditH, GWL_WNDPROC, Integer(POwnEditFieldWinFunc));
end;
function HiddenDlgProc(AHWnd : HWND; Msg : UINT; wParam : wParam; lParam : lParam) : BOOL; stdcall;
begin
Result := False;
HiddenDialogHandle := AHWnd;
case Msg of
WM_INITDIALOG :
begin
Apply_New_EditField_Window_Proc;
end;
WM_CLOSE :
begin
CloseHandle(HMutex);
PostQuitMessage(0);
end;
end;
end;
//...
Plik D:\menu_test.txt
jest niestety pusty, także raczej nie pobieram prawidłowego HMENU. Proszę o wszelkie wskazówki. Czy jedynym rozwiązaniem jest do swojego menu tworzonego oryginalnym programem z Hookiem, dodać pozycje do kopiowania, wklejania i wycinania i nimi te zadania obsługiwać. Bo wiadomo że funckja GetSysteMenu pozwala pobrac uchwyt Menu aplikacji i go zmodyfikować, na co są przykłady w sieci także pod Delphi, ale jak z polem edycyjnym i jego menu pod prawym przyciskiem myszki. Proszę o wszelkie porady w tym temacie jeżeli coś wiecie. I przepraszam za rozpisanie się, ale chciałem pokazać oraz opisać metody, które wypróbwałem zanim napisałem tego posta.