Select an item in combobox of another application - delphi-xe2

I am making Delphi application to control other application. I need to select item in ComboBox of other application one by one and get its text. I have used FindWindowEx to find that ComboBox. Now I can find ComboBox but I don’t know how to select item in ComboBox.
var
ParentWindow: HWND;
ChildWindow: HWND;
begin
ParentWindow := FindWindow('TfrmMain', nil);
if ParentWindow <> 0 then
begin
ChildWindow := FindWindowEx(ParentWindow, 0, 'TPageControl', nil);
ChildWindow := FindWindowEx(ChildWindow, 0, 'TTabSheet', nil);
ChildWindow := FindWindowEx(ChildWindow, 0, 'TfrmInnerView', nil);
ChildWindow := FindWindowEx(ChildWindow, 0, 'TPanel', nil);
ChildWindow := FindWindowEx(ChildWindow, 0, 'TComboBox', nil);
end;
end;
I wish someone tell me how to do it.

In case child window of TfrmInnerView have only one class named TPanel, the code below is working properly. But if your child window of TfrmInnerView has more than one class which has same name TPanel, code below can find only the 1st class TPanel. I am now looking for the ways to find 2nd class. I would appreciate for any idea to do it.
SendMessage(ComboHandle, CB_SETCURSEL, NewSelectionIndex, 0);
Use: SendMessage(ChildWindow, CB_SETCURSEL, 1, 0);
I got this code from [What windows messages are used by Delphi to notice changes in a combo box?

Related

core text displayed text is drawn without spacing

I'm trying to display text using coretext. Now it's on a delphi form, but I have code which seems to work fine getting the context for drawing shapes on (it just gets the FContext field from the form).
The text appears like this (it's upside down), it's suppose to be 'at'
Now here's the code, just put it in a formpaint
var
cgcontext : CGContextRef;
path : CGMutablePathRef;
framesetter : CTFramesetterRef;
frame : CTFrameRef;
attrString: CFAttributedStringRef;
function GetCGContextFromCanvas(ACanvas: TCanvas): CGContextRef;
var
Context: TRttiContext;
Field: TRttiField;
begin
Field := Context.GetType(ACanvas.ClassType).GetField('FContext');
Assert(Field <> nil);
Result := PPointer(Field.GetValue(ACanvas).GetReferenceToRawData)^;
end;
begin
cgcontext := GetCGContextFromCanvas(Canvas);
path := CGPathCreateMutable(); //1
CGPathAddRect(path, nil, CGRectMake(0,0,600,200));
attrString := CFAttributedStringCreate(kCFAllocatorDefault, CFSTR('at'), nil);
framesetter := CTFramesetterCreateWithAttributedString(attrString); //3
frame := CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 2), path, nil);
CTFrameDraw(frame, cgcontext); //4
end;
Any suggestions on why the spacing is wrong? Shouldn't it use the default spacing?
..it's because the textmatrix is set somewhere, but because it's not restored when you do CGContextSaveGState/CGContextRestoreGState the textmatrix is wrong. So just add a CGContextSetTextMatrix(cgcontext, CGAffineTransformIdentity);

Removing NotifyIcon from the notification area

