Afx Strings. Afx, Afx2, AfxNova

Started by HIGH-Zen, November 01, 2025, 05:21:37 AM

Previous topic - Next topic

HIGH-Zen

Hi!

I am new to José Roca's Windows Frameworks for FreeBasic. But has experience making little tools with FreeBasic.

I was reading kind words about CWSTR.
https://www.planetsquires.com/protect/forum/index.php?msg=30710
That is Afx. In AfxNova I couldn't find CWSTR, only DWSTRING. Is CWSTR deprecated?

Afx2 - I couldn't find it in GitHub (https://github.com/JoseRoca), only in Tiko Editor. What I did understand is AfxNova is a way to go. I am right?

Why there is Afx, Afx2, AfxNova?

Can someone explain?

Paul Squires

Afx, Afx2, and AfxNova is basically Jose's progression of the library over the past couple of years. You should use AfxNova as it is the latest and greatest. In AfxNova, the dynamic unicode string is called DWSTRING rather than CWSTR which was in the prior library versions.
Paul Squires
PlanetSquires Software

Paul Squires

I built the Tiko Editor using Afx but there is way too much code in that codebase to now convert it over to AfxNova.
Paul Squires
PlanetSquires Software

HIGH-Zen

#3
OK, I've decided to go with AfxNova.

For FreeBasic development, I use Geany as my IDE on Windows 11 Pro (version 25H2). It's a simple, minimalist setup with a custom color scheme and personalized build commands - including one for running with GDB.

I've tried VisualFBEditor, WinFBE, and more recently Tiko, but Geany meets my needs just fine. I honestly don't remember why I chose it in the first place - maybe because I've always liked editors like Notepad++ and Emacs too.

Thank You for the explanation.

José Roca

#4
CWSTR has some quirks for full integration with FreeBasic, like working with bytes instead of characters (WORDs), like DWSTRING does. DWSTRING is more integrated with FreeBasic, behaving as if it was a native data type. It is more suitable to work with Unicode. For those that need it, it also has an option to check and repair broken surrogate pairs.

CWindow has also been improved to allow to make GUIs even more easier than before, with a very easy to use anchoring system that removes the need, in almost all cases, of having to do resizing manually processing WM_SIZE. It also allows to set colors to the main window and to the controls that allow it, automatic scrolling, easy subclassing and much more. The CTabControl class allows to make tabbed interfaces using a tab control and CWindows as pages of the tab control.


I have added to AfxNova a Rich Edit Control that wraps all the windows messages for that control and also all the Text Object Model (TOM) interfaces.


Currently I'm working in GDI+ classes that replicate the C++ ones. They work very well with my graphic control and CWindow, but can be used with SDK windows, dialogs or any device that has an HDC (device context handle), like printers. I have added to them two methods, ScaleTransform and SetResolution to make graphics and images DPI aware.

Next I plan to write a WebView2 control to replace my old WebBrowser control.

That is, I'm modernizing my old framework to work better with the needs of the high resolution monitors. Therefore, for new applications, AfxNova is the way to go.


HIGH-Zen

#5
You guys are absolute coding machines - thank you for the detailed explanation!

I'm now fully convinced to use AfxNova for my future projects. I'll definitely take a closer look at CWindow as well. I'm curious how it compares to raw WinAPI programming. For example, here's how I've been doing things traditionally:
' RAW Windows API version
#Include Once "windows.bi"

Dim Shared hWndMain As HWND
Dim Shared hButton As HWND

Function WindowProc(ByVal hWnd As HWND, ByVal uMsg As UINT, ByVal wParam As WPARAM, ByVal lParam As LPARAM) As LRESULT
    Select Case uMsg
        Case WM_COMMAND
            If HiWord(wParam) = BN_CLICKED Then
                If Cast(HWND, lParam) = hButton Then
                    MessageBox(hWnd, "Button clicked!", "Info", MB_OK Or MB_ICONINFORMATION)
                End If
            End If
        Case WM_DESTROY
            PostQuitMessage(0)
            Return 0
    End Select
    Return DefWindowProc(hWnd, uMsg, wParam, lParam)
End Function

