SendMessage to Firefox, on cursor position - delphi

The code below sometimes works: it inserts text from tEdit, but only in "Notepad", "Word", "ICQ". Such software like Firefox or Google Chrome doesn't work with it.
What should I do?
var
Pos: TPoint;
Target: HWND;
...
if not GetCursorPos(Pos) then
RaiseLastOSError;
Target := WindowFromPoint(Pos);
if Target<>0 then
SendMessage(Target, EM_REPLACESEL, ord(True), LPARAM(PChar(Edit1.Text)));
That's it! I have found the code i needed
procedure SendText(ds:string);
var
TI: TInput;
KI: TKeybdInput;
i: integer;
begin
TI.Itype := INPUT_KEYBOARD;
for i := 1 to Length(ds) do
begin
KI.wVk := Ord(UpCase(ds[i]));
KI.dwFlags := 0;
TI.ki := KI;
SendInput(1, TI, SizeOf(TI));
KI.dwFlags := KEYEVENTF_KEYUP;
TI.ki := KI;
SendInput(1, TI, SizeOf(TI));
end;
end;
But the problem is - i can't copy russian(cyrilic) symbols using SendInpit(Edit1.Text); Any suggestions?

It doesn't work in Firefox and Chrome because the edit boxes you see in them are rendered by the HTML engines in the browser and not by the operating system. They are called "windowless controls", and thus do not have a window handle associated with them.
As far as the operating system is concerned, the webpage is one big HWND with a webpage painted inside it, and some of the painted elements just happen to look and act like controls thanks to the HTML engine.
You cannot target such controls with SendMessage(). Depending on exactly what you plan to do, there may be another, more direct way to automate the browser. But using SendMessage() is definitely not the way to go.

AFAIR, Firefox editboxes aren't really Windows native editboxes but something different.
I can be wrong, but you cannot treat those as normal editboxes. You need to get their window
handle (well, if they have an window handle) and send the message to that.
And I'm talking about editboxes of Firefox (address bar and search bar) itself not the ones
rendered out of HTML.
There are utilities on Windows Platform SDK (download from Microsoft) that can help you identify
the correct target for your SendMessage calls.

You can do this with MSAA. Here is an example: http://www.transl-gunsmoker.ru/2009/08/blog-post.html And in WinSDK there is an analog of WinSpy for MSAA which is called AccExplorer32.

Related

How to use Delphi standard confirmation dialog but with checkbox "Don't ask me again"?

