i tried "DarkMode_Explorer":
Win10: Attachment 195969
Win11: Attachment 195970
Supporting DarkMode with your app is really an annoying task...
Printable View
i tried "DarkMode_Explorer":
Win10: Attachment 195969
Win11: Attachment 195970
Supporting DarkMode with your app is really an annoying task...
Supporting DarkMode is by itself a pain in the ass. I think it’s not so bad with Krool’s VBCCR.
FrameW is easier to use thanks to the forecolor. And just keep in mind that MonthView is bigger in DarkMode.
Here are my tests :
Attachment 195971 Attachment 195972
Attachment 195973
Crapahute,
the StatusBar you could do darkmode with "DarkMode::ExplorerStatusBar". However, when you maximize and minimize the style is lost as VBCCR recreates the control.
I check if I can avoid such a recreation. According to reactos the status bar checks for WS_MAXIMIZE on the parent. And the parent of the status bar is the UserControl.
My quick check suggests it can be improved by applying WS_MAXIMIZE on the UserControl when the form is maximized. Will check further and do a fix, if possible.
Update released.
Improvement in the StatusBar control when the Form is maximized.
The UserControl will get the WS_MAXIMIZE style instead of re-creating the StatusBar control.
This allows to apply a different window theme and to not loose it.
The status bar control will not draw the size grip when its parent has the WS_MAXIMIZE style set.
When set on a child control, it will occupy the entire client area of its parent.
But only when set at creation. Therefore it is safe to set the WS_MAXIMIZE style at run-time.
Update released.
Finally the ImageCombo has it's BackColor and ForeColor property to behave like a normal ComboBox.
WM_CTLCOLOREDIT, WM_CTLCOLORSTATIC and WM_CTLCOLORLISTBOX are handled now and the UserControl.BackColor/ForeColor is set. (for the edges on some themes)
In addition and that is important for the drop-down list.
It installs an API hook during WM_DRAWITEM (ODT_COMBOBOX) for the GetSysColor function (user32.dll) and swaps COLOR_WINDOW for the BackColor and COLOR_WINDOWTEXT for the ForeColor.
Thanks to API Hooking basic working example there is also a x64 variant so it will work on twinBASIC for the future.
I updated to v1.8.101 but the ImageCombo still have no BackColor and ForeColor property.
Sorry, im a little bit confused about the version history, the OCX & control demo updates.
I seems i need to compile the OCX myself to get all new features:
1. Does the source code of the OCX contain all new features or only bug fixes?
2. Does the source code of the VBCCR control demo contain all new features? If yes: should i only copy the demo control source folders to the OCX folder and recompile the OCX?
I answer my question above myself and i have a new question:
yes, the VBCCR control demo contain all new features.
But:
You moved a large code block (Public Enum...) from Isublcass.cls to ComCtlsBase.bas in the Control Demo project which are used by many control properties.
Compiling this new source code leads to an incompatibility with any vb-project using the current OCX.
Is this the reason you dont offer new features with the compiled OCX? If so, how about releasing v1.9?
Mith, yes. There is no official v1.9 OCX yet.
I can't release a new OCX yet when there are new features coming. Normally I release a new OCX every one or two years.
The v1.9 would be probably the last version compiled in vb6 before switching to a twinBASIC compiled variant. Hopefully then stable and mature. (so that v2.0 would be then ideally x64 and x86)
EDIT: If the BackColor/ForeColor for the ImageCombo is really important for you. Then I can see to update the v1.8 OCX as that would cause only a typelib version jump but still be binary compatible. (only need to re-register)
Update released.
The FrameW control will now react upon WM_THEMECHANGED.
Before the new theme was not changed at all. (e.g. "DarkMode_DarkTheme")
The Item in the ItemDraw event can now be -1 in the ComboBoxW and VirtualCombo control.
This allows to draw a background and focus rect when the list index is -1.
Caution! You might need to adapt your ItemDraw handler as this change could cause crashes otherwise.
The OCX (1.8) was also updated with this change.
Also the FontCombo control (in-built owner draw) got fixed to draw a background and focus rect when the list index is -1.
Nice progress! Another issue with VB forms in dark mode is that on Show (not first time but already hidden one) the background flickers in vbWindowBackground which is almost invisible in light mode but mighty annoying in dark mode.
@Krool
Sorry for my delayed reaction.Quote:
is this still relevant ?
You want the images to be drawn without selected state for the combo box and the dropdown list ?
I worked around the disturbing effect:
The ComboBox sits in a PictureBox, and this PictureBox gets the focus on Private Sub CloseUp.
That's good enough in my situation.
Update released.
I included the AllowImageHighlight property in the ImageCombo control. (defaults to True)
When set to False a focused item image is not allowed anymore to be drawn highlighted.
When you don't need it anymore it's ok. Maybe somebody else will find it useful.
Thx Krool! This new property is very useful for the ImageCombo-Control!
I just wish i could test and use the new feature now...
Do you think i should create a new OCX from the Demo-Project-Source and replace all my controls in all vb-projects to get access to the new features?
Do you have a "roadmap" when you release OCX v1.9 in the future?
Thanks a lot!
Thanks Krool! It works! No more need for an extra picturebox to show the selected combobox image!
One thing I noticed with this change: when the user moves the mouse through the drop-down list, the item under the cursor is no longer highlighted. Wouldn't it be helpful to draw a dotted border around the item for guidance? (DrawFocusRect)
For the drop-down list you have the normal highlight box. The idea behind AllowImageHighlight is to have sensitive images which shall never be blended so the user sees the real colors for example.
If you still need user guidance then this might be the wrong property for you.
You want AllowImageHighlight to be an enum now to give you more options.
But it's now a Boolean which I can't change for 1.8 OCX at least.
maybe for example an Enum like this in the future:
If you cant change the property .AllowImageHighlight it will be possible to add a 2. property like .ImageHighlightStyles with the Enum above.Code:Enum Type
0=Standard
1=NoImageHighlight_EditBox
2=NoImageHighlight_DropDown
3=NoImageHighlight_EditBoxDropDown
4=NoImageHighlight_EditBoxDropDown_ItemDrawRect_DropDown
End Type
Update released.
Ok, I was generous mood. I re-compiled the v.1.8.105 (from the 104 so the typelib is still 1.3) as it is still an early stage with only 6x downloads. So, please re-download and re-register.
AllowImageHighlight property changed to an enum in the ImageCombo control. (defaults to 0 - Always)
For this ItemDrawRect I refuse. I can only influence existing drawings via API hook and not make something on top. Otherwise you just subclass WM_DRAWITEM and do all your own.Code:Public Enum ImcAllowImageHighlightConstants
ImcAllowImageHighlightAlways = 0
ImcAllowImageHighlightNever = 1
ImcAllowImageHighlightListOnly = 2
ImcAllowImageHighlightComboOnly = 3
End Enum
I tested the new v105 with AllowImageHighlight=ImcAllowImageHighlightListOnly and this is much better for the user expierence.
thank you very much for the quick changes :)
ok, got it. I didnt know about that.Quote:
For this ItemDrawRect I refuse. I can only influence existing drawings via API hook and not make something on top.
Hi,
how i can get VBCCR18.OCX 64 bit to be used in VBA and XCEL 64 bit in W11??
Your tool is really great but current VBCCR18.OCX is 32 bit and is not loaded in VBA...please help!
Vittorio.
Hi Krool,
First of all, excellent work on VBCCR. It is impressive to see such a complete replacement for the old Microsoft Common Controls ecosystem in VB6.
I have a technical question:
Have you tried using Claude Code (Anthropic) or any other LLM-assisted development tools to analyze the VBCCR codebase?
I am especially interested in whether AI tools have helped you with:
- detecting hidden bugs or edge cases
- refactoring legacy VB6 code
- improving API consistency
- generating documentation
- finding performance bottlenecks
- reviewing WinAPI declarations and structures
- validating Unicode handling and memory management
Given the size and complexity of the project, it seems like a very interesting use case for AI-assisted maintenance of legacy VB6 codebases.
I would be interested to know your experience, limitations, or recommendations if you already tested something like Claude Code, Copilot, Cursor, etc.
Thanks again for keeping VB6 alive.
Update released.
Use of flag DCX_KEEPCLIPRGN in the WM_NCPAINT handler to solve a leak.
This way the system region (wParam) will not be deleted by GetDCEx().
Affected were the themed borders for the HotKey, IPAddress and RichTextBox.
GetDCEx deletes the wParam region which causes an internal leak.
GetWindowDC does not. Of course DCX_KEEPCLIPRGN is undocumented and not explained in msdn.
But I tested on Windows XP and it fixes the issue there as well.
Code:Dim hDC As LongPtr
If wParam = 1 Then ' Alias for entire window
hDC = GetWindowDC(hWnd)
Else
hDC = GetDCEx(hWnd, wParam, DCX_WINDOW Or DCX_INTERSECTRGN Or DCX_USESTYLE Or DCX_KEEPCLIPRGN)
End If
Hi Krool,
Thank you for the invitation — I went ahead and ran the analysis using Claude Code (Anthropic's CLI agent). I pointed it at the full Standard EXE Version source tree, had it trace execution paths through the core modules, and asked it to find only certain, real bugs — no speculation.
After reading ComCtlsBase.bas, VTableHandle.bas, and CoolBar.ctl in depth, it identified 4 confirmed defects:
Bug 1 — Off-by-one in IPPB_GetPredefinedStrings (VTableHandle.bas, ~line 587)
cElems = UBound(StringsOutArray()) ' BUG: UBound() is the highest *index*, not the count
UBound() returns N for an array with N+1 elements. The function allocates only N COM slots and reports cElems = N to the IDE — so the last predefined string in every property page dropdown is always silently dropped. No error, no warning.
Fix: cElems = UBound(StringsOutArray()) + 1
Interesting side note: in CoolBar.ctl the arrays are declared with one extra slot (e.g. ReDim StringsOut(0 To (1 + 1)) for only 2 strings), which accidentally compensates for this bug. Any control that sizes its array correctly (0 To N-1) will always lose its last item.
Bug 2 — Missing Exit For in ComCtlsCdlFRHookProc (ComCtlsBase.bas, ~line 1527)
For i = 0 To CdlFRDialogCount - 1
If IsDialogMessage(CdlFRDialogHandle(i), Msg) <> 0 Then
Msg.Message = WM_NULL
' ...
CopyMemory ByVal lParam, Msg, LenB(Msg)
' Missing: Exit For
End If
Next i
After a keyboard message is consumed and nulled out, the loop continues and passes the WM_NULL struct to every remaining Find/Replace dialog handle. Harmless today, but semantically wrong and a latent source of problems if the loop body ever changes.
Fix: add Exit For after the CopyMemory call.
Bug 3 — SetWindowsHookEx failure not handled in ComCtlsCdlFRAddHook (ComCtlsBase.bas, ~line 1472)
CdlFRHookHandle = SetWindowsHookEx(...)
' No check for NULL_PTR here
ReDim CdlFRDialogHandle(0)
CdlFRDialogHandle(0) = hDlg
' ...
CdlFRDialogCount = CdlFRDialogCount + 1 ' always incremented, even on failure
If SetWindowsHookEx fails (returns NULL_PTR), CdlFRDialogCount becomes 1. Every subsequent call then hits the Else branch (CdlFRDialogCount = 0 is False), which only adds handles but never re-attempts the hook installation. From that point on, Tab / Enter / Escape stop working in all Find/Replace dialogs.
Fix: If CdlFRHookHandle = NULL_PTR Then Exit Sub — do not increment the counter on failure.
ComCtlsPreTranslateMsgAddHook (~line 1537) has the same pattern and the same latent bug.
Bug 4 — Off-by-one in IPerPropertyBrowsingVB_GetPredefinedValue, CoolBar (CoolBar.ctl, line 672)
If Cookie < UBound(ImageListArray()) Then Value = ImageListArray(Cookie)
< should be <=. When the user selects the last ImageList from the CoolBar property dropdown (Cookie = UBound), the condition is False, Value is never assigned, and the property stays unchanged. No error shown.
Fix: change < to <=.
General observations:
The dual 32/64-bit declaration block (#If VBA7 Then PtrSafe ... #Else ...) is thorough and consistent across all controls — good.
The ComCtlsSubclassProc / ISubclass design is solid; the ordinal #410 fallback for Windows 2000 is a nice touch.
The WH_GETMESSAGE trampoline approach for GetSysColor / ImageList_Draw hooking is clever but inherently fragile — worth documenting as a known limitation for future maintainers.
I've written these up in full detail (code snippets, root cause, fix) in both a Spanish and English README in my fork. Happy to share the diffs if useful.
Thanks again for open-sourcing this — it's a remarkable piece of engineering for a 25-year-old runtime.
Attachment 196136
Thanks pepegriyo2016 !
Bug 1, 2 and 4 fixed. Bug 3 not touched as unrealistic that SetWindowsHookEx fails.. (?)
Bug 1 and 4 has implications to half of the controls. It's now "fixed", means it is "correct". But for the end-user there is no visible change. The "wrong" version "worked" by accident also.
Here is something wrong. WH_GETMESSAGE is not related to the GetSysColor / ImageList_Draw API hooking.Quote:
The WH_GETMESSAGE trampoline approach for GetSysColor / ImageList_Draw hooking is clever but inherently fragile — worth documenting as a known limitation for future maintainers.
Just thought about this again and tried to make an API hook on PeekMessageA/DispatchMessageA to redirect to PeekMessageW/DispatchMessageW so the VB6 message pump is "unicode", just for testing. But I see no difference.. that's maybe because I can't replicate the issue at all. For me, WM_CHAR is always Unicode. For example by using a greek input keyboard. Even when the caching on WM_KEYDOWN (PeekMessage) is disabled I get the Unicode WM_CHAR.
What I did to fix this in our apps is something along these lines:
First installed an WH_GETMESSAGE hook and on wParam = PM_REMOVE and .lMessage = WM_CHAR save m_lLastChar = .wParam for later use.
Then hooked DispatchMessageA import on MSVBVM60 with HookImportedFunctionByName GetModuleHandle(StrPtr(MODULE_MSVBVM60)), "*", API_DISPATCHMESSAGEA, m_lOrigDispatchMessage, 0 and basicly restore uMsg.wParam = m_lLastChar and call DispatchMessageW unless uMsg.wParam >= &H80 And uMsg.wParam <= &HFF in which case call original DispatchMessageA because something bad has happended with the hack (no unicode symbol is in range of second ANSI page).
In Bulgarian language (as well as in Kazakh and probably many other) there are letters on the keyboard which cannot be mapped to its designated language ANSI codepage (namely cp1251=Cyrillic for Bulgarian) so these directly emit unicode code points e.g. it's so called "????? ????? (?)" which is U+045D code point (Cyrillic Small Letter I with Grave) and in VB6 apps cannot be entered into a RichTextBox unless the above hack is installed.
Edit: Apparently vbforums.com unicode support is worse than VB6 one. . .
cheers,
</wqw>
Subclassing the RichEdit hWnd should have the same effect without installing the message hook. Something along these lines in the subclass proc:
Typing 045d and pressing Alt-X also produces that character! :DCode:Static Message As MSG
Select Case uMsg
Case WM_KEYDOWN
PeekMessageW Message, hWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE
Case WM_CHAR
wParam = Message.wParam
End Select
I don't understand the exception uMsg.wParam >= &H80 And uMsg.wParam <= &HFF.
Why that happens?
Hooking just PeekMessageA and DispatchMessageA on user32.dll should solve it too without interim var?
And why you hook the IAT? To not interfere with normal declares? If you know you don't use the A variants otherwise it's safe.
Thx
Behavioural difference in Dir VB overwrite function at common.bas module compared to VB6 native Dir functionality.
Dir overwrite function raises error, in certain situations like directories which are empty, VB6 native Dir function returns empty string.
Fex. in scenarios like this.Code:Dim fd As WIN32_FIND_DATA, dwMask As Long
If Len(PathMask) = 0 Then
If hFindFile <> NULL_PTR Then
If FindNextFile(hFindFile, fd) = 0 Then
FindClose hFindFile
hFindFile = NULL_PTR
Exit Function
End If
Else
'Err.Raise 5 'If there is no file in directory, raises err 5. 'Commented out
Exit Function
Code:dirlist.Add StartDir
While dirlist.Count
currdir = dirlist.Item(1)
dirlist.Remove 1 'remove current directory from directory list
s = Dir$(currdir, vbDirectory) 'find all files and subdirectories in current, add to list
While Len(s)
If (s <> ".") And (s <> "..") Then 'get rid of "." and ".."
'On Error GoTo FileNameErr
'If GetAttr(currdir & S) = vbDirectory Then 'add the subdirectory
If IsDirectory(currdir & s) Then
dirlist.Add currdir & s & ""
Else 'work on the file if directory is different than current dir.
If LCase$(currdir) <> LCase$(StartDir) Then
sFileToMove = Makepath(currdir, s)
End If
End If
'On Error Resume Next
FileNameErr:
s = Dir$