News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Another listbox problem

Started by NoCforMe, December 17, 2011, 07:56:36 PM

Previous topic - Next topic

NoCforMe

Wrote a little app (attached) that shows strings in a file. Works, but has problems.

I used a listbox control to display the strings. I think I realize now that this is the wrong control for the job, for reasons given below. Which is too bad, because the listbox makes a couple of things reeeeally easy for me, the programmer:


  • It handles all the nasty memory-allocation and pointer management stuff
  • I can easily add strings one at a time, rather than having to handle the entire block of text as one chunk, as I'd have to do with an edit control
  • Sorting the strings is a piece of cake (just add the LBS_SORT style)
Here are the problems:
1. The listbox displays correctly after I fill it with strings (this is done in my FindStrings() subroutine). But the display is very flaky; if I click in the listbox, the text disappears. I can make it reappear (sometimes) by playing with the scrollbar, if there's more than a window-full of text there. But it's clearly not working properly.

2. The listbox is dog-sloooooooooow if there are a lot of strings (for example, I tried loading the .lst file from this project; it took a couple of minutes to load). Not efficient at all.

(This is under Win2K. Your results with another OS flavor may vary.)

I tried subclassing the list box, but that only made matters worse. (Code is there but commented out.) I tried handling the WM_PAINT message in the window proc, and I think what was happening is that I was just generating more messages recursively, or something. Anyhow, it didn't work at all.

So I have a couple questions (kind of risky here: I find that at most, one of my questions gets answered while the others are ignored, but whatever):

1. Knowing that a listbox isn't the ideal control here, I would nonetheless like to learn how to use it properly. What am I doing wrong here (regarding the incorrect display problem, not the inefficiency one)?