Is it possible to remove NotifyIcon from the notification area (system tray) when an app terminates abruptly?
if no, how can I remove it when the app runs for the next time?
Abruptly? No. Your program has ceased to exist, so there's no opportunity to run any code to tell the shell that it should remove the icon.
To remove the icon, move your mouse over it. The shell will try to notify your program, realize there's nothing there anymore, and remove the icon by itself.
On Windows 7 and later, notify icons can be identified by a user-defined GUID. On earlier versions, they are identified by a combination of HWND and ID number instead. Since your app is not guaranteed to get the same HWND value the next time it runs, the only way you can do anything to an old icon that is identified by HWND is if you remembered the previous HWND value so you can use it to remove the old icon, before then using a new HWND to add a new icon. But with a GUID-identified icon, the GUID needs to be persistent (as it is stored in the Registry to store app settings associated with the icon), so you should be able to simply keep updating the existing icon as needed, or remove it if desired.
FWIW, since code doesn't exist so far, I thought I'd throw this in. I don't know if it will help or not for the OP, but it should be good guidance in the right direction.
unit csystray;
{ removes dead system tray icons, by Glenn1234 # stackoverflow.com
since this uses "less than supported by Microsoft" means, it may
not work on all operating system. It was tested on Windows XP }
interface
uses commCtrl, shellapi, windows;
type
TTrayInfo = packed record
hWnd: HWnd;
uID: UINT;
uCallBackMessage: UINT;
Reserved1: array[0..1] of longint;
Reserved2: array[0..2] of longint;
hIcon: HICON;
end;
PTBButton = ^TTBButton;
_TBBUTTON = packed record
iBitmap: Integer;
idCommand: Integer;
fsState: Byte;
fsStyle: Byte;
bReserved: array[1..2] of Byte;
dwData: Longint;
iString: Integer;
end;
TTBButton = _TBBUTTON;
procedure RemoveStaleTrayIcons;
implementation
procedure RemoveStaleTrayIcons;
const
VMFLAGS = PROCESS_VM_OPERATION or PROCESS_VM_READ OR PROCESS_VM_WRITE;
var
ProcessID: THandle;
ProcessHandle: THandle;
trayhandle: HWnd;
ExplorerButtonInfo: Pointer;
i: integer;
ButtonCount: Longint;
BytesRead: Longint;
ButtonInfo: TTBButton;
TrayInfo: TTrayInfo;
ClassNameA: Array[0..255] of char;
outlen: integer;
TrayIconData: TNotifyIconData;
begin
// walk down the window hierarchy to find the notification area window
trayhandle := FindWindow('Shell_TrayWnd', '');
trayhandle := FindWindowEx(trayhandle, 0, 'TrayNotifyWnd', nil);
trayhandle := FindWindowEx(trayhandle, 0, 'SysPager', nil);
trayhandle := FindWindowEx(trayhandle, 0, 'ToolbarWindow32', nil);
if trayhandle = 0 then exit;
// find the notification area process and open it up for reading.
GetWindowThreadProcessId(trayhandle, #ProcessID);
ProcessHandle := OpenProcess(VMFLAGS, false, ProcessID);
ExplorerButtonInfo := VirtualAllocEx(ProcessHandle, nil, Sizeof(TTBButton),
MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
// the notification area is a tool bar. Get the number of buttons.
ButtonCount := SendMessage(trayhandle, TB_BUTTONCOUNT, 0, 0);
if ExplorerButtonInfo <> nil then
try
// iterate the buttons & check.
for i := (ButtonCount - 1) downto 0 do
begin
// get button information.
SendMessage(trayhandle, TB_GETBUTTON, i, LParam(ExplorerButtonInfo));
ReadProcessMemory(ProcessHandle, ExplorerButtonInfo, #ButtonInfo,
Sizeof(TTBButton), BytesRead);
// if there's tray data, read and process
if Buttoninfo.dwData <> 0 then
begin
ReadProcessMemory(ProcessHandle, PChar(ButtonInfo.dwData),
#TrayInfo, Sizeof(TTrayInfo), BytesRead);
// here's the validation test, this fails if the master window is invalid
outlen := GetClassName(TrayInfo.hWnd, ClassNameA, 256);
if outlen < 1 then
begin
// duplicate the shell icon removal, i.e. my component's DeleteTray
TrayIconData.cbSize := sizeof(TrayIconData);
TrayIconData.Wnd := TrayInfo.hWnd;
TrayiconData.uID := TrayInfo.uID;
TrayIconData.uCallbackMessage := TrayInfo.uCallBackMessage;
Shell_NotifyIcon(NIM_DELETE, #TrayIconData);
end;
end;
end;
finally
VirtualFreeEx(ProcessID, ExplorerButtonInfo, Sizeof(TTBButton), MEM_RELEASE);
end;
end;
end.

Why does the taskbar menu appear when right-clicking my notification icon?

I've read about how to make a notification popup menu go away when clicking outside it, but when I use the suggested code, a right click causes the taskbar popup menu to appear as well as my own. How do I make only my menu appear? Here is my code:
SetForegroundWindow(Form1.Handle);
PopupMenu1.Popup(x, y);
PostMessage(Form1.Handle, WM_NULL, 0, 0);
Edit: Okay, it seems like I did need to do something related to what I commented below.
trayhandle := FindWindow('Shell_TrayWnd', '');
trayhandle := FindWindowEx(trayhandle, 0, 'TrayNotifyWnd', nil);
trayhandle := FindWindowEx(trayhandle, 0, 'SysPager', nil);
trayhandle := FindWindowEx(trayhandle, 0, 'ToolbarWindow32', nil);
PostMessage(trayHandle, WM_LBUTTONDOWN, MK_LBUTTON, 0);
PostMessage(trayHandle, WM_LBUTTONUP, MK_LBUTTON, 0);
SetForegroundWindow(Handle);
PopupMenu1.Popup(x, y);
PostMessage(Handle, WM_NULL, 0, 0);
I've done other work and kept bouncing back to this app and the menu (and only my menu) displays properly and disappears properly as well. Seems to be working fine now (besides triggering the left mouse button)?

TRichEdit and URL highlighting problems

I am using the current code to highlight URLs on a TRichEdit:
procedure TForm1.WndProc(var Message: TMessage);
var
p: TENLink;
strURL: string;
begin
if (Message.Msg = WM_NOTIFY) then
begin
if (PNMHDR(Message.lParam).code = EN_LINK) then
begin
p := TENLink(Pointer(TWMNotify(Message).NMHdr)^);
if (p.Msg = WM_LBUTTONDOWN) then
begin
SendMessage(RichEdit1.Handle, EM_EXSETSEL, 0, Longint(#(p.chrg)));
strURL := RichEdit1.SelText;
ShellExecute(Handle, 'open', PChar(strURL), 0, 0, SW_SHOWNORMAL);
end
end;
end;
inherited;
end;
procedure TForm1.InitRichEditURLDetection;
var
mask: Word;
begin
mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0, mask or ENM_LINK);
SendMessage(RichEdit1.Handle, EM_AUTOURLDETECT, Integer(True), 0);
form1.RichEdit1.OnChange := form1.RichEdit1Change;
end;
It highlights the URLs, however it prevent my RichEdit1.OnChange from being called. I trying setting again from within WndProc and other approaches but nothing works. The minute I enable the URL highlighter (by calling InitRichEditURLDetection on FormCreate) OnChange stops working.
This is on Delphi 7.
Any suggestions?
thanks!
There is a bug in your code. Replace
mask := SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
with
mask := SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0);
Because of this bug, mask will not contain the default event bits of the Rich Edit control, so the Rich Edit control looses these event flags when you EM_SETEVENTMASK; in particular, it will lack the ENM_CHANGE bit.
Update
Sertac Akyuz found yet another show-stopping bug: mask needs to be an integer (which indeed is the result type of SendMessage).

Why does a simulated mouse click (using mouse_event) work on selected components only?

I have multiple cursors (which are actually forms) that can be controlled by their respective mouse. (1 cursor for 1 user).
I use SetCursorPos to position the default cursor (the original system cursor) in a position that will not take away the focus from my application, and use ShowCursor(false) to hide it.
I have a class that gets the handle of the mouse and the coordinates.
When the user clicks I use the SetCursorPos and the mouse_event to simulate the clicks in that particular position.
My simulated mouse clicks only work on certain components' OnClick event (It was supposed to be only buttons and labels, but I experimented with the stuff on my project just to know what will or won't work):
It works on:
Buttons (TButton, TBitBtn, TAdvSmoothButton)
TAdvGrid
TMenuItem (but the direct child of the TMainMenu only)
TRadioButton
It doesn't work on:
TLabel
Panels (TPanel, TAdvSmoothPanel)
TCoolBar
TMenuItem (not direct child of TMainMenu)
This is my code:
SetCursorPos(currentX , currentY);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Why doesn't it work on some components? Is there a workaround (because I would like to be able to click labels using mouse_event).
EDIT:
I tried checking if the clicking function was really called, so I put ShowMessage('clicked'); before the SetCursorPos and mouse_event...but strangely everything (minor edit: everything except MenuItems) works fine now (except for the fact that I have a Message popping out everytime I try to click something). Does anybody have an idea why this behaves that way?
mouse_event is actually deprecated, you should use SendInput instead, see if that fixes anything (I would also suggest making the mouse move an input message, over using SetCursorPos), also, if your doing this for a specific application, PostMessage might be a much better and simpler alternative
Seems to work here;
procedure TForm1.Panel1Click(Sender: TObject);
begin
ShowMessage('Click');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Pt: TPoint;
begin
Pt := Panel1.ClientToScreen(Point(0, 0));
SetCursorPos(Pt.x, Pt.y);
// SetCursorPos(Panel1.ClientOrigin.x, Panel1.ClientOrigin.y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
end;
or, without SetCursorPos;
procedure TForm1.Button1Click(Sender: TObject);
var
Pt: TPoint;
begin
Pt := Panel1.ClientToScreen(Point(0, 0));
Pt.x := Round(((Pt.x + 1) * 65535) / Screen.Width);
Pt.y := Round(((Pt.y + 1) * 65535) / Screen.Height);
mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_MOVE or MOUSEEVENTF_LEFTDOWN,
Pt.x, Pt.y, 0, 0);
mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_MOVE or MOUSEEVENTF_LEFTUP,
Pt.x, Pt.y, 0, 0);
end;
It works by accident right now, those components have probably captured the mouse. You need to pass the mouse pointer coordinates in the 2nd and 3rd arguments. Thus:
//SetCursorPos(currentX , currentY);
mouse_event(MOUSEEVENTF_LEFTDOWN, currentX, currentY, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, currentX, currentY, 0, 0);

Resources