How to hook to WH_KEYBOARD for PopupList.Window? - delphi

I need to know if F11 is pressed when PopupMenu of TrayIcon is open & terminate program. Do not want RegisterHotKey. Documentation states that "PopupList.Window provides access to the Window handle of the hidden window that processes popup menu messages". So my plan is to intercept keyboard messages to that window, but instead this is what happens:
Project Project2.exe raised exception class $C0000005 with message
'access violation at 0x00020003: write of address 0x2014fd38'.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ImgList, Vcl.Menus, Vcl.ExtCtrls;
type
TForm2 = class(TForm)
TrayIcon1: TTrayIcon;
PopupMenu1: TPopupMenu;
N11: TMenuItem;
N21: TMenuItem;
ImageList1: TImageList;
procedure PopupMenu1Popup(Sender: TObject);
private
function hook(code: Integer; w: WPARAM; p : LPARAM): Lresult stdcall;
public
{ Public declarations }
end;
var
Form2: TForm2;
HookID: hhook;
implementation
{$R *.dfm}
function TForm2.hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall;
begin
if code < 0 then
begin
Result := CallNextHookEx(0, code, w, p);
Exit;
end;
Result := CallNextHookEx(0, code, w, p);
end;
procedure TForm2.PopupMenu1Popup(Sender: TObject);
begin
HookID := SetWindowsHookEx(WH_KEYBOARD, #TForm2.hook, 0,
GetWindowThreadProcessId(PopupList.Window, nil));
end;
end.

Your hook function is a method of TForm2 and thus has an extra (hidden) self parameter passed. You should place the function outside of TForm2:
TForm2 = class(TForm)
TrayIcon1: TTrayIcon;
PopupMenu1: TPopupMenu;
N11: TMenuItem;
N21: TMenuItem;
ImageList1: TImageList;
procedure PopupMenu1Popup(Sender: TObject);
private
public
{ Public declarations }
end;
function hook(code: Integer; w: WPARAM; p : LPARAM): Lresult stdcall;
implementation
function hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall;
begin
if code < 0 then
begin
Result := CallNextHookEx(0, code, w, p);
Exit;
end;
Result := CallNextHookEx(0, code, w, p);
end;

Related

Creating a system-wide hook that adds a menu item to the system menu of every Windows program?