In many confirmation dialogs it is usefull to have such option (quick wayt to disable confirmation).
But i can't find how to do that. I don't want to design it myself because i need this dialog to be standard-like and don't wont to redesign with every update of Delphi. Is there simple way to use Delphi standard confirmation dialog with such checkbox ?
UPDATE2. Suggested SynTaskDialog library from Synopse project does great job (all i need and even more), i will use it in my projects. Thanks!
UPDATE. So, thank you guys for ideas. System function MessageBoxCheck is nice solution but seem to be not so stable as it should be. In general i agree that it is good idea to use latest API functions to provide users with best UI experience of modern os and use old-fashioned design for older systems. At moment i stay on simple solution (code is following), but if someone share the code with support of UI for modern OS, it will be nice.
function MsgDlgWithCB(const Msg,Title,CBMsg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn;
var cbDontAskAnymore: TCheckBox): TForm;
var
i: integer;
b: TButton;
y: integer;
begin
Result := CreateMessageDialog(Msg, DlgType, Buttons, DefaultButton) ;
Result.Position := poScreenCenter;
cbDontAskAnymore := TCheckBox.Create(Result);
cbDontAskAnymore.Caption := CBMsg;
cbDontAskAnymore.Width := 130;
y := -1;
for i := 0 to result.ComponentCount-1 do
if result.Components[i] is TButton then
begin
b := TButton(result.Components[i]);
b.Left := b.Left + cbDontAskAnymore.Width + 16;
Result.ClientWidth := Max(Result.ClientWidth, b.Left+b.Width+16);
y := b.Top+b.Height-cbDontAskAnymore.Height;
end;
if y<0 then
y := Result.ClientHeight - cbDontAskAnymore.height - 16;
Result.Caption := Title;
cbDontAskAnymore.Parent := Result;
cbDontAskAnymore.Top := y;
cbDontAskAnymore.Left := 8;
end;
function MessageDlgCheckbox(const Msg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn;
var cbDontAskAnymore: Boolean;
const Title: string ='Confirmation';
const CBMsg: string = 'Don''t ask anymore'): integer;
var
f: TForm;
c: TCheckbox;
begin
f := MsgDlgWithCB(Msg,Title,CBMsg,DlgType,Buttons,DefaultButton,c);
try
result := f.ShowModal;
cbDontAskAnymore := c.Checked;
finally
f.free;
end;
end;
You can use our Open Source SynTaskDialog unit.
Windows provides a generic task dialog available since Vista/Seven. But there is none available with previous versions of Windows, i.e. Windows XP or 2K.
This unit (licensed under a MPL/GPL/LGPL tri-license) will use the new TaskDialog API under Vista/Seven, and emulate it with pure Delphi code and standard themed VCL components under XP or 2K. It supports Delphi 6 up to XE4, and is Win32/Win64 Unicode ready.
Here is the result under a Windows Seven 64 bit computer:
And here is the same dialog created from our emulated pure Delphi code:
Since this screenshot was made on a Win 7 machine, the styling is native for that OS. When the emulated version of the dialog runs on XP it displays in a style native to that OS.
You have your "Do not ask for this setting next time" checkbox... and potentially much more!
The system native functionality that offers such facilities is the task dialog API introduced in Vista. This provides means for you to show much more capable dialogs than the older MessageBox API.
Should you need to support XP then you will have to create your own dialog. For example by deriving from TForm and calling ShowModal. If you do this, make the form capable of building itself dynamically. Don't make one form per message that you show!
In my codebase, I have my own wrapper of the task dialog API. This detects at runtime versions of Windows that do not support task dialog and falls back on a custom built Delphi dialog.
Regarding SHMessageBoxCheck I'd be a little wary of taking a dependency on that. According to its documentation it's not supported beyond XP, and you have to import it by ordinal. I'd personally be worried that it might be dropped from a future version of Windows. That said, MS has a strong track record of doing whatever it takes to keep legacy apps working with new OS releases.

IWebBrowser2 and Chrome Plugin for Internet Explorer

I am trying to get access to the IWebBrowser2 object from Internet Explorer 8 with the chrome plugin. I am able to access it when the chrome plugin isn't installed, but it doesn't work due to the class names etc different.
Without chrome plugin I can use:
function GetIEFromHWND(WHandle: HWND; var IE: IWebbrowser2): HRESULT;
var
hInst: HWND;
lRes: Cardinal;
MSG: Integer;
pDoc: IHTMLDocument2;
ObjectFromLresult: TObjectFromLresult;
begin
Result := 0;
hInst := LoadLibrary('Oleacc.dll');
#ObjectFromLresult := GetProcAddress(hInst, 'ObjectFromLresult');
if #ObjectFromLresult <> nil then begin
try
MSG := RegisterWindowMessage('WM_HTML_GETOBJECT');
SendMessageTimeOut(WHandle, MSG, 0, 0, SMTO_ABORTIFHUNG, 1000, lRes);
Result := ObjectFromLresult(lRes, IHTMLDocument2, 0, pDoc);
if Result = S_OK then
(pDoc.parentWindow as IServiceProvider).QueryService(IWebbrowserApp, IWebbrowser2, IE);
finally
FreeLibrary(hInst);
end;
end;
end;
This doesn't work (I'm presuming) because there's no IHTMLDocument2 interface (using MS Spy++ you can see that the window heirarachy is completely different).
I can access the instance of the "Tab" that I'm after, but ultimately I need to "refresh" that tab with a new URL (which I was going to use IWebBrowser2.Navigate to accomplish).
I've tried importing the type library for Chrome but I can't find anything in there to help either. So I'm happy to utilise whatever I need to, in order to refresh tab that I have the handle to.
Thanks
If you need to use Chrome Frame I have wrapped the ActiveX control here:
http://www.progdigy.com/?p=116
But I would suggest you to use Delphi Chromium Embedded Instead, you will have more possibilities.
http://code.google.com/p/delphichromiumembedded/

