- This topic has 12 replies, 4 voices, and was last updated 10 years ago by Chike.
-
AuthorPosts
-
July 14, 2013 at 11:19 am #191044DepartureMember
Here is an example how to get all of paltalks handles we use in our applications, It will get the handles to both “RichEdit20W” used for sending and receiving text and also the Nicklist handle, it will also create a list which can be used to store multiple windows, like “Paltalk room selector”. It uses a generic list with a custom record for storing the handles for easy access. This is only demonstration and I suggest using what you learn here to make your own class and add methods like “GetText”, “SendText” ect.. for each item in your list.
Here is an example how to retrive the data to use…
// Itemindex in this example is the item index of a combobox... // Returns a String which will be the main room / pm Title WindowList.Items[ItemIndex].sMain; // Returns a type Hwnd for the main Room / Pm Window WindowList.Items[ItemIndex].hMain // Returns a type Hwnd for the Incooming text WindowList.Items[ItemIndex].hIncomming // Returns a type Hwnd for the Outgoing text WindowList.Items[ItemIndex].hOutgoing // Returns a type Hwnd for the Nicklist WindowList.Items[ItemIndex].hNicklist
Sample:
Add the following control to your form and rename them as specified* 1 x Button – Rename to “btnRefresh”
* 1 x Combobox – Rename to “cbPalWindows”
* 5 x Labels – Rename to “lblTitle”, “lblMainHwnd”, “lblIncomming”, “lblOutgoing”, “lblNicklist”Code:
{ Record for holding all our paltalk handles } type TWindowInfo = record sMain: array [0 .. 255] Of Char; hMain: HWND; hIncomming: HWND; hOutgoing: HWND; hNicklist: HWND; end; var Form1: TForm1; { List for holding all our records } WindowList: TList; implementation {$R *.dfm} { Set all record values to nil } function ZeroOutWinInfo(rWinInfo: TWindowInfo): TWindowInfo; begin FillChar(rWinInfo.sMain, SizeOf(rWinInfo.sMain), #0); rWinInfo.hMain := 0; rWinInfo.hIncomming := 0; rWinInfo.hOutgoing := 0; rWinInfo.hNicklist := 0; Result := rWinInfo; end; { Get Window title based on Hwnd } function GetWindowTitle(HWND: HWND): string; begin SetLength(Result, 255); SetLength(Result, GetWindowText(HWND, PChar(Result), 255)); end; { Get the Classname based on Hwnd } function GetWindowClass(HWND: HWND): string; begin SetLength(Result, 255); SetLength(Result, GetClassName(HWND, PChar(Result), 255)); end; { EnumChildWindows Callback } function EnumChildProc(aHwnd: HWND; Param: lParam): Boolean; stdcall; var WindowList: TList; WindowInfo: TWindowInfo; begin WindowList := TList(Param); if IsWindowVisible(aHwnd) = True then begin WindowInfo := WindowList.Items[Pred(WindowList.Count)]; case GetDlgCtrlID(aHwnd) of 202: begin WindowInfo.hIncomming := aHwnd; WindowList.Items[Pred(WindowList.Count)] := WindowInfo; end; 203: begin WindowInfo.hOutgoing := aHwnd; WindowList.Items[Pred(WindowList.Count)] := WindowInfo; end; 1789: begin WindowInfo.hNicklist := aHwnd; WindowList.Items[Pred(WindowList.Count)] := WindowInfo; end; end; end; Result := True; end; { EnumWindows Callback } function EnumWinProc(aHwnd: HWND; Param: lParam): Boolean; stdcall; var WindowList: TList; WindowInfo: TWindowInfo; begin WindowList := TList(Param); if GetWindowClass(aHwnd) = 'DlgGroupChat Window Class' then begin WindowInfo := ZeroOutWinInfo(WindowInfo); WindowInfo.hMain := aHwnd; GetWindowText(aHwnd, WindowInfo.sMain, 255); WindowList.Add(WindowInfo); EnumChildWindows(aHwnd, @EnumChildProc, Param); end; Result := True; end; { Update Combobox With Rooms } procedure TForm1.btnRefreshClick(Sender: TObject); var WindowInfo: TWindowInfo; begin WindowList.Clear; cbPalWindows.Clear; cbPalWindows.Text := 'Room Selector'; EnumWindows(@EnumWinProc, lParam(WindowList)); for WindowInfo in WindowList do cbPalWindows.Items.Add(WindowInfo.sMain); end; { On select display our handles for current window } procedure TForm1.cbPalWindowsSelect(Sender: TObject); begin lblTitle.Caption := 'Title: ' + WindowList.Items[cbPalWindows.ItemIndex].sMain; lblMainHwnd.Caption := 'MainHwnd: ' + IntToStr(WindowList.Items[cbPalWindows.ItemIndex].hMain); lblIncomming.Caption := 'IncommingHwnd: ' + IntToStr(WindowList.Items[cbPalWindows.ItemIndex].hIncomming); lblOutgoing.Caption := 'OutgoingHwnd: ' + IntToStr(WindowList.Items[cbPalWindows.ItemIndex].hOutgoing); lblNicklist.Caption := 'NicklistHwnd: ' + IntToStr(WindowList.Items[cbPalWindows.ItemIndex].hNicklist); end; { Create WindowList note: add "ReportMemoryLeaksOnShutdown := True;" to projects source } procedure TForm1.FormCreate(Sender: TObject); begin WindowList := TList.Create; end; { Make sure our data has been disposed of } procedure TForm1.FormDestroy(Sender: TObject); begin WindowList.Free; end;
Don’t forget to point your OnClick Event for “btnRefresh” and OnSelect Event for “cbPalWindows” to corresponding Events.
It should look similar to below image
ScreenShot:
Summary:
If you running newer versions of Delphi then I suggest adding “ReportMemoryLeaksOnShutdown := True” to the main project file, This is only a reporting tool to inform you of any leaking memory if you wish to build on to the above example.While this demo uses Delphi it can also be achieved in the .net language, By using generic list and defining your “Record” a.k.a “Structure” in .net, you can make handling of multiple rooms and handles much easier on your self, Even use this style to create your own classes which will do things with the handles, and the most important thing about this demo is the usage of EnumWindows and EnuChildWindows, we limit the amount of calling to these functions, First we only need to call it once to get all rooms handles and child handles to those rooms. If you wish to update the rooms(incase a room / pm window) is close we only need to validate the top level window(room /pm) and no need to call EnumWindows, because if the top level window handle is valid so must be its child handles(incoming, outgoing and nicklist) We can check this with against our List using a simple call to IsWindow API to see if it returns true or false(http://msdn.microsoft.com/en-us/library/windows/desktop/ms633528(v=vs.85).aspx)
Also the benefits of inheriting all of the functions and procedure of TList makes managing your rooms and handles a lot easier……
AddRange
IndexOf
LastIndexOf
Contains
Remove
Delete
DeleteRange
Extract
TrimExcess
Clear
Insert
InsertRange
Sort
BinarySearch
Reverse
Count
Items
CapacityAnd finally here is the Project source incase you still have problems or would like to compile and test this out straight away.
Download Source:July 14, 2013 at 3:53 pm #191056ChikeMemberBetter send WindowInfo to rnum child windows as reference and add it to the list after it was filled, will save few lines of code and all the copying of windowinfo.
Also return false in enumchildwindows when all handles are found, no point to keep iterating.July 14, 2013 at 6:06 pm #191055DepartureMemberyeah I actually do send WindowInfo as a LParam in the class I am making, How ever I do not return false, reason for this because Pm windows do not have a nicklist, So unless you have an idea how to validate all child handles have been accounted for with rooms and pm’s then it must continue.
I guess one way of validating is to check if the Incoming and outgoing handles has been filled because if they are filled we know Nicklist(if it exists) has already been enumerated, from the order I have seen using the debugger… Also if set a given value to each handle on creation of WindowInfo we can use nicklist as indicator if it is a pm window or a room window…
for example…
hIncoming:= 1337; hOutgoing:= 1337; hNickList:= 1337; EnumChild... if WindowInfo.hIncoming <> 1337 and WindowInfo.hOutgoing <> 1337 then Return False;
Your code..
if WindowInfo.hNickList = 1337 then
Pm window….
else
Room window….Anyway thanks for the suggestions, I hope there is something useful in the code for you to use your self.
//Edit
Added Return False to EnumChildProc and it does save some time, Only nano second I am guessing, but still…function EnumChildProc(aHwnd: Hwnd; Param: lParam): Boolean; stdcall; var WindowInfo: TPalWindow; begin WindowInfo := TPalWindow(Param); if ((WindowInfo.FhIncoming <> 1337) and (WindowInfo.FhOutgoing <> 1337)) then Result := False else Result := True; if IsWindowVisible(aHwnd) = True then begin case GetDlgCtrlID(aHwnd) of 202: begin WindowInfo.FhIncoming := aHwnd; end; 203: begin WindowInfo.FhOutgoing := aHwnd; end; 1789: begin WindowInfo.FhNickList := aHwnd; end; end; end; end;
July 14, 2013 at 8:42 pm #191054ChikeMemberIt probably saves more than nano second, but that’s not the point, its the right thing to do.
It’s like if you searched a value in an array and didn’t break the loop when you have found it.
And the if sould be at the end, or it can be at the 2 RicjEdir cases in shoeter version, at the beginning you already made unnesessary loop and gonna waste time looking for the cases.
Initializing result to true at the beginning worth nothing compared to all the work done before.July 14, 2013 at 9:34 pm #191053DepartureMemberFor some weird and strange reason if I set false it will always loop one more time. which means it get set back to true again at the begining of enumchildproc
July 14, 2013 at 9:44 pm #191052ChikeMember@Departure wrote:
For some weird and strange reason if I set false it will always loop one more time. which means it get set back to true again at the begining of enumchildproc
Huh?
Just set it true at the beginning and move the if after the case and make sure which result is set and see if it loops after you return false.July 14, 2013 at 9:49 pm #191051DepartureMemberyes this is how originally had at the very begining until i noticed the problem of it never ending the enumchildproc, Anyway problem solved I need to have the return type as a C Bool and not a Delphi Boolean, must be a language bug..
EnumchildProc looks like this now that Return type is Bool and not a Boolean, and this fixed enumarating one more time after setting to False.
function EnumChildProc(aHwnd: Hwnd; Param: lParam): Bool; stdcall; var WindowInfo: TPalWindow; begin Result := True; if IsWindowVisible(aHwnd) = True then begin WindowInfo := TPalWindow(Param); case GetDlgCtrlID(aHwnd) of 202: begin WindowInfo.FhIncoming := aHwnd; Result := False; end; 203: begin WindowInfo.FhOutgoing := aHwnd; end; 1789: begin WindowInfo.FhNickList := aHwnd; WindowInfo.FbIsRoom := True; end; end; end; end;
July 14, 2013 at 10:15 pm #191050ChikeMemberDepends what boolean is, it’s not a language bug.
In .NET there is a lot of marshaling done in Delphi probably not so you need to use the correct types.
Windows BOOL is an integer and any value other than zero considered to be true. If Delphi use just a byte for boolean than if there is non zero bytes in the result it most likely to happen.July 15, 2013 at 5:58 am #191049DepartureMemberThat’s correct it seems Delphi Boolean is a single byte, while BOOL is a Dword(aka integer), While both are 0 in a false statement its weird that the win32 EnumChildProc does not work on False, True in Delphi boolean = 1 while a C BOOL = -1 and the true statement for boolean in EnumChildProc works, One would think that Boolean true would infact be the problem. Also weird that on the second loop it will accept the delphi boolean type as false… I can only put it down to boolean being a single byte and something with the stack that move that single byte across on the second loop
EnumProc runs and is set to true Boolean = $01 Bool = $0000000001 next loop we set to false Boolean = $00 and BOOL = $FFFFFFFF, so if used a boolean in the register would look this first false statement $00000000 now it must do somingthing weird and have a negative effect on the register when setting the boolean false for a second time around for the stack to be $FFFFFFFF (-1 for an integer). One would Just call Return = False(Boolean) twice to fix the problem, it didn’t work… so something happens with in the function that the follow loop around if set to false again it will be $FFFFFFFF
http://blog.delphi-jedi.net/2008/09/25/bool-boolean-and-integer/
July 15, 2013 at 2:35 pm #191048ChikeMemberYou got it a bit wrong, as shown in the link, false is always zero, and EnumChildProc does not check for true, it chack for non-zero value so as long as false is zero it’s all good.
You can just make your own type to use with it for Windows APIs that return BOOL or take it as a parameter, and not worry about any werdness anymore.July 15, 2013 at 5:27 pm #191047DepartureMemberIm not too worried about it, as Delphi has C types also which are just references to the standard type, for example Delphi’s BOOL is a type LongBoolean and a LongBoolean is a type Dword. When I program I don’t normally think to much on named types, I see most things as either Dwords or bytes as that’s what it finally comes down to at the end in the stack & register while debugging with olly. Saying that I wasn’t aware that Delphi’s Boolean was a single byte, Infact it was by chance I decide to find out the size of that type, as it was clear the true / false value was doing something strange.
July 21, 2013 at 6:22 pm #191046ChiNaAdministratorBro DEP, Really love your Widget, Great explanations. I like your hwndspy a lot too and it has really helped me to get through a lot of hard work searching for windows and handles!
January 30, 2014 at 2:41 am #191045JiiixMemberThanks Dep.
sorry but i’m new in delphi 🙂
if i need to GetRoomText
this code is ok ?
{ PalListOnRoom Callback } function RoomText(aHwnd: HWND; Param: lParam): Boolean; stdcall; var WindowList: TList; WindowInfo: TWindowInfo; begin WindowList := TList(Param); if GetWindowClass(aHwnd) = 'RichEdit20W' then begin WindowInfo := ZeroOutWinInfo(WindowInfo); WindowInfo.hMain := aHwnd; GetWindowText(aHwnd, WindowInfo.sMain, 255); WindowList.Add(WindowInfo); EnumChildWindows(aHwnd, @EnumChildProc, Param); end; Result := True; end;
because it try it but no help
Thanks
-
AuthorPosts
Related
- You must be logged in to reply to this topic.