In a 32-bit Windows 10 VCL Application in Delphi 11 Alexandria, I am trying to implement a system-wide hook that adds a menu item to the system menu of every Windows program. For this purpose, I have created and built this DLL:
library SystemMenuHookDLL;
uses
Winapi.Windows,
System.SysUtils,
System.Classes;
{$R *.res}
function AddMenuItem(WindowHandle: HWND): Boolean;
var
MenuHandle: HMENU;
MenuItemID: UINT;
WindowStyles: DWORD;
begin
Result := False;
MenuHandle := GetSystemMenu(WindowHandle, False);
if MenuHandle <> 0 then
begin
MenuItemID := 999;
AppendMenu(MenuHandle, MF_STRING, MenuItemID, 'My Menu Item');
SetMenuDefaultItem(MenuHandle, MenuItemID, MF_BYCOMMAND);
WindowStyles := GetWindowLong(WindowHandle, GWL_STYLE);
WindowStyles := WindowStyles or WS_SYSMENU;
SetWindowLong(WindowHandle, GWL_STYLE, WindowStyles);
DrawMenuBar(WindowHandle);
Result := True;
end;
end;
function HookFunc(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall;
begin
if Code = HCBT_CREATEWND then
begin
AddMenuItem(HWND(WParam));
end;
Result := CallNextHookEx(0, Code, WParam, LParam);
end;
exports HookFunc;
begin
end.
This is the code for the Host Application for the DLL:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TForm1 = class(TForm)
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
CodeSiteLogging;
type
THookProc = function(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall;
var
DLLHandle: HMODULE;
HookProc: THookProc;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnhookWindowsHookEx(WH_CBT);
FreeLibrary(DLLHandle);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DLLHandle := LoadLibrary('SystemMenuHookDLL.dll');
CodeSite.Send('TForm1.FormCreate: DLLHandle', DLLHandle);
if DLLHandle <> 0 then
begin
#HookProc := GetProcAddress(DLLHandle, 'HookFunc');
CodeSite.Send('TForm1.FormCreate: #HookProc', #HookProc);
if Assigned(HookProc) then
begin
CodeSite.Send('TForm1.FormCreate: Assigned');
SetWindowsHookEx(WH_CBT, HookProc, DLLHandle, 0);
end;
end;
end;
end.
I have put the DLL in the same directory of the host application exe file. Unfortunately, it does not work: After having started the host application, and then trying to start a 32-bit program, the program does not start! It seems that the hook is blocking the program.

Why has the parameter of my Delphi function with PyDelphiWrapper always a fix wrong value?

I am using Python4Delphi and try to get the demo WrapDelphiDemo running.
What it should do is to calculate the amount of prime numbers for values up to 1000000.
The expected value is 78498 but when I let the demo code running I get 575843.
I found out that parameter value "MaxN" of the function is always a fix value of 8574564 instead the expected 1000000.
class function TDelphiFunctions.count_primes(MaxN: integer): integer;
var
Count : integer;
begin
Count := 0;
ShowMessage(format('function parameter MaxN=%d is WRONG!!!! Should be 1000000!!!',[MaxN]));
//MaxN := 1000000;
TParallel.&For(2, MaxN, procedure(i: integer)
begin
if IsPrime(i) then
AtomicIncrement(Count);
end);
Result := Count;
end;
I use Delphi Seattle with Win7.
Python4Delphi is the latest from GitHub.
I use the original demo code.
What I need to adapt is that with Seattle version I cannot use inline variable definition.
Does anyone have an idea what I can do?
Here the full code of MainForm:
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, SynEdit, Vcl.StdCtrls,
PythonEngine, PythonGUIInputOutput, SynEditPythonBehaviour,
SynEditHighlighter, SynEditCodeFolding, SynHighlighterPython, Vcl.ExtCtrls,
WrapDelphi;
type
TForm1 = class(TForm)
sePythonCode: TSynEdit;
HeaderControl1: THeaderControl;
Panel1: TPanel;
Splitter1: TSplitter;
Panel2: TPanel;
HeaderControl2: THeaderControl;
mePythonOutput: TMemo;
SynPythonSyn: TSynPythonSyn;
SynEditPythonBehaviour: TSynEditPythonBehaviour;
PythonEngine: TPythonEngine;
PythonGUIInputOutput: TPythonGUIInputOutput;
btnRun: TButton;
PyDelphiWrapper: TPyDelphiWrapper;
PythonModule: TPythonModule;
procedure FormCreate(Sender: TObject);
procedure btnRunClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
System.Rtti,
System.Threading,
System.Math;
type
TDelphiFunctions = record
class function count_primes(MaxN: integer): integer; static;
end;
var
DelphiFunctions: TDelphiFunctions;
procedure TForm1.FormCreate(Sender: TObject);
var
Py : PPyObject;
begin
Py := PyDelphiWrapper.WrapRecord(#DelphiFunctions, TRttiContext.Create.GetType(TypeInfo(TDelphiFunctions)) as TRttiStructuredType);
PythonModule.SetVar('delphi_functions', Py);
PythonEngine.Py_DecRef(Py);
end;
procedure TForm1.btnRunClick(Sender: TObject);
begin
GetPythonEngine.ExecString(UTF8Encode(sePythonCode.Text));
end;
function IsPrime(x: Integer): Boolean;
var
q, i : integer;
begin
if (x <= 1) then Exit(False);
q := Floor(Sqrt(x));
for i := 2 to q do
if (x mod i = 0) then
Exit(False);
Exit(True);
end;
class function TDelphiFunctions.count_primes(MaxN: integer): integer;
var
Count : integer;
begin
Count := 0;
ShowMessage(format('function parameter MaxN=%d is WRONG!!!! Should be 1000000!!!',[MaxN]));
//MaxN := 1000000;
TParallel.&For(2, MaxN, procedure(i: integer)
begin
if IsPrime(i) then
AtomicIncrement(Count);
end);
Result := Count;
end;
end.

Invalid handle passed to midiInClose

Below I have some code. What I want to happen is when a button is pressed it creates, or closes, a MIDI connection depending on the state of an existing connection.
But, when attempting to close the MIDI handle I get an error response code 5, which means an invalid handle to a MIDI device has been passed to midiInClose.
I'm not sure why this is happening by my guess is scope issues? I just can't figure out how to resolve this. Should the hMidiIn be defined as a class variable within the form?
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.UITypes, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
MMSystem, uMIDI;
type
TForm2 = class(TForm)
MidiInputCombo: TComboBox;
Label1: TLabel;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
IsConnected: Boolean = False;
SelectedMidiInputID: Integer = 0;
hMidiIn: PHMIDIIN;
implementation
{$R *.dfm}
procedure MidiInProc(hmi: PHMIDIIN; wMsg: UINT; dwInstance: DWORD_PTR; dwParam1: DWORD_PTR; dwParam2: DWORD_PTR); stdcall;
begin
// Do something
end;
procedure TForm2.Button1Click(Sender: TObject);
var
MidiInCloseResult: Integer;
begin
if IsConnected then
begin
MidiInCloseResult := midiInClose(hMidiIn^);
if MidiInCloseResult = MMSYSERR_NOERROR then
begin
IsConnected := False;
Button1.Caption := 'Connect';
end
else
MessageDlg('Response: ' + IntToStr(MidiInCloseResult), mtInformation, [mbOk], 0, mbOk);
end
else
if midiInOpen(#hMidiIn, SelectedMidiInputID, DWORD_PTR(#MidiInProc), 0, CALLBACK_FUNCTION) = MMSYSERR_NOERROR then
begin
IsConnected := True;
Button1.Caption := 'Disconnect';
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
var
I: Integer;
DevCount: Integer;
Pmic: MIDIINCAPS;
begin
DevCount := MidiInputDeviceCount();
if DevCount > 0 then
for I := 0 to DevCount do
midiInGetDevCaps(I, #Pmic, SizeOf(pmic));
MidiInputCombo.Items.Add(Pmic.szPname);
MidiInputCombo.ItemIndex := SelectedMidiInputID;
end;
end.

issuing Netsh command from Delphi program

I am trying to capture the Device ID of an AirCard. I am using the following code with the intentions of storing the results in a text file (imei.txt) that I store in the Temp folder and loop through the contents, looking for DEVICE ID.
The problems is that it only writes "The following command was not found: mbn show interface." to the file.
I have tested the Netsh command from the command line and it returns what I would expect.
xs1 := CreateOleObject('WSCript.Shell');
xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
'"', 0, true);
It is failing to process the NetSh command properly. Am I passing it through the Comspec correctly? It seems to not run the "NetSh" command and acts as if I am running "mbn" from the command prompt.
Thanks
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Win.ComObj, ShlObj, Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
xs1 : OleVariant;
IMEIFileName: String;
IMEIStrings: TStringList;
I: Integer;
function GetSpecialFolder(const CSIDL: Integer): string;
var
RecPath: PWideChar;
begin
RecPath := StrAlloc(MAX_PATH);
try
FillChar(RecPath^, MAX_PATH, 0);
if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
result := RecPath
else
result := '';
finally
StrDispose(RecPath);
end;
end;
begin
IMEI := '';
IMEIFileName := GetSpecialFolder(CSIDL_LOCAL_APPDATA) + '\Temp\imei.txt';
Memo1.Lines.Add('IMEIFileName: ' + IMEIFileName);
try
if FileExists(IMEIFileName) then
DeleteFile(IMEIFileName);
xs1 := CreateOleObject('WSCript.Shell');
xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
'"', 0, true);
if FileExists(IMEIFileName) then
begin
IMEIStrings := TStringList.Create;
IMEIStrings.LoadFromFile(IMEIFileName);
IMEIStrings.NameValueSeparator := ':';
Memo1.Lines.Add('IMEIStrings Count: ' + intToStr(IMEIStrings.Count));
for I := 0 to IMEIStrings.Count - 1 do
begin
Memo1.Lines.Add(IMEIStrings.text);
if (Uppercase(Trim(IMEIStrings.Names[I])) = 'DEVICE ID') then
begin
IMEI := Trim(IMEIStrings.Values[IMEIStrings.Names[I]]);
Memo1.Lines.Add('IMEI:' + IMEI);
break;
end;
end;
end;
except
IMEI := '';
end;
Memo1.Lines.Add('process complete');
end;
end.
You should not be using the WShell COM object to run cmd.exe. That is overkill. You can use CreateProcess() instead. However, when running cmd.exe programmably, you cannot redirect its output using the > operator, that only works in an actual command window. You can instead use the STARTUPINFO structure to redirect the output to an anonymous pipe created with CreatePipe(), and then you can read from that pipe using ReadFile(). No need to use a temp file at all. MSDN has an article on this topic:
Creating a Child Process with Redirected Input and Output
There are plenty of examples floating around that demonstrate this technique in Delphi.
That being said, a better option is to not use netsh at all. Windows 7 and later have a Mobile Broadband API. You can enumerate the MBN interfaces directly in your code.
For example, using the WwanEnumerateInterfaces() function:
unit WwApi;
{$MINENUMSIZE 4}
interface
uses
Windows;
const
WWAN_STR_DESC_LENGTH = 256;
type
WWAN_INTERFACE_STATE = (
WwanInterfaceStateNotReady,
WwanInterfaceStateDeviceLocked,
WwanInterfaceStateUserAccountNotActivated,
WwanInterfaceStateRegistered,
WwanInterfaceStateRegistering,
WwanInterfaceStateDeregistered,
WwanInterfaceStateAttached,
WwanInterfaceStateAttaching,
WwanInterfaceStateDetaching,
WwanInterfaceStateActivated,
WwanInterfaceStateActivating,
WwanInterfaceStateDeactivating
);
WWAN_INTF_OPCODE = (
WwanIntfOpcodePin,
WwanIntfOpcodeRadioState,
WwanIntfOpcodePreferredProviders,
WwanIntfOpcodeCurrentConnection,
WwanIntfOpcodeProvisionedContexts,
WwanIntfOpcodeActivateUserAccount,
WwanIntfOpcodeVendorSpecific,
WwanIntfOpcodeInterfaceObject,
WwanIntfOpcodeConnectionObject,
WwanIntfOpcodeAcState,
WwanIntfOpcodeClearManualConnectState,
WwanIntfOpcodeGetStoredRadioState,
WwanIntfOpcodeGetRadioInfo,
WwanIntfOpcodeHomeProvider
);
// I don't know the definition of this type!
WWAN_STATUS = DWORD; //?
WWAN_INTERFACE_STATUS = record
fInitialized: BOOL;
InterfaceState: WWAN_INTERFACE_STATE;
end;
PWWAN_INTERFACE_INFO = ^WWAN_INTERFACE_INFO;
WWAN_INTERFACE_INFO = record
InterfaceGuid: TGuid;
strInterfaceDescription: array[0..WWAN_STR_DESC_LENGTH-1] of WCHAR;
InterfaceStatus: WWAN_INTERFACE_STATUS;
ParentInterfaceGuid: TGuid;
fIsAdditionalPdpContextInterface: BOOL;
end;
PWWAN_INTERFACE_INFO_LIST = ^WWAN_INTERFACE_INFO_LIST;
WWAN_INTERFACE_INFO_LIST = record
dwNumberOfItems: DWORD;
pInterfaceInfo: array[0..0] of WWAN_INTERFACE_INFO;
end;
function WwanOpenHandle(dwClientVersion: DWORD; pReserved: Pointer; var pdwNegotiatedVersion: DWORD; var phClientHandle: THandle): DWORD; stdcall;
function WwanCloseHandle(hClientHandle: THandle; pReserved: Pointer): DWORD; stdcall;
function WwanEnumerateInterfaces(hClientHandle: THandle; pdwReserved: PDWORD; var ppInterfaceList: PWWAN_INTERFACE_INFO_LIST): DWORD; stdcall;
procedure WwanFreeMemory(pMem: Pointer); stdcall;
function WwanQueryInterface(hClientHandle: THandle; const pInterfaceGuid: TGuid; opCode: WWAN_INTF_OPCODE; pReserved: Pointer; var pdwDataSize: DWORD; var ppData: PByte; var pRequestId: ULONG; var pStatus: WWAN_STATUS): DWORD; stdcall;
implementation
const
WwApiLib = 'WwApi.dll';
function WwanOpenHandle; external WwApiLib delayed;
function WwanCloseHandle; external WwApiLib delayed;
function WwanEnumerateInterfaces; external WwApiLib delayed;
procedure WwanFreeMemory; external WwApiLib delayed;
function WwanQueryInterface; external WwApiLib delayed;
end.
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
WwApi;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
dwNegotiatedVersion: DWORD;
hClientHandle: THandle;
pInterfaceList: PWWAN_INTERFACE_INFO_LIST;
pInterface: PWWAN_INTERFACE_INFO;
I: DWORD;
begin
IMEI := '';
Memo1.Clear;
try
// The value of the first parameter is undocumented!
// WlanOpenHandle() has a similar parameter, where 1
// is for XP and 2 is for Vista+. Maybe it is the same
// for WwanOpenHandle()?...
//
if WwanOpenHandle(2, nil, dwNegotiatedVersion, hClientHandle) = 0 then
try
if WwanEnumerateInterfaces(hClientHandle, nil, pInterfaceList) = 0 then
try
Memo1.Lines.Add('IMEIStrings Count: ' + IntToStr(pInterfaceList.dwNumberOfItems));
if pInterfaceList.dwNumberOfItems > 0 then
begin
pInterface := #pInterfaceList.pInterfaceInfo[0];
for I := 0 to pInterfaceList.dwNumberOfItems-1 do
begin
// use pInterface as needed...
Memo1.Lines.Add('Desc:' + StrPas(pInterface.strInterfaceDescription));
Memo1.Lines.Add('Intf:' + GUIDToString(pInterface.InterfaceGuid));
// and so on ...
Memo1.Lines.Add('');
Inc(pInterface);
end;
end;
finally
WwanFreeMemory(pInterfaceList);
end;
finally
WwanCloseHandle(hClientHandle, nil);
end;
except
end;
Memo1.Lines.Add('process complete');
end;
end.
Alternatively, using the IMbnInterfaceManager and IMbnInterface COM interfaces, which give you more detailed information:
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
// I found the MbnApi.pas unit on the DelphiPraxis forum:
//
// http://www.delphipraxis.net/1342330-post2.html
//
// It is too large to post here on StackOverflow!
// Otherwise, you can import the mbnapi.tlb TypeLibrary yourself...
//
MbnApi, ActiveX, ComObj;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
Mgr: IMbnInterfaceManager;
pInterfaceArray, pPhoneNumberArray: PSafeArray;
pInterface: IMbnInterface;
subscriber: IMbnSubscriberInformation;
ReadyState: MBN_READY_STATE;
lIntfLower, lIntfUpper: LONG;
lPhoneNumLower, lPhoneNumUpper: LONG;
I, J: LONG;
wStr: WideString;
begin
Memo1.Clear;
try
OleCheck(CoCreateInstance(CLASS_MbnInterfaceManager, nil, CLSCTX_ALL, IMbnInterfaceManager, Mgr));
OleCheck(Mgr.GetInterfaces(pInterfaceArray));
try
OleCheck(SafeArrayGetLBound(pInterfaceArray, 1, lIntfLower));
OleCheck(SafeArrayGetUBound(pInterfaceArray, 1, lIntfUpper));
for I = lIntfLower to lIntfUpper do
begin
OleCheck(SafeArrayGetElement(pInterfaceArray, I, pInterface));
try
// use pInterface as needed...
OleCheck(pInterface.get_InterfaceID(wStr));
try
Memo1.Lines.Add('Interface ID:' + wStr);
finally
wStr := '';
end;
OleCheck(pInterface.GetReadyState(ReadyState));
Memo1.Lines.Add('Ready State:' + IntToStr(Ord(ReadyState)));
OleCheck(pInterface.GetSubscriberInformation(subscriber));
try
OleCheck(subscriber.Get_SubscriberID(wStr));
try
Memo1.Lines.Add('Subscriber ID: ' + wStr);
finally
wStr := '';
end;
OleCheck(subscriber.Get_SimIccID(wStr));
try
Memo1.Lines.Add('Sim ICC ID: ' + wStr);
finally
wStr := '';
end;
OleCheck(subscriber.Get_TelephoneNumbers(pPhoneNumberArray));
try
OleCheck(SafeArrayGetLBound(pPhoneNumberArray, 1, lPhoneNumLower));
OleCheck(SafeArrayGetUBound(pPhoneNumberArray, 1, lPhoneNumUpper));
for J = lPhoneNumLower to lPhoneNumUpper do
begin
OleCheck(SafeArrayGetElement(pPhoneNumberArray, J, wStr));
try
Memo1.Lines.Add('Phone #:' + wStr);
finally
wStr := '';
end;
end;
finally
SafeArrayDestroy(pPhoneNumberArray);
end;
finally
subscriber := nil;
end;
// and so on...
Memo1.Lines.Add('');
finally
pInterface := nil;
end;
end;
finally
SafeArrayDestroy(pInterfaceArray);
end;
except
end;
Memo1.Lines.Add('process complete');
end;
end.

Form1 disappears when focus is on Form1 and browser window loses focus

I have a problem when my Form1 appear on body of browser window, simply he disappear when I put focus in my Form and the browser window lose focus. How solved it? Any suggestion will welcome.
See image below:
and here is my complete code:
Unit for enumeration of windows (EnumWindowUtil_.pas):
unit EnumWindowUtil_;
interface
uses
Winapi.Windows,
System.SysUtils,
System.Classes;
type
TWindowList = class(TStringList)
private
FAddClassname: Boolean;
public
procedure EnumChildWindows(handle: HWND);
property AddClassname: Boolean read FAddClassname write FAddClassname;
end;
var
wlistChilds: TWindowList;
implementation
function GetWindowClassName(hwnd: HWND): string;
begin
SetLength(Result, 1024);
GetClassName(hwnd, PChar(Result), Length(Result));
Result := PChar(Result);
end;
procedure EnumWindowCallback(hwnd: HWND; lParam: TWindowList); stdcall;
var
buffer: array[0..255] of char;
texto: string;
begin
if (not IsWindowVisible(hwnd)) then
Exit;
SendMessage(hwnd, $000D, 256, Integer(#buffer));
texto := StrPas(buffer);
texto := texto + ':' + GetWindowClassName(hwnd) + ' - ' + Format('%6.6x', [hwnd]) + '/' + IntToStr(hwnd);
lParam.AddObject(texto, TObject(hwnd));
end;
procedure TWindowList.EnumChildWindows(handle: HWND);
begin
Clear;
if Winapi.Windows.EnumChildWindows(handle, #EnumWindowCallback, Integer(Self)) then;
end;
end.
Here is main unit:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, EnumWindowUtil_,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
linha: string;
implementation
{$R *.dfm}
function GetNavigatorHandle(BASE: HWND): HWND;
var
I: integer;
begin
linha:= '';
Result := 0;
wlistChilds := TWindowList.Create;
wlistChilds.AddClassname := True;
wlistChilds.EnumChildWindows(BASE);
for I := 0 to wlistChilds.Count - 1 do
begin
linha := wlistChilds.Strings[I];
if
(Pos('Chrome_Render',linha)>0)then
begin
Result := StrToInt(copy(linha, pos('/', linha) + 1, Length(linha)));
Break;
end;
end;
FreeAndNil(wlistChilds);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
janela, janelaContainer: HWND;
begin
janela := GetForegroundWindow;
janelaContainer := GetNavigatorHandle(Janela);
if janelaContainer = 0 then
begin
Exit;
end;
Winapi.Windows.SetParent(form1.handle,janelaContainer);
end;
end.

Resources