Can you make a Borderless Application Main Window in Windows, without WS_POPUP style?

I want to create a window that will be the main window and that Windows itself recognizes as a main application window. However, when I make my window borderless, and without any non-client area at all, Windows no longer recognizes that this window is an application main window. This has several side effects:
WindowsKey+M minimizes all windows except my application's main window.
Clicking once on the taskbar (in win7) and then again, should toggle the application main window's state/visibility between normal and minimized state. This does not work for such a window.
In bare Win32 programming terms, I'm asking about parameter values for dwStyle as when calling CreateWindow (WS_... constants), or CreateWindowEx (WS_EX_... constants). For delphi users, these values would be set in the CreateParams method, which you would override, and then set Params.Style := WS_xxx; For MFC/C++ users and C users, something in your framework would eventually be calling CreateWindow, with this dwStyle value.
In delphi terms, setting your form.BorderStyle=bsNone, results in dwStyle=WS_POPUP. However I want a borderless window without using dwStyle=WS_POPUP.
Note: All the answers below are good, but using each in production scenarios is problematic, and my attempts to do so, have resulted in encountering many glitches, which for a professional quality application, I still find I can not work around. Davids answer is a great pure Win32 API answer though, and fits the bill. It seems that an industrial strength solution should combine multiple qualities, including all those I have in my question above. In short, borderless forms using BorderStyle=bsNone (dwStyle=WS_POPUP) block all Windows functionality that usually applies to main windows of applications, and all the solutions below solve part of it.
Based on David's suggestions, I wrote the following, which doesn't work:
I want a window without a border, that behaves in all ways, like a windows application window, to the system, that is, it can be minimized/restored by clicking on the window in the taskbar, and will be minimized by WindowsKey+M. I am beginning to think that the only way to do this is to add non-client paint code and to resize the top nonclient area bounds to zero. This is of course not a trivial idea.
It turns out that I made a simple mistake in my coding (hence the two paragraphs above) and in fact the code below does now work as I desire. This one is in pascal, but it should be easy to convert it to C++ or anything else.
program NoBorderProject;
uses
Windows, Messages;
{the Messages unit contains the windows
Message constants like WM_COMMAND}
{$R *.RES}
var
wClass: TWndClass;
Msg: TMsg;
win:HWND;
function WindowProc(hWnd,Msg,wParam,lParam:Integer):Integer; stdcall;
begin
if Msg = WM_DESTROY then PostQuitMessage(0);
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;
begin
wClass.lpszClassName:= 'CN';
wClass.lpfnWndProc := #WindowProc;
wClass.hInstance := hInstance;
wClass.hbrBackground:= 1;
RegisterClass(wClass);
win := CreateWindow(wClass.lpszClassName,'Title Bar',
WS_POPUP,//WS_OVERLAPPEDWINDOW or WS_VISIBLE,
10,10,340,220,0,0,hInstance,nil);
SetWindowLong(win, GWL_STYLE, WS_POPUP or WS_MINIMIZEBOX);
SetWindowLong(win, GWL_EXSTYLE, 0 );
ShowWindow(win,SW_SHOW);
while GetMessage(Msg,0,0,0) do
DispatchMessage(Msg);
end.
The following gets the job done:
hWnd = CreateWindow(...);
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_MINIMIZEBOX);
SetWindowLong(hWnd, GWL_EXSTYLE, 0);
ShowWindow(hWnd, ...);
You were probably missing WS_MINIMIZEBOX.
A bit icky, but you can set the window region by putting this in YourForm.OnShow event:
var
r: TRect;
begin
r := ClientRect;
OffsetRect(r, 0, GetSystemMetrics(SM_CYCAPTION));
OffsetRect(r, GetSystemMetrics(SM_CXFRAME), GetSystemMetrics(SM_CYFRAME));
SetWindowRgn(Handle,
CreateRectRgn(
r.Left, r.Top,
ClientWidth + r.Left, ClientHeight + r.Top), True);
You need to override TForm.CreateParams and set or remove any style that you are interest in
procedure TYourForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style and
Params.ExStyle := Params.ExStyle or ;
end;

