News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Adventures in message direction and superclassing

Started by NoCforMe, March 12, 2024, 09:16:31 AM

Previous topic - Next topic

NoCforMe

The latest adventures in coding my assembly-language editor, EdAsm. I'm posting it here since it involves quite a bit of Win32-specific issues, primarily superclassing and message handling/inter-window communication.

The problem: the editor has two types of edit windows, one the main editing window, and a set of "extra file view" windows for peeking at the contents of other files. The main window has text search and replace capability, but there was no way to search text in the "extra file" windows. I wanted to add that.

Problem 1: The main edit window is a RichEdit control, but the extra-view windows used a regular edit control. So I would have to implement a whole separate find-text mechanism for those windows. Ugh.

Solution 1: Make the extra-view windows RichEdits as well. Done. However, this leads to

Problem 2: Both sets of windows are subclassed, for various reasons. So that won't work, because now the subclassed procs will be mixed up.

Solution 2: Superclassing to the rescue! Since I have 2 types of windows of the same class (RichEdit), superclassing is the way to create 2 new classes, each based on the underlying class but each having their own behavior (because each has its own superclass proc). It's like subclassing but just a little bit different; basically, each superclass has a procedure that defines its behavior but yields to the base class procedure for most of its processing.

This allows me to have the main edit control capture keystrokes for pop-up menus and to do some other special processing, and the extra-file edit controls do their special thing. More on this below.

However:
Problem 3: The whole point of this exercise is to add search capability to the extra-file edit controls. But because I'm using a hotkey (Ctrl-F) to initiate text searches, I can't get the extra-file windows to capture this keystroke; it always goes to my main window proc (that's the way hotkeys work in Win32). I had started a whole thread on this elsewhere on this site, with no apparent solution.

Well;
Solution 3: Do nothing. Don't even try to capture this key combination. Instead, think about it: you now have two classes of windows of the same type (RichEdit), and a text-finding routine that works with RichEdit. So just use that for the extra-file edit controls. All that needs doing is to change the control handle used by the text-finding routine. For the main window, set it to the main edit control. For extra-file windows, set it to the particular extra-file edit control. Fortunately I'd already stored those handles (there can be multiple extra-file windows) in an array of structures.

So to summarize, you can use superclassing to create any number of new classes, all of which are based on an existing class. Very useful!

So that's what I did, and it works fine. The diagram below shows how it all fits together.

One little detail: where the extra-file edit controls were previously defined as read-only, that wouldn't work, since I needed the control to be able to accept focus and accept keystrokes. However, it was easy to fix that up; the superclass proc for the extra-file edit controls simply "eats" all keystrokes:
;====================================
; EVeditSuperclassProc()
;
; Proc for superclassed edit controls in extra-file viewers.
; The only reason for this code's existence is to deny the
; extra-view edit control any keystrokes (= read-only).
;====================================

EVeditSuperclassProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD

MOV EAX, uMsg
CMP EAX, WM_CHAR
JE do_char
CMP EAX, WM_KEYDOWN
JE do_char

oldprc: INVOKE CallWindowProc, REbaseProcAddr, hWin, uMsg, wParam, lParam
RET

;***** Eat all keystrokes!  *****
do_char:
XOR EAX, EAX
RET

EVeditSuperclassProc ENDP
So far as message handling goes, it turned out that there were 2 messages that allowed me to determine when an extra-file window becomes active:
  • WM_ACTIVATE tells me when the user clicks on the extra-file container window, not the edit control
  • WM_PARENTNOTIFY tells me when the user clicks on the extra-file edit control
I use these messages to 1) set focus to the extra-file edit control, and 2) set the handle (FindEditHandle) used the text-finding routine to the handle of the extra-file edit control. I also do the above at the time I create a new extra-file view window, so the user can immediately use Ctrl-F to find text in that window without having to click on it.Seems to work fine.
Assembly language programming should be fun. That's why I do it.

NoCforMe

For those who want to try superclassing, here's how to do it:
Recipe for Tasty Superclassed Windows
-------------------------------------

BaseClassWindwProc DD ?

BaseClassName DB "static", 0
SuperclassName DB "SuperDuperClass", 0

LOCAL wc:WNDCLASSEX