Sub WinMain()
    Dim wc As WNDCLASS
    Dim msg As MSG
    Dim hInstance As HINSTANCE = GetModuleHandle(NULL)
    Dim className As String = "MyWindowClass"

    With wc
        .style = CS_HREDRAW Or CS_VREDRAW
        .lpfnWndProc = @WindowProc
        .cbClsExtra = 0
        .cbWndExtra = 0
        .hInstance = hInstance
        .hIcon = LoadIcon(NULL, IDI_APPLICATION)
        .hCursor = LoadCursor(NULL, IDC_ARROW)
        .hbrBackground = Cast(HGDIOBJ, COLOR_BTNFACE + 1)
        .lpszMenuName = NULL
        .lpszClassName = StrPtr(className)
    End With
    RegisterClass(@wc)

    hWndMain = CreateWindowEx(0, className, "Pure WinAPI Window", WS_OVERLAPPEDWINDOW, _
                              100, 100, 400, 300, NULL, NULL, hInstance, NULL)

    hButton = CreateWindowEx(0, "BUTTON", "Click Me", WS_CHILD Or WS_VISIBLE Or BS_PUSHBUTTON, _
                             150, 100, 100, 30, hWndMain, Cast(HMENU, 1001), hInstance, NULL)

    ShowWindow(hWndMain, SW_SHOWNORMAL)
    UpdateWindow(hWndMain)

    While GetMessage(@msg, NULL, 0, 0)
        TranslateMessage(@msg)
        DispatchMessage(@msg)
    Wend
End Sub

WinMain()
I'd love to understand how CWindow simplifies or abstracts this kind of setup. Does it handle message loops and control creation internally?

José Roca

Your example is correct for the times in which all monitors had a DPI of 96, but if you are using a higher DPI, e.g. 175, if you run it virtualized it will look blurry, and if you make it DPI aware, it will look small. CWindow does the scaling for you, so your GUI will be displayed with the same relative size regardless of the DPI settings of your computer.

I have written a tutorial for CWindow: https://github.com/JoseRoca/AfxNova/blob/main/docs/Windows/Windows%20GUI%20/CWindow%20Class.md

I also have written many templates ready to compile and run:
https://github.com/JoseRoca/AfxNova/tree/main/Templates/SDK%20Templates

An small template of a window with a button.

' ########################################################################################
' Microsoft Windows
' File: CW_Button_01.bas
' Contents: CWindow - Button
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "AfxNova/CWindow.inc"
USING AfxNova

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Creates the main window
   DIM pWindow AS CWindow = "MyClassName"   ' Use the name you wish
   DIM hWin AS HWND = pWindow.Create(NULL, "CWindow - Button", @WndProc)
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(400, 220)
   ' // Centers the window
   pWindow.Center

   ' // Adds a button
   pWindow.AddControl("Button", hWin, IDCANCEL, "&Close", 270, 155, 75, 30)
   ' // Anchors the button to the bottom and the right side of the main window
   pWindow.AnchorControl(IDCANCEL, AFX_ANCHOR_BOTTOM_RIGHT)

   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

   SELECT CASE uMsg

      ' // If an application processes this message, it should return zero to continue
      ' // creation of the window. If the application returns –1, the window is destroyed
      ' // and the CreateWindowExW function returns a NULL handle.
      CASE WM_CREATE
         RETURN 0

      ' // Sent when the user selects a command item from a menu, when a control sends a
      ' // notification message to its parent window, or when an accelerator keystroke is translated.
      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN SendMessageW(hwnd, WM_CLOSE, 0, 0)
         END SELECT
         RETURN 0

      CASE WM_DESTROY
         ' // End the application by sending an WM_QUIT message
         PostQuitMessage(0)
         RETURN 0

   END SELECT

   ' // Default processing of Windows messages
   FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================

When trying the examples, you will see the big difference in sharpness.

HIGH-Zen

Cool!
I'm on a 17.3" laptop with display scaling set to 150%, and now the program looks much better - definitely a noticeable improvement.

Unicode support is also really important to me, especially since Latvian uses accented characters like ā, č, ķ, ī, ē, ū, ļ, and ņ.

The tutorial and templates are fantastic - documentation like this makes a huge difference when getting started.

Thanks again for the detailed explanation and resources. With your guidance, I now have a much clearer understanding of how to work with AfxNova.

José Roca

