News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Creating Windows controls: the initialization problem

Started by NoCforMe, January 02, 2023, 08:57:05 AM

Previous topic - Next topic

NoCforMe

I'm on my second homemade Windows control. They're fun to create, and certainly within the capabilities of a lot of us here.

One small problem, though: unlike the controls that Windows provides and knows about, you can't just use these in your program without first explicitly initializing them. For example, let's say you put your custom control in a dialog, and provide the class name to the dialog. (I do this by creating a memory template for the dialog, where the class name gets put in as a Unicode string.) Fine: the problem is that the entire dialog will fail because the dialog manager can't create that control because it hasn't been registered.

That's where the initialization function comes in: before the dialog is created, it registers the control class with RegisterClassEx(). Now the dialog can be created, or you can create the control yourself with CreateWindowEx(). And of course, the initialization function can do other stuff to prepare the control, although it would be neater if that stuff were inside the window proc for the control.

It's not that big a deal, but if you forget to put in your initialization subroutine you're screwed. I'm just wondering if there's any way around this problem. Somehow you'd have to make Windows aware of the existence of your class without explicitly using RegisterClassEx(). I can't think of how to do that.

Until then I guess I'll just have to live with that extra line of code in my programs ...
Assembly language programming should be fun. That's why I do it.

jj2007

The RichEdit initialises itself when you invoke LoadLibrary, "Richxx.dll". Check DLL_PROCESS_ATTACH.

zedd151

Cannot be done in the WM_INITDIALOG handler? Unless I missed some other point you were making.

NoCforMe

OK, but that means I'd have to use LoadLibrary() for my control. (Plus I'd have to make it into a DLL, something I've never done.) Same difference, isn't it? In fact, my initialization code is actually simpler:


INVOKE InitBarberPole, InstanceHandle


Doesn't need the name of any files, just the name of the function.

Actually, your reply kind of confirms my suspicion that there really is no way to do what I was asking about. The control has to be registered by somebody at some point, right?
Assembly language programming should be fun. That's why I do it.

NoCforMe

Quote from: zedd151 on January 02, 2023, 09:04:07 AM
Cannot be done in the WM_INITDIALOG handler? Unless I missed some other point you were making.

