IWebBrowser2 and Chrome Plugin for Internet Explorer - delphi

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/

Related

How can I invoke the driver Settings dialog for a printer

Rather than use the standard PrintDialog I am building my own dialog.
I want to be able to invoke the Printer driver's own Setup dialog i.e. as if one had clicked on the Properties button from the PrintDialog.
Can you suggest a method of doing this?
I have not used this API before, but it seems to me like you can use the DocumentProperties function for this.
A minimal example (using the default printer):
var
PrinterName: string;
BufLen: Cardinal;
PrinterHandle: THandle;
begin
GetDefaultPrinter(nil, #BufLen);
SetLength(PrinterName, BufLen);
GetDefaultPrinter(PChar(PrinterName), #BufLen);
SetLength(PrinterName, BufLen - 1);
if not OpenPrinter(PChar(PrinterName), PrinterHandle, nil) then
begin
ShowMessage('Could not open printer.');
Exit;
end;
try
DocumentProperties(Handle, PrinterHandle, PChar(PrinterName), nil, nil, DM_IN_PROMPT)
// possibly do other things that might raise an exception
finally
ClosePrinter(PrinterHandle);
end;
The nil pointers can be replaced by DEVMODE structures that contain the initial settings and the settings selected by the user in the GUI, if you also add the corresponding flags. See the documentation for details.

How to write and show something on Delphi IDE status bar

I want to know how can I write a module to show something like clock or other thing on Borland Delphi 7 IDE status bar, because I know it's possible but I couldn't find how!
To insert a text in a StatusBar, you have to insert a panel first.
Just select your statusbar, find the property "Panels" (or perform double click over the statusbar) and click in "Add new".
After that, you can write what you want inside the panel in the property "Text" (you can insert one or more panels).
To do it programmatically, you can do something like this:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
StatusBar1.Panels[0].Text := 'Today is: ' + FormatDateTime('dd/mm/yyyy hh:nn:ss', Now);
end;
Since OP didn't replied with more details, I'm going to post a little demonstration how to reach a status bar of Delphi's edit window. I had no success with adding new distinct status panel w/o disturbing layout, so I'm just changing the text of INS/OVR indicator panel.
Disclaimer: I still do not have access to the machine with Delphi 7 installed, so I've done that in BDS ("Galileo") IDE. However, differences should be minor. I believe what main difference lies in the way how we locate edit window.
Key strings are: 'TEditWindow' for edit window class name and 'StatusBar' for TStatusBar control name owned by edit window. These strings are consistent across versions.
{ helper func, see below }
function FindForm(const ClassName: string): TForm;
var
I: Integer;
begin
Result := nil;
for I := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[I].ClassName = ClassName then
begin
Result := Screen.Forms[I];
Break;
end;
end;
end;
procedure Init;
var
EditWindow: TForm;
StatusBar: TStatusBar;
StatusPanel: TStatusPanel;
begin
EditWindow := FindForm('TEditWindow');
Assert(Assigned(EditWindow), 'no edit window');
StatusBar := EditWindow.FindComponent('StatusBar') as TStatusBar;
(BorlandIDEServices as IOTAMessageServices).AddTitleMessage(Format('StatusBar.Panels.Count = %d', [StatusBar.Panels.Count]));
//StatusPanel := StatusBar.Panels.Add;
StatusPanel := StatusBar.Panels[2];
StatusPanel.Text := 'HAI!';
end;
initialization
Init;
finalization
// nothing to clean up yet
Another note: As you see, I use Open Tools API to output debug messages only, to interact with IDE I do use Native VCL classes. Therefore, this code must be in package.
The code above is a relevant part of the unit which should be contained in package. Do not forget to add ToolsAPI to uses clause as well as other appropriate referenced units (up to you).
Package should require rtl, vcl and designide (important!).
Since I run the testcase directly from initialization section, installing the package is enough for testcase to run and produce some result.

UI Automation - ElementFromHandle doesn't find element

I am trying to use UIAutomation to access/control Chrome browser (using that method based on what other people have used - eg to get the current URL).
For the purposes ok the exercise, I'm trying to replicate this question - Retrieve current URL from C# windows forms application - in Delphi. I've imported the TLB ok. However, my call to ElementFromHandle never locates an element.
The signature of the ElementFromHandle method is:
function ElementFromHandle(hwnd: Pointer; out element: IUIAutomationElement): HResult; stdcall;
My test is simply:
procedure TForm3.Button1Click(Sender: TObject);
var
UIAuto: IUIAutomation;
element: IUIAutomationElement;
value: WideString;
h: PInteger;
begin
new(h);
h^ := $1094E;
SetForegroundWindow(h^);
ShowWindow(h^, SW_SHOW);
UIAuto := CoCUIAutomation.Create;
UIAuto.ElementFromHandle(h, element);
if Assigned(element) then
begin
element.Get_CurrentName(value);
showmessage('found -' + value);
end
else
showMessage('not found');
end;
Calls to SetForegroundWindow and ShowWindow are just there in case it needed focus (but I doubted that it would make a difference and doesn't). I can confirm that the Handle I'm passing in ($1094E) is "correct" in so much as Spy++ shows that value for the Chrome Tab I'm trying to access. The active tab in Chrome always reports that Handle.
Is my implementation correct above? Is there more to using UIAutomation than what I have implemented above? I have never explored it before.
Thanks
EDIT
I have found if I use ElementFromPoint and pass in a (hardcoded) value of where I know my Tab sits in terms of X,Y - it does work. ie:
UIAuto := CoCUIAutomation.Create;
p.x := 2916;
p.y := 129;
UIAuto.ElementFromPoint(p, element);
if Assigned(element) then
The above snippet if placed in the above OnClick event does return an element instance and the one I'm expecting too (which is a bonus). So maybe I'm passing in an incorrect value for Hwnd in ElementFromHandle? ie, I'm using the "top" level handle of Chrome as found my MS Spy++:
This sits directly under (Desktop) in Spy++.
Your mistake is in the way that you pass the window handle to ElementFromHandle. You are meant to pass an HWND. Instead you pass the address of an HWND.
The function should really be:
function ElementFromHandle(hwnd: HWND;
out element: IUIAutomationElement): HResult; stdcall;
You should remove the call to New and instead do:
var
window: HWND;
....
window := HWND($1094E);
Then call the function like this:
if Succeeded(UIAuto.ElementFromHandle(window, element)) then
....
Perhaps your biggest fundamental problem is the complete absence of error checking. I think you need to adjust your mindset to realise that these API calls will not raise exceptions. They report failure through their return value. You must check every single API call for failure.
One common way to do that is to convert HRESULT values to exceptions in case of failure with calls to OleCheck. For example:
var
UIAuto: IUIAutomation;
element: IUIAutomationElement;
value: WideString;
window: HWND;
....
window := HWND($1094E);
SetForegroundWindow(window);
ShowWindow(window, SW_SHOW);
UIAuto := CoCUIAutomation.Create;
OleCheck(UIAuto.ElementFromHandle(window, element));
OleCheck(element.Get_CurrentName(value));
ShowMessage('found -' + value);

SendMessage to Firefox, on cursor position

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.

Using DwmIsCompositionEnabled (JwaDwmApi) on pre-vista causes error

Been trying to use the following code in order to check if Windows Aero is enabled:
function AeroEnabled: boolean;
var
enabled: bool;
begin
// Function from the JwaDwmapi unit (JEDI Windows Api Library)
DwmIsCompositionEnabled(enabled);
Result := enabled;
end;
...
if (CheckWin32Version(5,4)) and (AeroEnabled) then
CampaignTabs.ColorBackground := clBlack
else begin
GlassFrame.Enabled := False;
CampaignTabs.ColorBackground := clWhite;
end;
However, doing so on a pre-vista machine causes the app to crash because the DWMApi.dll is missing. I've also tried this code however it produces 2 AV's in a row. How can I do this ? I am using Delphi 2010. :)
You've got your versions wrong. Vista/2008 server are version 6.0. Your test should be:
CheckWin32Version(6,0)
I believe that you are using Delphi 2010 or later in which case you should simply call the DwmCompositionEnabled function from the built-in Dwmapi unit. This organises the version check and the delayed binding for you. No need for JEDI.
Edit: Text below was written before the question was edited.
Probably the easiest approach is to check the Windows version. You need Win32MajorVersion>=6 (i.e. Vista or 2008 server) in order to call DwmIsCompositionEnabled.
If you were binding yourself then you would call LoadLibrary with DWMApi.dll and if that succeeded you would then call GetProcAddress to bind. If that succeeded you are good. But, as I said, since you aren't handling the binding yourself then a version check is probably the simplest.
So the function would be:
function AeroEnabled: boolean;
var
enabled: bool;
begin
if Win32MajorVersion>=6 then begin
DwmIsCompositionEnabled(enabled);
Result := enabled;
end else begin
Result := False;
end;
end;
Note, I'm assuming that your library is doing late binding, i.e. explicit linking. If not then you'll need LoadLibrary/GetProcAddress, exactly as is done in #RRUZ's code to which you link.

Resources