For Unicode strings the only thing that you have to do is to declare the variable AS DWSTRING instead of AS STRING. It works transparently with all the native FreeBasic string functions and operators. All he code of AfxNova is Unicode aware, There is much more that AfxNova offers and all is documented. The number of ready to use string procedures is amazing (see: https://github.com/JoseRoca/AfxNova/blob/main/docs/String%20Management%20/String%20Procedures.md ) and includes support for VB Script Regular Expressions, that you can use to build your own procedures.

There is also support for file management in Unicode (FreeBasic supports unicode in files, but not in file names): https://github.com/JoseRoca/AfxNova/tree/main/docs/File%20Management%20

HIGH-Zen

It's 2025 - Unicode should really be the default in FreeBasic by now. The web, browsers, and Windows (e.g., MessageBoxW) have embraced Unicode for decades.
Unfortunately, FreeBasic still lags behind in this area. Even GDB 10.2 (GCC 14.2.0 x64) on Windows 11 Pro doesn't display Unicode characters properly. 😕

On a brighter note - wow, I just discovered the IniFile class! That's a super useful addition. Great job, I'm genuinely impressed.

Thanks for pointing out the DWSTRING type. I didn't realize it integrates so seamlessly with native FreeBasic string functions and operators. The fact that AfxNova is fully Unicode-aware and comes with such extensive documentation is a huge plus.
The string procedures list is amazing - especially with support for VBScript regular expressions. That opens up a lot of possibilities for custom parsing and text handling.

Also, Unicode file management support is a big deal. Even though FreeBasic doesn't support Unicode in file names, having this functionality in AfxNova is a major step forward.

Really appreciate the work that's gone into this framework. It's exciting to see FreeBasic evolve like this!

José Roca

As explained in the documentation ( https://github.com/JoseRoca/AfxNova/blob/main/docs/String%20Management%20/DWSTRING%20Class.md ) DWSTRING works transparently with literals and Free Basic native strings, can be used with Windows API functions and with files, and with arrays. Just as the FreeBasic native string data types.

The Tiko editor allows to use string literals in your language, e.g. DIM dws AS DWSTRING = "a, ā, b, c, č, d, e, ē, f, g, ģ, h, i, ī, j, k, ķ, l, ļ, m, n, ņ, o, p, r, s, š, t, u, ū, v, z, ž", if you select an option other than ansi. You can type them as any other text, without having to use Ctrl+Shift+U, then hold Ctrl and Shift and type the hexadecimal code point for the character you want (as the Geany documentation says).

It is more convenient to work with FreeBasic and AfxNova that Geany.


HIGH-Zen

I'll take a closer look and start using DWString.

With the Latvian Apostrophe keyboard layout, I can freely type Latvian characters in Geany on Windows. Similarly, for Russian input, the Russian Mnemonic layout works well. I save source files using UTF-8 with BOM, and no hexadecimal code points are needed.

I've also tested the Latvian Apostrophe layout in Geany on GNU/Linux, and it works without any issues there too.

As for the Ctrl+Shift+U method (followed by holding Ctrl and Shift while typing the hex code), that approach is specific to GNU/Linux environments and doesn't apply to Windows.  :)

HIGH-Zen

#12
I've been exploring José Roca's AfxNova framework and I have to say - I'm genuinely impressed! The Unicode support via DWSTRING is seamless, and the integration with VBScript-style regular expressions opens up powerful text processing capabilities.
#define UNICODE
#INCLUDE ONCE "AfxNova/DWString.inc"
USING AfxNova

Dim sText As DWSTRING

' Assign a Unicode string with Latvian accents
sText = "Sveiki! Šī ir FreeBasic demonstrācija ar diakritiskajām zīmēm: āčķīēūļņ."

' Convert to uppercase
Print "Uppercase:"
Print Ucase(sText)

' Replace a word
print DWStrReplace(sText, "demonstrācija", "piemērs")

' Use VBScript RegExp (example from CRegExp.inc)
DIM pRegExp AS CRegExp
pRegExp.Pattern = "(\w+)@(\w+)\.(\w+)+"
DIM dwsText AS DWSTRING = "Please send mail to dragon@xyzzy.com. Thanks!"

pRegExp.IgnoreCase = TRUE
IF pRegExp.Execute(dwsText) = FALSE THEN
   PRINT "No match found"
ELSE
   ' // Get the number of submatches
   DIM nCount AS LONG = pRegExp.SubMatchesCount(0)
   PRINT "Submatches: ", nCount
   FOR i AS LONG = 0 TO nCount - 1
      PRINT pRegExp.SubMatchValue(0, i)
   NEXT
END IF

' Show string length and character access
Print "Lenght of string: "; Len(sText)
Print "9. character: "; MID(sText, 9, 1)
Sleep
Output:
QuoteUppercase:
SVEIKI! ŠĪ IR FREEBASIC DEMONSTRĀCIJA AR DIAKRITISKAJĀM ZĪMĒM: ĀČĶĪĒŪĻŅ.
Sveiki! Šī ir FreeBasic piemērs ar diakritiskajām zīmēm: āčķīēūļņ.
Submatches:    3
dragon
xyzzy
com
Lenght of string: 72
9. character: Š
Big thanks to José for building such a robust and well-documented toolset!