2. I guess the other candidate for a control is an edit box. However, this means that I have to:

  • Handle memory allocation and pointers to my strings
  • Create the text in the edit control as one big "chunk", rather than being able to add strings one at a time
  • Sort the list (not hard to do, but it's nice when Big Daddy Bill does this for you!)
So is there anything besides an edit box that will do this job?

When adding text to an edit box, does it have to be done all at once (using WM_SETTEXT), or is there some way to add text incrementally to an edit box, the way one does with a listbox?

Oh, and another little problem: Notice that the listbox doesn't qiuite occupy the full height of the parent window's client area. It stops a little above the bottom. I can't figure out why this is. I tried two methods of calculating the height to make the listbox when resizing it:

  • Use the new window sizes passed in through lParam
  • Use GetClientRect() to get the new size of the parent's client area
Both give the same (wrong) results. I tried adding the height of the menu strip to the starting point, but that was wrong as well. This is annoying.

Thanks in advance to all helpful answers!


NoCforMe

OK, that's interesting. So I take it by this you're saying "yes, listboxes can be used efficiently this way, but they have to be tweaked"?

Another thought: maybe neither listboxes nor edit boxes are the way to go, since all I really want to do is display text: I could just put the text into the client area using DrawText() or ExtTextOut(). (BTW, which of these would you recommend?)

Except that now I'd have to handle scrolling, which is nicely taken care of by both edit boxes and list boxes ...

The other nice thing about an edit box is that the user can select text from it to copy. This could be done by drawing your own text but would require a lot more code. (Does anyone have some examples of selecting text from application-drawn text? I don't know how to do this yet.)

Sorry about all the cascading questions, but that's the nature of the beast. It'd be nice to get answers to all of them ...

jj2007

What is the issue here? Just opening a file, reading in strings, sorting them, displaying them? Sort the file and read it into a RichEdit...

include \masm32\MasmBasic\MasmBasic.inc   ; download
   Init[/b]
   Recall "\Masm32\include\Windows.inc", L$()
   QSort L$()
   Store "WinIncSorted.inc", L$()
   Inkey Str$("%i strings sorted", eax)
   Exit
end start

MichaelW

In my quick test, done entirely in the WM_INITDIALOG handler before the list was displayed so redraws were not a factor, about half of the time spent adding the strings was sorting time.

;==============================================================================
include \masm32\include\masm32rt.inc
;==============================================================================
IDC_LB equ 1000
;==============================================================================
    .data
        hInstance dd 0
        hwndLB    dd 0
        buffer    db 100 dup(0)
    .code
;==============================================================================

;==============================================================================

DialogProc proc hwndDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    SWITCH uMsg

        CASE WM_INITDIALOG

            invoke GetDlgItem, hwndDlg, IDC_LB
            mov hwndLB, eax

            invoke GetTickCount
            push eax

            push ebx
            push edi
            push esi
            mov ebx, 50000

            .WHILE ebx
                invoke RtlZeroMemory, ADDR buffer, SIZEOF buffer
                invoke nrandom, 50
                add eax, 11
                mov esi, eax
                mov edi, OFFSET buffer
                .WHILE esi
                    invoke nrandom, 'Z' - 'A' + 1
                    add eax, 'A'
                    stosb
                    dec esi
                .ENDW
                invoke SendMessage, hwndLB, LB_ADDSTRING, 0, ADDR buffer
                dec ebx
            .ENDW

            pop esi
            pop edi
            pop ebx

            invoke GetTickCount
            pop edx
            sub eax, edx
            invoke SetWindowText, hwndDlg, str$(eax)

        CASE WM_SIZE

            mov eax, lParam
            mov edx, eax
            and eax, 0ffffh
            shr edx, 16
            invoke MoveWindow, hwndLB, 0, 0, eax, edx, TRUE

        CASE WM_COMMAND

            SWITCH wParam

                CASE IDCANCEL

                    invoke EndDialog, hwndDlg, NULL

            ENDSW

        CASE WM_CLOSE

            invoke EndDialog, hwndDlg, NULL

    ENDSW

    return 0

DialogProc endp

;==============================================================================
start:
;==============================================================================

    invoke GetModuleHandle, NULL
    mov hInstance, eax

    Dialog "Test", \
           "MS Sans Serif",10, \
            WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \
            1, \
            0,0,300,300, \
            1024

    DlgList LBS_STANDARD,0,0,0,0,IDC_LB  ;~29500 ticks, list sorted
    ;DlgList 0,0,0,0,0,IDC_LB              ;~15300 ticks, list not sorted

    CallModalDialog hInstance, 0, DialogProc, NULL

    exit

;==============================================================================
end start


eschew obfuscation

NoCforMe

Quote from: jj2007 on December 17, 2011, 09:28:10 PM
What is the issue here?

Did you look at my app? Listbox misbehaves , that's the issue.

QuoteJust opening a file, reading in strings, sorting them, displaying them? Sort the file and read it into a RichEdit...
[snip code]

Um, not quite. My program extracts strings from any kind of file, including executables, object files, etc. (that's its intended purpose).

Anyhow, let me try things this way: I'll start with my first question: Why does my listbox (mis)behave the way it does?

What happens is when I open a file, it fills it with strings from the file. Fine. But if you click anywhere in the listbox, the strings (and the scrollbar if there is one) disappears! If you play around with the scrollbar a few times (assuming there's more text than will fit in the window), it then starts behaving normally. Except that if you open another file, it shows the strings from that file correctly, but then if you make the text disappear and then reappear as described above, you get the strings ... from the original file!!!?!! (This is even after I added sending the LB_RESETCONTENT message to clear it out each time I process the strings from a file.)

I'm clearly doing something wrong here, and I'd like to know what it is so I don't do it anymore.

Regarding you MasmBasic stuff, don't take this the wrong way, but I'm not interested in using it. I appreciate how much effort you put into that package, and how much praise you deserve for such a useful tool. I might use it sometime in the future, but I'm just not there yet.

qWord

check your WM_COMMAND-handler!
You should use .if/.elseif/.else - this constructs increase the readability and help to avoid such nasty bugs  :wink
FPU in a trice: SmplMath
It's that simple!

Gunner

When you "click" on the listbox, it sends a notification to your parent window, you don't handle that, you only handle if a menu item was clicked.. fine but what happens for all other commands?  Your WM_COMMAND handler "falls through" to your WM_CREATE Handler!!!  So eack click you are creating a NEW listbox..  So your command handler should look like:
do_command:
   MOV     AX, WORD PTR wParam
   CMP     AX, $menuOpenFile
   JE      do_openfile
   CMP     AX, $menuExit
   JE      do_close
   xor     eax, eax ; <<<<<<<  Needed
   ret              ; <<<<<<< This too!

I agree qWord, but I think he feels it is TOO high level....  Produces almost the same code BUT with less headaches.
~Rob (Gunner)
- IE Zone Editor
- Gunners File Type Editor
http://www.gunnerinc.com

NoCforMe

Thank you, Gunner, that was exactly the problem. Doh!

Qword, if you're going to tell me there's a problem with my WM_COMMAND code, perhaps you could be a little more specific next time.

And thanks, but I don't like using .if ... .else constructs in assembly language.

Different strokes for different folks, dontcha know...

And Gunner, I actually added this:


JMP dodefault


which goes to this:


dodefault:
INVOKE DefWindowProc, hWin, uMsg, wParam, lParam
RET


because you're supposed to use the default handler for anything you don't handle yourself, correct?

NoCforMe

OK, now we're getting somewhere. Fixed my problem with the listbox control.

Question no. 2: What is the most appropriate control to use here? Keep in mind that large files may produce lots of strings, and that without doing any speeding-up, the sorted listbox seems to be much too slow for this application.

Should I instead use

  • an edit control?
  • drawing text directly into the client area?
  • something else?
Much thanks for the help so far.

Gunner

I believe listboxes have limits unless you owner draw.  You could also use a listview control with 1 column with the header hidden, has more features too, and you could owner draw that too, or edit control... your choice
QuoteDifferent strokes for different folks, dontcha know...
:bg
~Rob (Gunner)
- IE Zone Editor
- Gunners File Type Editor
http://www.gunnerinc.com

jj2007

Quote from: NoCforMe on December 19, 2011, 12:02:37 AM
Qword, if you're going to tell me there's a problem with my WM_COMMAND code, perhaps you could be a little more specific next time.

He took the pains of deciphering your very unorthodox setup and pointed exactly at the (obvious) problem. A simple "thank you" would have been enough.

NoCforMe

So jj, I implemented your "speed-up", holding off redrawing the listbox control and using LB_INITSTORAGE to allocate memory. It didn't make much, if any, difference. Which tells me that most of the time is being spent sorting strings, not displaying them.

Of course, my computer is probably considerably slower than yours. The program ate up about 90-95% of CPU on my system. (W2K Pro.)

I wonder if you'd care to try this app on your computer? I tested it on its own .lst file (with no .NOLISTs, so it included the entire windows.inc file). Took a couple minutes before I saw any text on my computer.

Next question: What's with that little bit of space at the bottom of the window between the window edge and the bottom of the scrollbar? That ain't right! Can't seem to size the listbox correctly within the main window client area.

jj2007

Quote from: NoCforMe on December 19, 2011, 01:02:10 AM
I wonder if you'd care to try this app on your computer?

Attach it, and we'll see :bg

NoCforMe

I'll take you up on your offer, jj.

Odd behavior: if you resize the window, the size of the listbox doesn't follow it smoothly; it "jumps". Try resizing the window vertically and you'll see what I mean.

I added writing the strings to a file (the write routine also removes duplicate strings).

Here's the resizing code (alternate method commented out, behaves exactly the same):


INVOKE GetClientRect, hWin, ADDR gpRect
; MOVZX EAX, WORD PTR lParam
; MOV newWidth, EAX
; MOVZX EAX, WORD PTR lParam + 2
; MOV newHeight, EAX
INVOKE MoveWindow, StringsWinHandle, 0, 0, gpRect.right, gpRect.bottom, TRUE
; INVOKE MoveWindow, StringsWinHandle, 0, 0, newWidth, newHeight, TRUE
XOR EAX, EAX
RET


This same method seems to work fine in every other app of mine.