Determine when an Excel workbook has closed with Delphi

The following code opens the document specified by the 'app' parameter and then waits until the particular document has been closed. This works fine for all document types, except when you have an Excel workbook open and open another Excel workbook. The code thinks the document has closed when it is actually still open. How would I solve this?
procedure RunAppAndWAit( a: TApplication; app, par, verb: string);
var
seinfo: tshellexecuteinfo;
exitcode: dword;
begin
fillchar( seinfo, sizeof( seinfo), 0);
seinfo.cbsize := sizeof( tshellexecuteinfo);
with seinfo do
begin
fmask := see_mask_nocloseprocess;
wnd := a.Handle;
lpfile := pchar( app);
lpDirectory := pchar( ExtractFileDir( app));
lpParameters := pchar( par);
lpVerb := pchar( verb);
nshow := sw_shownormal;
end;
if ShellExecuteEx( #seinfo) then
begin
repeat
a.ProcessMessages;
GetExitCodeProcess( seinfo.hprocess, exitcode);
until ( exitcode <> still_active) or a.terminated;
end
else
sshowmessage( 'Unable to open ' + app);
end;
Your attempt only works for applications that open the document in the same process which launches the document.
A lot of applications don't work this way any more: the process launching the document will pass the document to another process that shows/edits it, and the launching process dies.
You will need to find an API that supports event callbacks (in this case for Excel, most likely the COM API that Excel exposes) that lets you watch more closely what Excel actually does with your document.
Open your document using this API, register an event that gets called when the document is closed, wait for the event, then close.
This isn't pretty and may not be as reliable as you wish, but you could loop (or better, use a timer event?) calling the Windows EnumWindows function looking for title bars that match what you'd expect Excel to show for this file. (Obviously, this is an Excel-specific solution.)
For example, look for a title bar that contains the word "Excel" and your file name, which is what Excel shows in the title bar.
There may be holes in this approach that make it fragile. In fact, I'm a bit hesitant to post this since I don't think the solution is particularly robust. However, if you have no other way to solve your problem, this might work...
Google "EnumWindows Delphi" for sample code.
... on further thought,below is another way. As Jeroen noted, you could use an API to Excel. If you're doing a lot of these calls, then put the CreateOLEObject and unAssigned assignment outside the function might make it less heavy. (And you'll need some try...except blocks in case Excel is no longer running, etc.) This solution, too, is Excel-specific and clumsy, IMO. I don't know if there might be circumstances (like a File, Dialog box open in Excel?) that would cause this to return erroneous result.
So, basically, I'm saying, here are two relatively weak approaches that are specific to Excel and may not always work. (When I say it that way, I'd almost rather just delete this entire post... But, maybe it'll give you some ideas on how you want to proceed.)
This code is not tested, but similar code has worked for me in the past:
uses ComObj;
function FindWorkbook( Workbookname: String):boolean;
var
ExcelOLE: Variant;
WorkbookNumber: Integer;
begin
Result := FALSE;
ExcelOLE := CreateOLEObject('Excel.Application');
try
for WorkbookNumber := 1 to ExcelOLE.Workbooks.Count do
if UpperCase(WorkbookName) = UpperCase(ExcelOLE.Workbooks[WorkbookNumber].Name) then
Result := TRUE;
finally
ExcelOLE := unAssigned;
end;
end;

Windows System Tray icons - controlling position

