[Delphi] Get All handles & Management

This topic contains 12 replies, has 4 voices, and was last updated by  Jiiix 3 weeks, 4 days ago.

  • Author
    Posts
    1 Star2 Stars3 Stars4 Stars5 Stars Rating: 5.00 - 1 votes
  • #15294
    Administrator
    Posts: 1011

    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:
    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
    Capacity

    And finally here is the Project source incase you still have problems or would like to compile and test this out straight away.
    Download Source:

    #38679
    Administrator
    Posts: 1093

    Better 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.

    #38680
    Administrator
    Posts: 1011

    yeah 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;
    #38677
    Administrator
    Posts: 1093

    It 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.

    #38682
    Administrator
    Posts: 1011

    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

    #38683
    Administrator
    Posts: 1093

    @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.

    #38684
    Administrator
    Posts: 1011

    yes 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;
    #38678
    Administrator
    Posts: 1093

    Depends 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.

    #38685
    Administrator
    Posts: 1011

    That’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/

    #38687
    Administrator
    Posts: 1093

    You 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.

    #38686
    Administrator
    Posts: 1011

    Im 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.

    #38681
    Administrator
    Posts: 442

    Bro 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!

    #38688
    Member
    Posts: 37

    Thanks 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

You must be logged in to reply to this topic.