No, and maybe it isn't obvious. The problem is that it will never get to WM_INTIDIALOG, because when the dialog manager tries to create the control (using CreateWindowEx() or whatever it does internally), that call will fail because the custom control class hasn't been registered. You have to register a class before using it, if it's a class that Windows doesn't know about. (This doesn't apply to the "standard" control classes like static, button, edit, etc.; Windows has already registered these internally.)

It's kind of a classic case of trying to put the cart before the horse, programming-wise.

It took me a while to wrap my head around this concept, but it's pretty clear now. Does that make sense?
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on January 02, 2023, 09:08:06 AM
Does that make sense?
Ok, any reason to not be able to register the class before the dialog box gets called? I have never attempted to use any 'custom' (registered) control in a dialog box, so I don't know how involved the process is. If the class name gets registered successfully prior to the dialog box call, then the class should be valid and registered when the dialog box needs to use it? I obviously do not understand. Could you post a sample of what you have tried, especially how and when it fails? That would help to understand better what you are attempting.

One would assume that registering a class prior to dialog creation should work. Easest way around it would seem to use a normal window instead of a dialog box perhaps??? (signed clueless in Louisiana)

NoCforMe

No, that's just the point: registering the class (outside of the dialog call) does work:


INVOKE InitBarberPole, InstanceHandle


contains RegisterClassEx() for the custom control class. Then when I call


INVOKE DialogBoxIndirectParam, InstanceHandle, OFFSET TheDialog, NULL,
OFFSET TheDialogProc, 0


to create the dialog, the dialog manager can create the control because its class has been registered.

I'm just being picky here; it's kind of inelegant to have to call that initialization routine before creating the dialog, that's all. And I'm thinking of other people here who might actually use my custom controls, how they'll have to call that routine as well, which is kind of an unfamiliar thing if you're used to working with standard Windows controls. No big deal.

Oh, and even with a "normal window" (i.e., one created manually using CreateWindowEx() ), you still have to register the class before creating it. You do this all the time with programs that use a main window instead of a dialog box; first RegisterClassEx(), then CreateWindowEx().
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on January 02, 2023, 09:45:19 AMit's kind of inelegant to have to call that initialization routine before creating the dialog, that's all

Yes, but that's exactly the case of the RichEdit control. And of several others btw - they require a call to InitCommonControlsEx. In short: you are wasting precious time on problems that do not exist :tongue:

QuoteThe set of bit flags that indicate which common control classes will be loaded from the DLL. This can be a combination of the following values.

Value   Meaning
ICC_ANIMATE_CLASS
0x00000080
Load animate control class.
ICC_BAR_CLASSES
0x00000004
Load toolbar, status bar, trackbar, and tooltip control classes.
ICC_COOL_CLASSES
0x00000400
Load rebar control class.
ICC_DATE_CLASSES
0x00000100
Load date and time picker control class.
ICC_HOTKEY_CLASS
0x00000040
Load hot key control class.
ICC_INTERNET_CLASSES
0x00000800
Load IP address class.
ICC_LINK_CLASS
0x00008000
Load a hyperlink control class.
ICC_LISTVIEW_CLASSES
0x00000001
Load list-view and header control classes.
ICC_NATIVEFNTCTL_CLASS
0x00002000
Load a native font control class.
ICC_PAGESCROLLER_CLASS
0x00001000
Load pager control class.
ICC_PROGRESS_CLASS
0x00000020
Load progress bar control class.
ICC_STANDARD_CLASSES
0x00004000
Load one of the intrinsic User32 control classes. The user controls include button, edit, static, listbox, combobox, and scroll bar.
ICC_TAB_CLASSES
0x00000008
Load tab and tooltip control classes.
ICC_TREEVIEW_CLASSES
0x00000002
Load tree-view and tooltip control classes.
ICC_UPDOWN_CLASS
0x00000010
Load up-down control class.
ICC_USEREX_CLASSES
0x00000200
Load ComboBoxEx class.
ICC_WIN95_CLASSES
0x000000FF
Load animate control, header, hot key, list-view, progress bar, status bar, tab, tooltip, toolbar, trackbar, tree-view, and up-down control classes.

fearless

You can register your custom control outside of the dialog procedure if your placing the control in the dialog via resource editor for the dialog manager to use it, or create the control and manually place it on the dialog after it has initialized (using WM_INITDIALOG)

Thats how I handle the custom controls I create. It gives the developer flexibility to either use resource & dialog or manually. But yes, a function to register the class before use in the dialog is required if that is the route you are going. Then the dialog manager will know the class exists when it loads the dialog and any child controls as defined in the resource, and that control's class will point to the control's main window procedure.


After that you can use custom messages to handle specific features of the custom control, or custom functions, or both.


For example (using the ModernUI_Button control as example) I have a MUIButtonRegister function the developer can use to register the class before use in a dialog (usually I place this at start of program alongside the InitCommonControls call. https://github.com/mrfearless/ModernUI/blob/master/Controls/ModernUI_Button/ModernUI_Button.asm#L349

Or the user can use a MUIButtonCreate function (which is a wrapper for registering the class (via MUIButtonRegister) and calling CreateWindowEx): https://github.com/mrfearless/ModernUI/blob/master/Controls/ModernUI_Button/ModernUI_Button.asm#L384

Once the control is created (via either method) then the _MUI_ButtonWndProc window procedure is called: https://github.com/mrfearless/ModernUI/blob/master/Controls/ModernUI_Button/ModernUI_Button.asm#L442


zedd151

#9
Quote from: NoCforMe on January 02, 2023, 09:45:19 AM
Oh, and even with a "normal window" (i.e., one created manually using CreateWindowEx() ), you still have to register the class before creating it. You do this all the time with programs that use a main window instead of a dialog box; first RegisterClassEx(), then CreateWindowEx().
That was exactly my point. Anyone else using it would be familiar with that scenario, so using a dialog box as you intend instead, will not be so unfamiliar. At any rate fearless has posted links to some examples for you in the meantime, maybe you can get some hints there on how best to achieve your goal.
I wish you the best of luck. When ready, I'm here to help test/debug your "BarberPole ProgressBar".   :biggrin: 

NoCforMe

Quote from: jj2007 on January 02, 2023, 10:32:12 AM
Quote from: NoCforMe on January 02, 2023, 09:45:19 AMit's kind of inelegant to have to call that initialization routine before creating the dialog, that's all

Yes, but that's exactly the case of the RichEdit control. And of several others btw - they require a call to InitCommonControlsEx. In short: you are wasting precious time on problems that do not exist :tongue:

Probably the best answer in this thread so far. If it ain't broke don't fix it.
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on January 02, 2023, 02:13:22 PM
Probably the best answer in this thread so far. If it ain't broke don't fix it.
So does that mean your code is working now, even if it has some non elegant coding? The original post sounded like something was broken. Confused.  :undecided:

NoCforMe

It has always worked. My previous custom control project, mcListbox (which is used in my TheBox) works this way. I just thought it was, well, a little clunky.

I've decided that what JJ said is good advice; don't sweat it. Calling one routine before creating a control is a small price to pay. So I'm good with it.
Assembly language programming should be fun. That's why I do it.