; Get base class info:
MOV wc.cbSize, SIZEOF WNDCLASSEX
INVOKE GetClassInfoEx, hInst, OFFSET BaseClassName, ADDR wc

; Replace the base class name in the WNDCLASS structure with our class name:
MOV wc.lpszClassName, OFFSET SuperclassName

; Copy the window proc. address out of the base class to our code:
MOV EAX, wc.lpfnWndProc
MOV BaseClassWindowProc, EAX

; Plug in our window proc. address (see below for this proc.):
MOV wc.lpfnWndProc, OFFSET SuperclassProc

; Fill in other stuff in WNDCLASSEX:
; Be aware that GetClassInfoEx() does NOT fill in the following members:
   o lpszMenuName
   o lpszClassName
   o hInstance

MOV EAX, brush
MOV wc.hbrBackground, EAX
INVOKE LoadCursor, NULL, IDC_ARROW
MOV wc.hCursor, EAX
MOV EAX, InstanceHandle ;Global copy of instance handle.
MOV wc.hInstance, EAX
MOV wc.hIcon, NULL
MOV wc.hIconSm, NULL
MOV wc.lpszMenuName, NULL

; Register the new class:
INVOKE RegisterClassEx, ADDR wc

; Create a window procedure for the new class:

SuperclassProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD

; Be sure to pass necessary messages to the the base class window proc:
INVOKE CallWindowProc, BaseClassWindowProc, hWin, uMsg, wParam, lParam
RET
Assembly language programming should be fun. That's why I do it.

NoCforMe

#2
And a word about Unicode:

Kinda strange: before I made all these changes, my main edit control was a Unicode RichEdit control (created with CreateWindowExW() ). However, this program doesn't use Unicode, just plain ASCII, so all my SendMessage() calls were the ASCII ("A") variety, and everything worked fine.

However, as soon as I introduced my superclassed controls, the program broke. My main edit window was filled with question-mark characters. Reading a 280,000-byte file resulted in a ... 1-byte file! Whoa.

Turned out that I needed to change all my SendMessage()s to SendMessgeW()s. I'm still not sure why this happened; at the end of the day, I was still accessing the RichEdit controls through the same entry point (the "base class" proc address). But somehow adding the superclass changed this. So I guess the moral of the story is that if you're dealing with a Unicode-aware control, you might ought to use the "W" version of functions. (This applied to GetWindowText() and SetWindowText() as well, which I use to fill the edit control and extract text from it).

@JJ: You have lots of experience with both Unicode and RichEdit; can you think of why I had to make these changes?

My theory is that since I superclassed these controls, the functions I used to manipulate them (like SendMessage() ) somehow became aware that the control was a Unicode one and failed because of the mismatch. ???
Assembly language programming should be fun. That's why I do it.

sudoku

Have you tried using an MDI interface?
Whichever window is active would be where the search is performed. I bring up MDI since you may have several windows open at any time, no? Of course MDI code could also be a bit hairy to work with.  :tongue:
Just a suggestion...
:undecided:

sinsi

#4
If you originally created your main window using RegisterClassExW, Windows will do some trickery with SendMessageA, changing your ansi string to unicode (and changing any returned unicode string to ansi).

Knowing Windows, there are probably a few places where this occurs (CreateWindowExW perhaps?)

NoCforMe

Quote from: sudoku on March 13, 2024, 12:55:59 AMHave you tried using an MDI interface?
I've essentially rolled my own MDI-like mechanism, so no need to use the "official" API. Pretty simple, really: I have an array of structures that hold info for each window, handle, etc., and a routine to locate a window by its handle, so it works just fine for me.

Reasons NOT to use MDI:
1. From Micro$oft Learn:
Quote[Many new and intermediate users find it difficult to learn to use MDI applications. Therefore, you should consider other models for your user interface. However, you can use MDI for applications which do not easily fit into an existing model.]

2. Another part of Micro$oft Learn:
QuoteEach document in an multiple-document interface (MDI) application is displayed in a separate child window within the client area of the application's main window.

That wouldn't work in my case: my "extra file view" windows aren't child windows but pop-ups that can go anywhere on the desktop. Mobility!
Assembly language programming should be fun. That's why I do it.