I have a few old apps I've written (in Delphi) which for various reasons use a system tray icon. Most are using AppControls TacTrayIcon or some other similar component.
Here's my question: How does one control the position of a tray icon? (i.e. where it is, say, relative to the system time -- 1st position/"slot", 2nd position/"slot", etc). I recall seeing a demo (C#, if memory serves) that allowed the user to "shift icon to the left" and "shift icon to the right", but don't recall how it was done.
I'd like to allow the user to select what position they want to icon to appear in, for Windows 2000 - Windows 7. (I understand Windows 7 handles system tray stuff a little differently, but haven't tested that out yet).
Thanks for any and all help.
There is no documented or supported way for programs to control the positions of their shell notification icons. There's not even anything guaranteeing they will appear at all, or if they do appear, that they will appear anywhere near the clock, such that your positioning instructions would make any sense.
(I used to use a program that hijacked some or all of the icons and optionally displayed them in its own window instead of in the area near the clock. It was TraySaver, by Mike Lin. The source is available if you wish to see how his hack worked.)
The icon's position is not under your control. My advice to you is to not try to make it your program's responsibility, especially if nobody has actually requested such functionality from your program in the first place. If people want to control your program's icon location, they probably want to control other programs' icon locations, in which case the problem is bigger than you anyway.
Accessing and modifying the shell notification area is hackish but possible. You first need to find the top level window:
var
Wnd: HWND;
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
if IsWindow(Wnd) then
EnumChildWindows(Wnd, #FindTrayWnd, 0);
end;
then enumerate its children to find the tray notification area:
function FindTrayWnd(AWnd: HWND; AParam: LPARAM): BOOL; stdcall;
var
ClassName: string;
begin
SetLength(ClassName, 64);
SetLength(ClassName, GetClassName(AWnd, PChar(ClassName), 64));
Result := True;
if AnsiCompareText(ClassName, 'TrayNotifyWnd') = 0 then begin
EnumChildWindows(AWnd, #FindToolbar, 0);
Result := False;
end;
end;
then enumerate its children to find the standard Windows toolbar with the notification icons. Windows messages are used to get or set toolbar properties. Since the toolbar lives in another process you need to employ ReadProcessMemory() and WriteProcessMemory() for all messages that involve a buffer of some sort (like getting the button text or button info):
function FindToolbar(AWnd: HWND; AParam: LPARAM): BOOL; stdcall;
const
VMFLAGS = PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE;
var
ClassName: string;
i, ButtonCount: integer;
ProcessId, BytesRead: Cardinal;
ProcessHandle: THandle;
ExplorerButtonInfo: PTBButton;
ButtonInfo: array of TTBButton;
begin
SetLength(ClassName, 64);
SetLength(ClassName, GetClassName(AWnd, PChar(ClassName), 64));
if AnsiCompareText(ClassName, 'ToolbarWindow32') = 0 then begin
GetWindowThreadProcessId(AWnd, #ProcessId);
ProcessHandle := OpenProcess(VMFLAGS, FALSE, ProcessId);
ExplorerButtonInfo := VirtualAllocEx(ProcessHandle, nil, SizeOf(TTBButton),
MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
if ExplorerButtonInfo <> nil then try
ButtonCount := SendMessage(AWnd, TB_BUTTONCOUNT, 0, 0);
SetLength(ButtonInfo, ButtonCount);
for i := 0 to ButtonCount - 1 do begin
SendMessage(AWnd, TB_GETBUTTON, i, LPARAM(ExplorerButtonInfo));
ReadProcessMemory(ProcessHandle, ExplorerButtonInfo, #ButtonInfo[i],
SizeOf(TTBButton), BytesRead);
end;
// manipulate the button info, use WriteProcessMemory() and SendMessage()
// to repopulate the toolbar
finally
VirtualFreeEx(ProcessId, ExplorerButtonInfo, SizeOf(TTBButton),
MEM_RELEASE);
end;
Result := False;
end else
Result := True;
end;
You should be able to identify the button of your notification icon via its name, then delete that button, then insert it at the desired position. All error handling omitted, but this should get you started.
Look at this article on CodeProject, hope it helps.
This solution have very limited compatibility, and not recommended at all.

Resources