CWindow Release Candidate 23
I have finished the CWebBrowser class.
Besides allowing to call all the relevant methods of the IWebBrowser2 interface, you can sink to the wanted events in a similar way as with VB6.
Events of the IDochHostUIHandler2 inteface are also supported for easy customization.
Events from the loaded page are supported in a generic way, i.e. there is not a callback for every possible event (there are too many) but just a function in which you can decide what to do according to the passe identifier of the event.
The most common need is to detect if a certain element in the web page, e.g. a button, has been clicked. You can do it by subscribing to the web page events
pwb.SetEventProc("HtmlDocumentEvents", @WebBrowser_HtmlDocumentEventsProc)
and then processing the event
PRIVATE FUNCTION WebBrowser_HtmlDocumentEventsProc (BYVAL hwndContainer AS HWND, BYVAL dispid AS LONG, BYVAL pEvtObj AS IHTMLEventObj PTR) AS BOOLEAN
SELECT CASE dispid
CASE DISPID_HTMLELEMENTEVENTS2_ONCLICK ' // click event
' // Get a reference to the element that has fired the event
DIM pElement AS IHTMLElement PTR
IF pEvtObj THEN pEvtObj->lpvtbl->get_srcElement(pEvtObj, @pElement)
IF pElement = NULL THEN EXIT FUNCTION
DIM cbsHtml AS CBSTR ' // Outer html
pElement->lpvtbl->get_outerHtml(pElement, @cbsHtml)
' DIM cbsId AS CBSTR ' // identifier
' pElement->lpvtbl->get_id(pElement, @cbsID)
pElement->lpvtbl->Release(pElement)
AfxMsg cbsHtml
RETURN TRUE
END SELECT
RETURN FALSE
END FUNCTION
Full example
' ########################################################################################
' Microsoft Windows
' Contents: WebBrowser customization test
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2016 Jose 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 "Afx/CWindow.inc"
#INCLUDE ONCE "Afx/AfxCtl.inc"
#INCLUDE ONCE "Afx/CWebBrowser/CWebBrowser.inc"
USING Afx
CONST IDC_WEBBROWSER = 1001
CONST IDC_SATUSBAR = 1002
DECLARE FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
END WinMain(GetModuleHandleW(NULL), NULL, COMMAND(), SW_NORMAL)
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
' // Forward declarations
DECLARE SUB WebBrowser_StatusTextChangeProc (BYVAL hwndContainer AS HWND, BYVAL pwszText AS WSTRING PTR)
DECLARE SUB WebBrowser_DocumentCompleteProc (BYVAL hwndContainer AS HWND, BYVAL pdisp AS IDispatch PTR, BYVAL vUrl AS VARIANT PTR)
DECLARE SUB WebBrowser_BeforeNavigate2Proc (BYVAL hwndContainer AS HWND, BYVAL pdisp AS IDispatch PTR, _
BYVAL vUrl AS VARIANT PTR, BYVAL Flags AS VARIANT PTR, BYVAL TargetFrameName AS VARIANT PTR, _
BYVAL PostData AS VARIANT PTR, BYVAL Headers AS VARIANT PTR, BYVAL pbCancel AS VARIANT_BOOL PTR)
DECLARE FUNCTION WebBrowser_HtmlDocumentEventsProc (BYVAL hwndContainer AS HWND, BYVAL dispId AS LONG, BYVAL pEvtObj AS IHTMLEventObj PTR) AS BOOLEAN
DECLARE FUNCTION DocHostUI_ShowContextMenuProc (BYVAL hwndContainer AS HWND, BYVAL dwID AS DWORD, BYVAL ppt AS POINT PTR, BYVAL pcmdtReserved AS IUnknown PTR, BYVAL pdispReserved AS IDispatch PTR) AS HRESULT
DECLARE FUNCTION DocHostUI_GetHostInfo (BYVAL hwndContainer AS HWND, BYVAL pInfo AS DOCHOSTUIINFO PTR) AS HRESULT
DECLARE FUNCTION DocHostUI_TranslateAccelerator (BYVAL hwndContainer AS HWND, BYVAL lpMsg AS LPMSG, BYVAL pguidCmdGroup AS const GUID PTR, BYVAL nCmdID AS DWORD) AS HRESULT
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS HINSTANCE, _
BYVAL hPrevInstance AS HINSTANCE, _
BYVAL szCmdLine AS ZSTRING PTR, _
BYVAL nCmdShow AS LONG) AS LONG
' // Set process DPI aware
' // The recommended way is to use a manifest file
AfxSetProcessDPIAware
' // Creates the main window
DIM pWindow AS CWindow
' -or- DIM pWindow AS CWindow = "MyClassName" (use the name that you wish)
DIM hwndMain AS HWND = pWindow.Create(NULL, "Embedded WebBrowser control with events and customization", @WndProc)
' // Sizes it by setting the wanted width and height of its client area
pWindow.SetClientSize(750, 450)
' // Centers the window
pWindow.Center
' // Add a status bar
DIM hStatusbar AS HWND = pWindow.AddControl("Statusbar", , IDC_SATUSBAR)
' // Add a WebBrowser control
DIM pwb AS CWebBrowser = CWebBrowser(@pWindow, IDC_WEBBROWSER, 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
' // Connect events
pwb.Advise
' // Set event callback procedures
pwb.SetEventProc("StatusTextChange", @WebBrowser_StatusTextChangeProc)
pwb.SetEventProc("DocumentComplete", @WebBrowser_DocumentCompleteProc)
pwb.SetEventProc("BeforeNavigate2", @WebBrowser_BeforeNavigate2Proc)
pwb.SetEventProc("HtmlDocumentEvents", @WebBrowser_HtmlDocumentEventsProc)
' // Set the IDocHostUIHandler interface
pwb.SetUIHandler
' // Set event callback procedures
pwb.SetUIEventProc("ShowContextMenu", @DocHostUI_ShowContextMenuProc)
pwb.SetUIEventProc("GetHostInfo", @DocHostUI_GetHostInfo)
pwb.SetUIEventProc("TranslateAccelerator", @DocHostUI_TranslateAccelerator)
' // Navigate to a URL
pwb.Navigate("http://com.it-berater.org/")
' pwb.Navigate("http://www.jose.it-berater.org/smfforum/index.php")
' // Display the window
ShowWindow(hWndMain, nCmdShow)
UpdateWindow(hWndMain)
' // Dispatch Windows messages
DIM uMsg AS MSG
WHILE (GetMessageW(@uMsg, NULL, 0, 0) <> FALSE)
IF AfxForwardMessage(GetFocus, @uMsg) = FALSE THEN
IF IsDialogMessageW(hWndMain, @uMsg) = 0 THEN
TranslateMessage(@uMsg)
DispatchMessageW(@uMsg)
END IF
END IF
WEND
FUNCTION = uMsg.wParam
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
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
CASE IDCANCEL
' // If ESC key pressed, close the application by sending an WM_CLOSE message
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
END SELECT
CASE WM_SIZE
' // Optional resizing code
IF wParam <> SIZE_MINIMIZED THEN
' // Resize the status bar
DIM hStatusBar AS HWND = GetDlgItem(hwnd, IDC_SATUSBAR)
SendMessage hStatusBar, uMsg, wParam, lParam
' // Calculate the size of the status bar
DIM StatusBarHeight AS DWORD, rc AS RECT
GetWindowRect hStatusBar, @rc
StatusBarHeight = rc.Bottom - rc.Top
' // Retrieve a pointer to the CWindow class
DIM pWindow AS CWindow PTR = AfxCWindowPtr(hwnd)
' // Move the position of the control
IF pWindow THEN pWindow->MoveWindow GetDlgItem(hwnd, IDC_WEBBROWSER), _
0, 0, pWindow->ClientWidth, pWindow->ClientHeight - StatusBarHeight / pWindow->ryRatio, CTRUE
END IF
CASE WM_DESTROY
' // Ends the application by sending a WM_QUIT message
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Default processing of Windows messages
FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Process the WebBrowser StatusTextChange event.
' ========================================================================================
SUB WebBrowser_StatusTextChangeProc (BYVAL hwndContainer AS HWND, BYVAL pwszText AS WSTRING PTR)
IF pwszText THEN StatusBar_SetText(GetDlgItem(GetParent(hwndContainer), IDC_SATUSBAR), 0, pwszText)
END SUB
' ========================================================================================
' ========================================================================================
' Process the WebBrowser DocumentComplete event.
' ========================================================================================
SUB WebBrowser_DocumentCompleteProc (BYVAL hwndContainer AS HWND, BYVAL pdisp AS IDispatch PTR, BYVAL vUrl AS VARIANT PTR)
' // The vUrl parameter is a VT_BYREF OR VT_BSTR variant
' // It can be a VT_BSTR variant or a VT_ARRAY OR VT_UI1 with a pidl
DIM varUrl AS VARIANT
VariantCopyInd(@varUrl, vUrl)
StatusBar_SetText(GetDlgItem(GetParent(hwndContainer), IDC_SATUSBAR), 0, "Document complete: " & AfxVarToStr(@varUrl))
VariantClear(@varUrl)
END SUB
' ========================================================================================
' ========================================================================================
' Process the IDocHostUIHandler ShowContextMenu event.
' ========================================================================================
FUNCTION DocHostUI_ShowContextMenuProc (BYVAL hwndContainer AS HWND, BYVAL dwID AS DWORD, BYVAL ppt AS POINT PTR, BYVAL pcmdtReserved AS IUnknown PTR, BYVAL pdispReserved AS IDispatch PTR) AS HRESULT
' // This event notifies that the user has clicked the right mouse button to show the
' // context menu. We can anulate it returning %S_OK and show our context menu.
' // Do not allow to show the context menu
' AfxMsg "Sorry! Context menu disabled"
' RETURN S_OK
' // Host did not display its UI. MSHTML will display its UI.
RETURN S_FALSE
END FUNCTION
' ========================================================================================
' ========================================================================================
' Process the IDocHostUIHandler GetHostInfo event.
' ========================================================================================
PRIVATE FUNCTION DocHostUI_GetHostInfo (BYVAL hwndContainer AS HWND, BYVAL pInfo AS DOCHOSTUIINFO PTR) AS HRESULT
IF pInfo THEN
pInfo->cbSize = SIZEOF(DOCHOSTUIINFO)
pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER OR DOCHOSTUIFLAG_THEME OR DOCHOSTUIFLAG_DPI_AWARE
pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT
pInfo->pchHostCss = NULL
pInfo->pchHostNS = NULL
END IF
RETURN S_OK
END FUNCTION
' ========================================================================================
' ========================================================================================
' Process the IDocHostUIHandler TranslateAccelerator event.
' ========================================================================================
PRIVATE FUNCTION DocHostUI_TranslateAccelerator (BYVAL hwndContainer AS HWND, BYVAL lpMsg AS LPMSG, BYVAL pguidCmdGroup AS const GUID PTR, BYVAL nCmdID AS DWORD) AS HRESULT
' // When you use accelerator keys such as TAB, you may need to override the
' // default host behavior. The example shows how to do this.
IF lpMsg->message = WM_KEYDOWN AND lpMsg->wParam = VK_TAB THEN
RETURN S_FALSE ' S_OK to disable tab navigation
END IF
' // Return S_FALSE if you don't process the message
RETURN S_FALSE
END FUNCTION
' ========================================================================================
' ========================================================================================
' Fires before navigation occurs in the given object (on either a window or frameset element).
' ========================================================================================
SUB WebBrowser_BeforeNavigate2Proc (BYVAL hwndContainer AS HWND, BYVAL pdisp AS IDispatch PTR, _
BYVAL vUrl AS VARIANT PTR, BYVAL Flags AS VARIANT PTR, BYVAL TargetFrameName AS VARIANT PTR, _
BYVAL PostData AS VARIANT PTR, BYVAL Headers AS VARIANT PTR, BYVAL pbCancel AS VARIANT_BOOL PTR)
' ' // Sample code to redirect navigation to another url
' IF AfxVarToStr(vUrl) = "http://com.it-berater.org/" THEN
' ' // Get a reference to the Afx_IWebBrowser2 interface
' DIM pwb AS Afx_IWebBrowser2 PTR = cast(Afx_IWebBrowser2 PTR, cast(ULONG_PTR, pdisp))
' IF pwb THEN
' ' // Stop loading the page
' pwb->Stop
' ' // Cancel the navigation operation
' *pbCancel = VARIANT_TRUE
' ' // Navigate to another new url
' DIM cvNewUrl AS CVARIANT = "http://www.planetsquires.com/protect/forum/index.php"
' pwb->Navigate2(@cvNewUrl)
' END IF
' END IF
END SUB
' ========================================================================================
' ========================================================================================
' For cancelable document events return TRUE to indicate that Internet Explorer should
' perform its own event processing or FALSE to cancel the event.
' ========================================================================================
PRIVATE FUNCTION WebBrowser_HtmlDocumentEventsProc (BYVAL hwndContainer AS HWND, BYVAL dispid AS LONG, BYVAL pEvtObj AS IHTMLEventObj PTR) AS BOOLEAN
SELECT CASE dispid
CASE DISPID_HTMLELEMENTEVENTS2_ONCLICK ' // click event
' // Get a reference to the element that has fired the event
DIM pElement AS IHTMLElement PTR
IF pEvtObj THEN pEvtObj->lpvtbl->get_srcElement(pEvtObj, @pElement)
IF pElement = NULL THEN EXIT FUNCTION
DIM cbsHtml AS CBSTR ' // Outer html
pElement->lpvtbl->get_outerHtml(pElement, @cbsHtml)
' DIM cbsId AS CBSTR ' // identifier
' pElement->lpvtbl->get_id(pElement, @cbsID)
pElement->lpvtbl->Release(pElement)
AfxMsg cbsHtml
RETURN TRUE
END SELECT
RETURN FALSE
END FUNCTION
' ========================================================================================
I have reuploaded the headers incorporating the changes posted in reply #12.
Because of the lack of response, I think that I'm going to stop posting, unless I find bugs that require an update.
I would like to know if it is because...
1) There is not much interest in Free Basic among the members of this forum.
2) There is not interest in this framework.
3) Other reasons.
I'm at the beginning of my transition to FB and I follow your posts with much interest. It is your work that convinced me I could make a go of FB. I have a large toolbox of stuff (classes like PDF creation, SMTP, tcp/ip, SQLite client/server, etc) to port, cCalendar being the first before I begin the rewrite of my applications. I'm counting on Paul to integrate cWindows as completely as possible to provide the foundation for first rate Windows apps. I'm not into all the technical details as you are - my area is the end game and developing software that uniquely solutions commercial lines of business. For that, I need you and Paul and appreciate all that the both of you do.
So, if I haven't misunderstood you, you're waiting for a visual designer.
Jose
As always you are doing a very valuable job.
Be sure , I'm very interrested on FreeBasic , on your Framework and globally on your posts.
I'm still "absorbing" the big amount of code you are producing.
What I'm less interrested is in the CWebBrowser : for me it is more a way ( a sample )on how to use the COM.
As you know,i'm more interrested on the generic tools to facilitate the use of COM under Freebasic.
I'm currently converting to freebasic an application i've previously done with powerbasic interfacing pfdcreator to convert to pfd many types of documents ( because pdfcreator changed completly their COM interfaces), i will use lot of your solutions.
The generation code of your typelib browser will play a big role.
Still do not understand how to use your Colecon class on IUNKNOWN interface and how to fire IUNKNOWN event interface...
If you think your Freebasic contribution not enougth followed here, why not extend it on posting on the official Freebasic Forum , and / or in this one https://www.freebasic-portal.de/ (https://www.freebasic-portal.de/) german but no problem to put english info.
Jose,
I understand your reluctance to continue posting, but the sheer volume of your contributions, takes a while to absorb. Please continue your FreeBasic research and post your findings here.
I believe a majority of advanced Fb coders are Linux centric. Novice Fb Windows coders may very well just be overwhelmed with the material you have posted here.
Much of your code is not very BASIC in appearance. I know you are not a big MACRO fan but maybe that is a direction that would interest more people.
Thank you for your dedication.
James
Jose,
Thank you for your contribution to the FreeBasic world.
Your knowledge of programming is far beyond mine.
I converted almost all of my FF-PB programs into FF-FreeBasic thanks to the help of Paul.
However these programs are not High DPI Aware.
It would be great if Paul implements all of your work in FireFly, so we will be able to use it in FireFly.
This could be the way to persuade the PB-people to switch to FreeBasic.
Klaas
Quote from: Jose Roca on October 04, 2016, 02:21:20 AM
So, if I haven't misunderstood you, you're waiting for a visual designer.
Yes - that is the logical endgame for your work.
Quote
Still do not understand how to use your Colecon class on IUNKNOWN interface and how to fire IUNKNOWN event interface...
And I still don't understand what you mean. I never have seen a visual OCX which inherits directly from IUnknown instead of IDispatch. Can you give me an example?
The OLE Container is a class to host
visual ActiveX controls. Non visual ActiveX don't need an OLE Container.
Quote
What I'm less interrested is in the CWebBrowser : for me it is more a way ( a sample )on how to use the COM.
Probably you don't realize all that can be done with it.
Quote from: Richard Kelly on October 04, 2016, 09:11:05 AM
Quote from: Jose Roca on October 04, 2016, 02:21:20 AM
So, if I haven't misunderstood you, you're waiting for a visual designer.
Yes - that is the logical endgame for your work.
If a visual designer is a prerequisite to use the framework then we are screwed because we don't know if this will ever happen or when.
Any project without participants dies because of lack of motivation.
Jose,
You have done a huge work with your framework; almost everything is avaible including ODBC (which is nice to replace SQLtools usually used with PB).
I read each of your posts with great attention.
Currently I'm still using FireFly+PowerBASIC+SQLitening+SQLTools+VPE (Virtual Print Engine) to create 32 bits only, ansi only, client/serveur windows software (business applications).
It will be easier (at least for me) to switch to FreeBasic when a visual designer coupled with your framework will be available.
Quote from: Jose Roca on October 04, 2016, 02:03:37 PM
Quote from: Richard Kelly on October 04, 2016, 09:11:05 AM
Quote from: Jose Roca on October 04, 2016, 02:21:20 AM
So, if I haven't misunderstood you, you're waiting for a visual designer.
Yes - that is the logical endgame for your work.
If a visual designer is a prerequisite to use the framework then we are screwed because we don't know if this will ever happen or when.
Any project without participants dies because of lack of motivation.
There is much in the framework I will use as part of the classes I have yet to port over. cWindows is the key integration into a visual designer and I hope to see something this year.
I do understand participation. You are the only one who posted on my cCalendar effort over much of the past months and I spent roughly 50 hours on it. Perhaps this forum is in decline.
Modified the following wrapper functions in which the parameter was being wrongly cast to the address of the passed pointer (I still make this error sometimes because I have used @ with pointers so many times with PowerBASIC).
ComboBoxEx_InsertItem
Syslink_GetItem
SysLink_HitTest
SysLink_SetItem
TreeView_GetItemEx
I have reuploaded the headers incorporating these changes.
I read each and every post you make, although I don't understand half of it.
Many of us are dependent on a visual designer and we truly hope that it would happen because this massive amount of stuff you develop combined with a visual designer from Paul will enable the rest of us to excel like never before.
I mean, you made Powerbasic Rock. You and Paul made it accessible to me and now I hope that you two will combine once again with all your lovely inspiring computer voodoo you do so a dummy like me can do cool stuff too.
We are here, every day. Please don't stop with your magic.
I just don't post anything because I cant contribute any value to your work!!!
All of us appreciate the stuff you do. Without it most will be lost!! thanks a million.
Thanks to all for answering my questions.
As I'm not willing to officially release something that has not been thoroughly tested, I'm going to prepare a stripped version with all the COM stufff removed. I will keep CWindow, the wrappers for the Windows API and controls, the string functions, the GDI+ helpers (not the classes) and the CWSTR data type. Maybe something more that I'm forgetting. Everything else will be removed. That is, I'm only going to keep what Paul will need to finish his editor.
Jose,
i'm very sad :'(
All the knowledge and passion you have for COM technology must not desapear !
You are one of the people who gave me the possibility to understand and work at certain extend with COM outside VC/VB .
For a vb6 guy as i was, it was so frustating to not be able to play as simple, as it was on the vb6 era.
For that reason, i've investigated a lot on Autoit wich has the flavor of basic and giving almost the same easy support to COM as VB6.
You on PowerBasic, Loe and Aloberoger on the freebasic forum ( who were also inspired by you) gave me almost the same possibilities (not as simpler) and i have to thank you so much for that.
Even making the last TypeLib browser functionnal for freebasic! And sharing your source code!
I hope, the frustration you feel by the "lack of interrest" will not persiste...
For my part, I will continue to investigate, improving my small competences on using activeX components,
it is a deep mine for curious people.
Hope, if i have stupid questions you can continue to give me some help, here or somewhere else...
Please receive my best regards, Marc.
Note : be sure, i have and will archive your precious posts 8)
Hi Jose,
I too am sad to read of your disappointment with the lack of interaction and feedback about your amazing efforts. I imagine the lack of feedback is largely to do with the fact that your accomplishments are so far ahead of where the rest of us 'mere mortals' are at. It is immensely difficult for the silent majority to contribute as you operate at a level so far beyond ours. What might seem obvious and straightforward to you is somewhat obscure and impenetrable to others and it is hugely reassuring to know that Jose the master has blazed a trail for the rest and made everything simple to use.
It is a frequent source of pleasure and admiration for me that you are kind and gracious enough to share your efforts with those of us who would never be able to originate such tremendous software. Whatever your choice may be about what you publish in the future, I would just like to express my thanks to you for all that you have already done. You have demonstrated not only how to write the best software but how to be a decent, generous and inspirational human being. Thank you.
What it remains still contains a lot of useful stuff. I mainly have removed the COM parts, so those that never use COM won't miss it. The COM wrappers were an attempt to make this technology more accessible, but I don't really need them.
I will continue exploring what I can do with the WebBrowser control using HTML and javascript. With the OLE Container and the CWebBrowser class I can start to attempt to make GUIs using HTML instead of / or mixing with Windows controls. This is an exciting possibility to make cool GUIs, taking the best of both technologies.
Dear Jose,
I just recently started exploring FreeBasic, as I have a project written in FireFly/PowerBasic which I want to finish before quitting PowerBasic entirely. So my lack of interest is because I am late in switching to FreeBasic, not because I am not interested in your work. On the contrary, I really appreciate your expertise and the huge amount of work you spent on developing and testing these includes for FireFly/Powerbasic and FreeBasic. So I hope you do not stop this work...
Wilko
Quote from: Jose Roca on October 07, 2016, 04:50:44 PM
...With the OLE Container and the CWebBrowser class I can start to attempt to make GUIs using HTML instead of / or mixing with Windows controls. This is an exciting possibility to make cool GUIs, taking the best of both technologies.
That would be a very cool project for GUI's. I wrote an ecommerce site last year and it was my first big project using Javascript, HTML/CSS, PHP, MYSQL and other libraries. Web technologies have come a long way. These days there is a push to create standalone cross platform applications. Take a look at projects like the Atom code editor that uses the Electron framework. Amazing what can be created these days with web technologies.
Paul, let me please ask a question i think many here is waiting for.
Would you consider using this cwindow developments of Jose in a future firefly for FB?
There are so much new developments that if you two throw this together, you will build one serious development tool.
I for one would love to have a Firefly that has all this new stuff from Jose as well.
Ps. Checked out ATOM, very cool.
Hi Peter, yes, of course I would use Jose's CWindow in FireFly for FB. That is not too difficult to do at all. I did it for the PB version a long time ago. The "problem" is that I want FireFly to be unicode based and Jose has done tremendous work in that area with regards to FreeBasic and unicode. I would want the visual designer to be 100% unicode based while also using CWindow for the code generation. As you probably already know, I have been working on code editors for FB (FBE) and one version is pretty much done with the other version almost done. Both are unicode and CWindow based. The next step after that would be to add a visual designer that integrates with the code editor.
Now that we have CWSTR, there is not excuse to not use unicode. It is super fast and dynamic. I learned years ago that using ansi in an application that you later plain to convert to unicode is the wrong way. For what we need a framework with both ansi and unicode versions if only the unicode one is needed? Although FB has not full unicode suport, at least it deals with unicode transparently, allowing to pass ansi strings to functions that have WSTRING parameters and doing an automatic conversion.
For the "Spectre" version of the framework, I'm going to rescue some stuff like the OLE Container and the CWebBrowser class. There are only a few wrappers that use CBSTRings and I can adapt them to use CWSTR instead, doing the needed conversions in the internal code. I'm going to use it extensively and don't want to have a version of the framework for general use and another for private use.
Regarding CBSTR, I wasn't fully satisfied with this data type because I had to use a couple of hacks. Anyway, if I'm the only one that is going to use COM, I have no problem to use it at low-level.
I'm also going to rescue some COM related stuff, like AfxCOM.inc, that does not use CBSTR, CVARIANT or CSafeArray. I need it and its use is optional.
I have discovered a curious thing.
If I use a CWSTR in a line such
PRINT "Line " & WSTR(curLine) & ", Column " & WSTR(curColumn) & ": " & **cwsText
I have to use **cwsText (double indirection)
But if I use + instead of &, I don't need the **
PRINT "Line " & WSTR(curLine) & ", Column " & WSTR(curColumn) & ": " + cwsText
It also happens when concatenating strings:
cws = WSTR(curLine) + ", Column " + WSTR(curColumn) + ": " + cwsText
Hi Jose
at the risk of being wrong, could it be that the + operator is overloaded ?
Both are overloaded. The difference is that whereas the two expressions concatenated by the + operator must be strings, the & operator also accepts numbers and tries to do the conversion.
Jose, I have also noticed that several times in the code that I've written. Sometimes the "&" concatenation does not work. I have always used the "&" rather than "+".
Using ** or the + operator always works. Maybe the & operator sometimes can't ascertain if it is a pointer to a string or a number that must be converted to a string.
I have managed to get most of the framework working without using CBSTR and CVARIANT.
Quote from: Jose Roca on October 09, 2016, 10:26:26 PM
Both are overloaded. The difference is that whereas the two expressions concatenated by the + operator must be strings, the & operator also accepts numbers and tries to do the conversion.
in fact when using operator "&" , it will implicitly make a conversion to string using str()
the operator "+" will expect true strings so it will not make implicit conversion
that explain :
QuoteIf I use a CWSTR in a line such
Code: [Select]
PRINT "Line " & WSTR(curLine) & ", Column " & WSTR(curColumn) & ": " & **cwsText
I have to use **cwsText (double indirection)
because cwsText is a class and str() function does not understand how to do with it.
i think you can define a new overload operator "&" to use cwstr directly without **cwsText
for string result
Operator & (ByRef ust1 as string , ByVal cwst2 as CWSTR) as string
return ust1 & **cwst2 'using here the implicit cast to string to convert cwst2 content to string
'or return ust1 & str(**cwst2) 'explicit conversion
end operatoreasier way but more operations !
could also be duplicated to CWSTR or WSTRING
it is not complete answer:
why operrator "+" works correctly in that case?
because that operator expect a string after it and the compiler use the implicit cast implemented by the cwstr class to make the job.
in reality the convertion task is done but not seen by the user.
Quote
i think you can define a new overload operator "&" to use cwstr directly without **cwsText
for string result
Operator & (ByRef ust1 as string , ByVal cwst2 as CWSTR) as string
return ust1 & **cwst2 'using here the implicit cast to string to convert cwst2 content to string
'or return ust1 & str(**cwst2) 'explicit conversion
end operator
easier way but more operations !
could also be duplicated to CWSTR or WSTRING
No way. Converting it to ansi with STR will screw the unicode contents. Better to use +.
Modified an small bug in one of the Add methods of CWSTR. Didn't noticed before because I was not using UTF-8.
PRIVATE SUB CWstr.Add (BYREF ansiStr AS STRING, BYVAL nCodePage AS UINT = 0)
CWSTR_DP("CWSTR Add STRING Code page = " & WSTR (nCodePage))
' DIM AS LONG nLenString = .LEN(ansiStr) * 2
' IF nLenString = 0 THEN RETURN
IF LEN(ansiStr) = 0 THEN RETURN
' Create the wide string from the incoming ansi string
DIM pbstr AS BSTR
IF nCodePage = 0 THEN nCodePage = m_CodePage
IF nCodePage = CP_UTF8 THEN
DIM dwLen AS DWORD = MultiByteToWideChar(CP_UTF8, 0, STRPTR(ansiStr), LEN(ansiStr), NULL, 0)
IF dwLen THEN
pbstr = SysAllocString(WSTR(SPACE(dwLen)))
MultiByteToWideChar(CP_UTF8, 0, STRPTR(ansiStr), LEN(ansiStr), pbstr, dwLen * 2)
END IF
ELSE
pbstr = SysAllocString(WSTR(ansiStr))
MultiByteToWideChar(m_CodePage, MB_PRECOMPOSED, STRPTR(ansiStr), -1, pbstr, LEN(ansiStr) * 2)
END IF
IF pbstr THEN
' Copy the string into the buffer and update the length
' this.AppendBuffer(pbstr, nLenString)
this.AppendBuffer(pbstr, SysStringLen(pbstr) * 2)
SysFreeString(pbstr)
END IF
END SUB
When used with UTF-8, it was passing the length in UTF-8 instead of UTF-16.
The problem with the intrinsic FB functions that don't work directly with CWSTR seems to be that they don't trigger the casting operators of the class.
AfxMsg LEFT(cws, 10) fails with an ambiguous call error, but AfxMsg LEFT("" & cws, 10) works. The concatenation triggers the cast operator.
Quote from: Jose Roca on October 10, 2016, 02:13:02 PM
No way. Converting it to ansi with STR will screw the unicode contents. Better to use +.
its true but as i also said can be extended to wstring and cwstr
hope it work like that: (not tested!)
Operator & (ByRef wst1 as wstring , ByVal cwst2 as CWSTR) as wstring
return wst1 & **cwst2
end operator
Operator & (ByRef cwst1 as CWSTR , ByVal cwst2 as CWSTR) as CWSTR
return cwst1 & **cwst2
end operator
I know + is working but it is not coherent with the freebasic convention
+ normaly to concatenate string or wstrings only
& helper to convert to "string" and concatenate
QuoteAfxMsg LEFT(cws, 10) fails with an ambiguous call error, but AfxMsg LEFT("" & cws, 10) works. The concatenation triggers the cast operator.
it is normal behaviour, your cast in cwstr class are the following :
' ========================================================================================
' Returns a pointer to the CWSTR buffer.
' ========================================================================================
PRIVATE OPERATOR CWstr.CAST () BYREF AS WSTRING
CWSTR_DP("CWSTR CAST BYREF AS WSTRING - buffer: " & WSTR(m_pBuffer))
OPERATOR = *cast(WSTRING PTR, m_pBuffer)
END OPERATOR
' ========================================================================================
' ========================================================================================
PRIVATE OPERATOR CWstr.CAST () AS ANY PTR
CWSTR_DP("CWSTR CAST ANY PTR - buffer: " & WSTR(m_pBuffer))
OPERATOR = cast(ANY PTR, m_pBuffer)
END OPERATOR
' ========================================================================================probably the cast for any ptr is the trick here
Implementing & as an overloded operator for CWSTR works, of course (I have tried it before)
PRIVATE Operator & (ByRef cwst1 as CWSTR , ByRef cwst2 as CWSTR) as CWSTR
CWSTR_DP("CWSTR Operator &")
RETURN **cwst1 & **cwst2
END OPERATOR
but it is pretty innefficient, and my goal is to be as fast as possible.
A line like this
cws = "Line " & WSTR(1) & ", Column " & WSTR(2) & ": " & **cwsText
generates this trace code
CWSTR OPERATOR * buffer: 6632152
CWSTR LET WSTRING PTR
CWSTR Clear
CWSTR ResizeBuffer - Value = 27
CWSTR ResizeBuffer - pNewBuffer = 6631952 - old buffer = 6632688
CWSTR Add WSTRING
CWSTR AppendBuffer 0 54
CWSTR ResizeBuffer - Value = 108
CWSTR ResizeBuffer - pNewBuffer = 6632688 - old buffer = 6631952
--END - CWSTR AppendBuffer 54
Using the operator +
cws = "Line " & WSTR(1) + ", Column " & WSTR(2) & ": " + cwsText
generates this trace code
CWSTR CAST BYREF AS WSTRING - buffer: 2634456
CWSTR LET WSTRING PTR
CWSTR Clear
CWSTR ResizeBuffer - Value = 27
CWSTR ResizeBuffer - pNewBuffer = 2634288 - old buffer = 2634992
CWSTR Add WSTRING
CWSTR AppendBuffer 0 54
CWSTR ResizeBuffer - Value = 108
CWSTR ResizeBuffer - pNewBuffer = 2634992 - old buffer = 2634288
--END - CWSTR AppendBuffer 54
Changing it to
cws = "Line " & WSTR(1) & ", Column " & WSTR(2) & ": " & cwsText
with the overloaded & operator implemented, generates this trace code
+++BEGIN- CWSTR CONSTRUCTOR WSTRING - 4255888
CWSTR ResizeBuffer - Value = 520
CWSTR ResizeBuffer - pNewBuffer = 8533768 - old buffer = 0
CWSTR Add WSTRING
CWSTR AppendBuffer 0 36
--END - CWSTR AppendBuffer 36
-END- CWSTR CONSTRUCTOR WSTRING - 8533768
CWSTR Operator &
CWSTR OPERATOR * buffer: 8532696
CWSTR OPERATOR * buffer: 8533768
+++BEGIN- CWSTR CONSTRUCTOR WSTRING - 8532432
CWSTR ResizeBuffer - Value = 520
CWSTR ResizeBuffer - pNewBuffer = 8534304 - old buffer = 0
CWSTR Add WSTRING
CWSTR AppendBuffer 0 54
--END - CWSTR AppendBuffer 54
-END- CWSTR CONSTRUCTOR WSTRING - 8534304
CWSTR LET CWSTR
CWSTR Clear
CWSTR OPERATOR * buffer: 8534304
CWSTR OPERATOR LEN - len: 27
CWSTR OPERATOR * buffer: 8534304
CWSTR ResizeBuffer - Value = 27
CWSTR ResizeBuffer - pNewBuffer = 8532432 - old buffer = 8533232
CWSTR OPERATOR @ - buffer: 8534304
CWSTR Add CWSTR - LEN = 27
CWSTR OPERATOR @ - buffer: 8534304
CWSTR CAST ANY PTR - buffer: 8534304
CWSTR AppendBuffer 0 54
CWSTR ResizeBuffer - Value = 108
CWSTR ResizeBuffer - pNewBuffer = 8532472 - old buffer = 8532432
--END - CWSTR AppendBuffer 54
***CWSTR DESTRUCTOR - buffer: 8533768
***CWSTR DESTRUCTOR - buffer: 8534304
Both ** and + generate identical code, except the first line: ** calls the operator * ( CWSTR OPERATOR * buffer: 6632152 ) and + calls the operator CAST ( CWSTR CAST BYREF AS WSTRING - buffer: 2634456 ).
The overloaded operator & has to create three instances of the CWSTR class, two to concatenate and another one to return the result.
We have not worked so hard to get a superfast unicode dynamic string to spoil it using innefficient techniques.
Jose
sometime i've problems to understand or explain, probably because English is not my native language :'(
let me sumerize my purpose :
I'm
not saying "+" or "**" is wrong , they work perfectly to make the job , so it's a perfect true solution
I'm just saying "&" could work
also and for me the more important word is : also
I think it is better to have that solution too than to have compilation problems
I've said in my previous posts : if using "&" it will be :
Quoteeasier way but more operations !
I've said also
QuoteI know + is working but it is not coherent with the freebasic convention
+ normaly to concatenate string or wstrings only
& helper to convert to "string" and concatenate
myself, i normally use without thinking more : "+" for "add" operation and "&" for concatenation... probably coming from Fortran
i'm sure i'm not the only one doing that !
Last point, you are showing the extra operations if using overloaded "&" ,
but you know the way you have done the overload "&" to test it, could probably be optimized
as we are "derriere le rideau" / "behind the curtain", at level of operator we could directly play with the class functions...
At the end, it is not a big problem , these kind of overload operator does not need to be part of the class
it can be done everywhere in the code as you want it.
Probably you understood also, even i'm fare away of your knowledge, i like to go to the details :
the speed of concatenation was one of the aspects very important for me, if your remember our previous contacts.
I sincerely hope, you will not take that present post as a critic in any sort,
i just try to participate, showing at least my interest,
and at the end supporting as much as i can the very important job you are doing.
And, for all, please excuse my wording, if the syntax, grammar or tence are not perfect...
Marc
I appreciate any comments and suggestions, but while trading some speed for ease of use is acceptable or even convenient in many cases, in others not.
For example, in a procedure that must return a result, it is usually faster to pass a variable by reference to get the result that returning it as the result of a function. If we have to return an small string, then it doesn't matter, but if we are using big strings, it does. There was an initial version of the class in which I overloaded the & operator and it slowed string concatenations considerably. This is why I used Paul's string builder code, added several changes like marking the end of the string with a double null to make it compatible with the FB intrinsic string functions and removed the operators except += and &=. Overloading the & operator is an speed killer. Also, as you have pointed, it is not mandatory to add it to the class. You can add it anywere in your code.
VB6 was a beginner's tool in which ease of use took precedence over efficiency. This is why they got the slower compiler ever made. It has spoiled generations of programmers.
BTW I have implemented a new class, CWstrArray, to work with arrays of CWSTRs. Internally, it uses BSTRs because the safe array APIs work with this type of string, but the in and out parameters are CWSTRs. I have used low-level techniques to avoid copying data as much as possible: for example, when inserting or removing array elements, I don't copy the string data to expand or shrink the array, but I only move the BSTRs pointers.
' ========================================================================================
' * Deletes the specified element of the array.
' - nPos = Index of the array element to be removed.
' Return value: TRUE or FALSE.
' ========================================================================================
PRIVATE FUNCTION CWstrArray.DeleteItem (BYVAL nPos AS LONG) AS BOOLEAN
CWSTRARRAY_DP("CWstrArray DeleteItem")
DIM cElem AS LONG = nPos - this.LBound
IF nPos < this.LBound OR nPos > this.UBound THEN RETURN FALSE
DIM cElements AS LONG = this.UBound - this.LBound + 1
DIM pvData AS AFX_BSTR PTR = this.AccessData
IF pvData THEN
' // Save the element to be deleted
DIM pTemp AS AFX_BSTR = pvData[cElem]
' // Move all the elements up
FOR i AS LONG = cElem TO cElements - 1 STEP 1
pvData[i] = pvData[i + 1]
NEXT
' // Copy the element to be deleted to the end of the array
pvData[cElements - 1] = pTemp
END IF
this.UnaccessData
' // Shrink the array by one element (will free the last element)
IF this.Redim(cElements - 1) = S_OK THEN RETURN TRUE
END FUNCTION
' ========================================================================================
This makes a BIG difference regarding speed.
I also use direct access to get and set the string data instead of the slower SafeArrayGetElement / SafeArrayPutElement API functions.
' ========================================================================================
' * Gets an element of the array. If the function fails, it returns an empty string.
' - idx : The index of the array element.
' ========================================================================================
PRIVATE PROPERTY CWstrArray.Item (BYVAL idx AS LONG) AS CWSTR
CWSTRARRAY_DP("PROPERTY ITEM [GET] - CWSTR")
IF m_psa = NULL THEN EXIT PROPERTY
SafeArrayLock(m_psa)
DIM pvData AS AFX_BSTR PTR = this.PtrOfIndex(idx)
IF pvData THEN PROPERTY = *pvData
SafeArrayUnlock(m_psa)
END PROPERTY
' ========================================================================================
' ========================================================================================
' * Puts a string element at a given location in the array.
' - idx : The index of the array element.
' - cws : The string data to store.
' ========================================================================================
PRIVATE PROPERTY CWstrArray.Item (BYVAL idx AS LONG, BYREF cws AS CWSTR)
CWSTRARRAY_DP("PROPERTY ITEM [PUT] - CWSTR")
IF m_psa = NULL THEN EXIT PROPERTY
SafeArrayLock(m_psa)
DIM pvData AS AFX_BSTR PTR = this.PtrOfIndex(idx)
IF pvData THEN *pvData = SysAllocString(**cws)
SafeArrayUnlock(m_psa)
END PROPERTY
' ========================================================================================
This makes the internal code of many of my wrappers to look not very "BASIC", as James has pointed, but it is efficient.
> I'm just saying "&" could work also and for me the more important word is : also
I kown what you mean, but this is like putting a red button with a label saying "don't push me"... If it must not be pushed, why you put it?
If they use & and it compiles, they will always use & and then complain that it is slow. If it does not compile, they will learn that have to use + or **.
I have done some modifications on the CWSTR.inc, i posted here because its some continuation of previous posts.
You can see it on the attached file with a test to verify and possibly compare with the existing one
the changes have affected different topics / behaviour: ( i've tried to mimic the string behaviour as much as possible)
the idea is if you know how to use string in specific situation it is the same with cwstr class!
@cwstr : now gives the pointer of the cwstr var ( as cwstr ptr )
varptr(cwstr) : same as above
strptr(cwstr) : working and give the pointer to the internal buffer ( as wstring ptr)
*cwstr : dereference the internal pointer to wstring ( byref as wstring)
new & operator to concatenate cwstr with wstring (both sides) or other cwstr without **cwstr ( or any work-arround)
no more need to **cwstr ( or any work-arround) to use right and left functions they have been overloaded with new ones
and to finish : a bit faster at construction, let operations and concatenation too
measured : 15 to 25 % faster, by optimization of operations (allocating only once when possible, avoiding intial alloc... )
and few tweeks more.
to test use the cwstr2.inc on the same folder as the test file and compile with console, you can trace the actions directly on the console.
remarks/comments appreciated as usual
Marc
I don't agree in removing the operator @ and changing the operator *. Besides breaking all my code and Paul's code, I think that you don't have understood why I have implemented it this way.
The purpose of the @ and * operators is to allow to pass a CWSTR to the Windows API functions that expect a WSTRING by reference. Whitout them, the compiler will pass a pointer to the CWSTR class instead of the underlying WSTRING and it will crash.
For example, in a function like this one
' ========================================================================================
' Gets the text of a window. This function can also be used to retrieve the text of buttons,
' and edit and static controls.
' Remarks: The function uses the WM_GETTEXT message because GetWindowText cannot retrieve
' the text of a window in another application.
' Example: DIM cws AS CWSTR = AfxGetWindowText(hwnd)
' ========================================================================================
PRIVATE FUNCTION AfxGetWindowText (BYVAL hwnd AS HWND) AS CWSTR
DIM nLen AS LONG = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
DIM cwsText AS CWSTR = SPACE(nLen + 1)
SendMessageW(hwnd, WM_GETTEXT, nLen + 1, cast(LPARAM, *cwsText))
RETURN cwsText
END FUNCTION
' ========================================================================================
I can use cast(LPARAM, *cwsText) or cast(LPARAM, @cwsText), but with your suggested changes, I will have to use cast(LPARAM, cwsText.m_pBuffer).
In an earlier version of CBSTR, I used an "Addr" method instead of "**", and the only user that gave his opinion was Paul, that liked more to use **cws than cws.Addr.
It is nice to use *cws instead of **cws, but it is not so nice to use cws.m_pBuffer (or cws.Addr) instead of @cws.
To work seamless with the intrinsic FB functions the only solution is to have a dynamic unicode data type implemented in the compiler natively.
Regarding Right and Left, I didn't know that I could overload these intrinsic functions. I can add these functions to CWSTR:
PRIVATE FUNCTION RIGHT (BYREF cws AS CWSTR, BYREF n AS LONG) AS CWSTR
RETURN RIGHT(**cws, n)
END FUNCTION
PRIVATE FUNCTION LEFT (BYREF cws AS CWSTR, BYREF n AS LONG) AS CWSTR
RETURN LEFT(**cws, n)
END FUNCTION
I can also add:
PRIVATE FUNCTION VAL (BYREF cws AS CWSTR) AS DOUBLE
RETURN VAL(**cws)
END FUNCTION
But, of course, using RIGHT(**cws) is faster than RIGHT(cws).
I don't understand the purpose of this function.
' ========================================================================================
' Write the number of bytes from the specified memory address to the buffer. **** new function
' ========================================================================================
PRIVATE SUB CWstr.WriteBuffer (BYVAL addrMemory AS ANY PTR, BYVAL nNumBytes AS LONG)
if nNumBytes < m_GrowSize /2 THEN nNumBytes += m_GrowSize/2
CWSTR_DP("CWSTR WriteBuffer " & WSTR(nNumBytes))
this.ResizeBuffer( nNumBytes * 2)
memcpy(m_pBuffer , addrMemory, nNumBytes)
m_BufferLen = nNumBytes
' Mark the end of the string with a double null
m_pBuffer[m_BufferLen] = 0
m_pBuffer[m_BufferLen + 1] = 0
CWSTR_DP("--END - CWSTR WriteBuffer " & WSTR(m_BufferLen))
END SUB
' ========================================================================================
If I use
DIM wsz AS STRING = "abc"
DIM cws AS CWSTR = wsz
PRINT LEN(cws)
It returns a length of 123 instead of 3.
for the WriteBuffer function, my mistake the function should be: (thanks to point that mistake)
PRIVATE SUB CWstr.WriteBuffer (BYVAL addrMemory AS ANY PTR, BYVAL nNumBytes AS LONG)
m_BufferLen = nNumBytes '**** assign m_BufferLen before modifying nNumBytes
if nNumBytes < m_GrowSize /2 THEN nNumBytes += m_GrowSize/2
CWSTR_DP("CWSTR WriteBuffer " & WSTR(nNumBytes))
this.ResizeBuffer( nNumBytes * 2)
memcpy(m_pBuffer , addrMemory, nNumBytes)
' Mark the end of the string with a double null
m_pBuffer[m_BufferLen] = 0
m_pBuffer[m_BufferLen + 1] = 0
CWSTR_DP("--END - CWSTR WriteBuffer " & WSTR(m_BufferLen))
END SUBOverloaded functions right, left : of course you can overload them , they are already overloaded
not tested if your proposals are faster than mines but why not.
sure val function can also be overloaded.
the
@cwstr and
*cwstr your proposal gives same result a pointer to the buffer (AS WSTRING PTR)
QuoteI can use cast(LPARAM, *cwsText) or cast(LPARAM, @cwsText), but with your suggested changes, I will have to use cast(LPARAM, cwsText.m_pBuffer).
my proposal, you missed, it is :
strptr(cwstr) giving the pointer to the buffer (AS WSTRING PTR)
similar way as used with normal string to get the pointer to data ( as zstring ptr)
that's why i've undef the strptr macro and i've recreated them as functions
so the use for window api is like:
PRIVATE FUNCTION AfxGetWindowText (BYVAL hwnd AS HWND) AS CWSTR
DIM nLen AS LONG = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
DIM cwsText AS CWSTR = SPACE(nLen + 1)
SendMessageW(hwnd, WM_GETTEXT, nLen + 1, cast(LPARAM, strptr(cwsText))) ' **** notice the use of strptr as with string
RETURN cwsText
END FUNCTIONi aggree strptr is longer than @ or * but it is more "conventionnal" to not mix the use of @ and *
normally :
@ is the operator to get the pointer to the var and
* is the operator to get the value pointed by the pointer ( or by extension as in string the value of data pointer)
I'm not saying it has to be like that, i'm just holding some time, and review the work done to verify if it is still coherent with the other existing behaviour.
I think, the exeptions (even for good reason) are the way for complexity and at the end for errors.
And taking the opportunity also, to review if some optimization could be found ( speed for me is crucial, i can trade in some extend with the "simplicity" but not too much)
I understand completly the behaviours are different and can have impacts on the other part of your framework...
but as your framework is not totally freezed...
No comments for & and for speed ?
for right noticed a mistake on the logic in my proposal
corrected here
PRIVATE FUNCTION RIGHT( BYREF cwstr AS CWSTR, BYREF n AS LONG )AS CWSTR
CWSTR_DP("CWSTR -RIGHT FUNCTION-")
FUNCTION = cast(wstring ptr, cwstr.m_pBuffer + cwstr.m_BufferLen - (n * 2))
END FUNCTION
tested your overload proposal right and left : mines (after that correction) are a bit faster
and confirm also with my modified class , the speed has been improved at least 10% up to 25%
even with the & new operator
QuoteBut, of course, using RIGHT(**cws) is faster than RIGHT(cws).
its true with your cwstr.inc but with cwstr2.inc and with my overload proposal for left/right not true
in fact almost 2 time faster with my solution ( because I do not allocate 2 times...)
stop for today, family lunch now :)
> the @cwstr and *cwstr your proposal gives same result a pointer to the buffer (AS WSTRING PTR)
Of course, but it is more intuitive, at least for me, to use @ with out parameters and * with in parameters. VARPTR and STRPTR also return the same value when used with WSTRINGs because there is only one address to return. These data types don't have a descriptor.
> i aggree strptr is longer than @ or * but it is more "conventionnal" to not mix the use of @ and *
normally : @ is the operator to get the pointer to the var and * is the operator to get the value pointed by the pointer ( or by extension as in string the value of data pointer
Not with ZSTRING, WSTRING of fixed length strings. See above.
And CWSTR is also a null terminated string without a descriptor.
> for the WriteBuffer function, my mistake the function should be: (thanks to point that mistake)
I still don't understand why do you do that. You pass numBytes to copy and you end copying 260 additional bytes of garbage?
> tested your overload proposal right and left : mines (after that correction) are a bit faster
I still have to check it. It is unsafe code because there are not checks for bounds. What if I pass a negative value or a value bigger than the buffer?
good some new input !
if you feel happy mixing @ and * , no problem for me
but "official" freebasic definition
QuoteOperator @ (Address of) returns the memory address of its operand
Operator * (Value of) returns a reference to the value stored at an address, and is often called the dereference operator
you can choose to overload to what you want, but at the end , it is more complexity
QuoteVARPTR and STRPTR also return the same value when used with WSTRINGs
true but both are dedicated to return a pointer not a value as * is supposed to
and with my proposal varptr and strptr give their respective true pointer( one to cwstr ptr, and other to wstring ptr (casted)
QuoteAnd CWSTR is also a null terminated string without a descriptor.
:o, for me CWSTR class is not really different than dynamic String, lets compare the structure elements
data as zstring ptr for string : m_pBuffer AS UBYTE PTR for cwstr
len as long for string : m_BufferLen AS LONG for cwstr
size as long for string : m_Capacity AS LONG for cwstr
data in the string type is obviously a null terminated string, so i do not see structure difference
again thanks to point the second mistakes on CWstr.WriteBuffer, i've replied to fast
the reason of that function is only: to not have all the cases done on the CWstr.AppendBuffer (avoiding initial setting for nothing)
and if the length is small enougth to increase it a bit to not have to resize after but not fully needed
last proposal
PRIVATE SUB CWstr.WriteBuffer (BYVAL addrMemory AS ANY PTR, BYVAL nNumBytes AS LONG)
m_BufferLen = nNumBytes '**** assign m_BufferLen before modifying nNumBytes
' the idea here is to have at least some buffer reserve to not have always to resize if append after
if nNumBytes < m_GrowSize /2 THEN nNumBytes += m_GrowSize/2 ' can be avoided probably
CWSTR_DP("CWSTR WriteBuffer " & WSTR(nNumBytes))
this.ResizeBuffer( nNumBytes * 2)
memcpy(m_pBuffer , addrMemory, m_BufferLen) ' nNumBytes '**** sure not needed to copy garbage thanks Jose
' Mark the end of the string with a double null
m_pBuffer[m_BufferLen] = 0
m_pBuffer[m_BufferLen + 1] = 0
CWSTR_DP("--END - CWSTR WriteBuffer " & WSTR(m_BufferLen))
END SUB and obviouly the right and left have been secured
'New overload function right for cwstr
PRIVATE FUNCTION RIGHT( BYREF cwstr AS CWSTR, BYREF n AS LONG )AS CWSTR
CWSTR_DP("CWSTR -RIGHT FUNCTION-")
if cwstr.m_BufferLen = 0 or n <= 0 THEN
'RETURN ""
FUNCTION = "" '**** probably faster with return
elseif n > cwstr.m_BufferLen THEN
'RETURN CAST(WSTRING PTR, cwstr.m_pBuffer)
FUNCTION = cast(wstring ptr, cwstr.m_pBuffer)'**** probably faster with return
else
'RETURN CAST(WSTRING PTR, cwstr.m_pBuffer + cwstr.m_BufferLen - (n * 2))
FUNCTION = cast(wstring ptr, cwstr.m_pBuffer + cwstr.m_BufferLen - (n * 2))'**** probably faster with return
END IF
END FUNCTION
'New overload function left for cwstr
PRIVATE FUNCTION LEFT( BYREF cwstr AS CWSTR, BYREF n AS LONG )AS CWSTR
CWSTR_DP("CWSTR -LEFT FUNCTION-")
IF cwstr.m_BufferLen = 0 or n <= 0 THEN
FUNCTION = ""
EXIT FUNCTION
ELSEIF n > cwstr.m_BufferLen THEN
FUNCTION = CAST(WSTRING PTR, cwstr.m_pBuffer)
EXIT FUNCTION
END IF
DIM pNewBuffer AS WSTRING PTR = cast(WSTRING PTR , cwstr.m_pBuffer)
dim as ubyte u1, u2
u1 = cwstr.m_pBuffer[(n * 2)]
u2 = cwstr.m_pBuffer[(n * 2) + 1]
pNewBuffer[n] = 0
FUNCTION = pNewBuffer
cwstr.m_pBuffer[(n * 2)] = u1
cwstr.m_pBuffer[(n * 2) + 1] = u2
END FUNCTION all in the attachment : CWSTR2.inc and test extended
About speed, do you agree, in fact it is the most important thing
> for me CWSTR class is not really different than dynamic String, lets compare the structure elements
The difference if that the intrinsic functions of the FB compiler can access the members of the FB string structure through the string descriptor. With m_pBuffer alone, you don't have access to the other variables of the class.
My use of @ and * is consistent with other data types such CBSTR and CSafeArray. These data types need an operator to return the address of the variable that holds the pointer and another operator to return the pointer. Using a system with CWSTR and another with CBSTR and CSafeAray is inconsistent. Both BSTR and SafeArray have descriptors.
> About speed, do you agree, in fact it is the most important thing
The faster way is to use + or **
Anyway, the speed in LEFT, RIGHT is not very important because they are used sparely and usually to return small strings.
Jose ,
I'm speaking about speed of CWSTR class, globally , construction , let ...
I will send a meesage directly
I also noticed here as you before, event the CWSTR is a core element, because it gives the dynamic unicode string type,
only 2 people give it some attention!
:-\
Regarding the & operator, I have noticed the following
This works without having to use **cwsText
DIM cws AS CWSTR
DIM cwsText AS CWSTR = "test string"
cws = "Line " & 1 & ", Column " & 2 & ": " & cwsText
print cws
This also works
DIM cws AS CWSTR
DIM cwsText AS CWSTR = "test string"
cws = "Line " & STR(1) & ", Column " & STR(2) & ": " & cwsText
print cws
This fails unless we use **cwsText
DIM cws AS CWSTR
DIM cwsText AS CWSTR = "test string"
cws = "Line " & WSTR(1) & ", Column " & WSTR(2) & ": " & cwsText
print cws
The behavior of this operator is somewhat erratic. The + operator does not give problems.
Jose
my last evolution / optimized /simplified , DWSTR class
Renamed to be able to use it with your CWSTR class on same code to compare speed
the reference CWSTR is the one you post on the RC 24 evolution
i put it to simplify on the attached file with my_DWSTR.inc and code to compare
+ 2 screenshots of the results on my old XP machine (tested with 1 000 000 steps)
I've noticed also some "not so clear" points about index for insert , get charcode...
i've modified them in my code, but not traced everything : sorry
I have changed UBYTE to USHORT in the Char properties. Thanks for spotting it.
My use of the @ and * operators in the CWSTR class is in accordance with what other languages such C++ do. For example, the MFC CComBstr class (C++ uses the & operator instead of @): https://msdn.microsoft.com/en-us/library/5s6et3yb.aspx
Quote
CComBSTR::operator &
Returns the address of the BSTR stored in the m_str member
When we do DIM
cws AS CWSTR,
cws is NOT the string variable, but the class. We can't pass a pointer to
cws to an external third party function because that function has no idea of what a CWSTR is. We have to pass a pointer to the null terminated string that is stored in the
m_pBuffer member. Therefore, the @ operator returns the address of the stored null terminated variable, not a pointer to the class.
The * operator acts like STRPTR, that is, it returns the
m_pBuffer pointer (a pointer to the beginning of the string data).
Using the FB string data type, we can do:
DIM s AS STRING = "Test string"
DIM p AS ZSTRING PTR = STRPTR(s)
PRINT *p
Using CWSTR, we can do:
DIM cws AS CWSTR = "Test string"
DIM p AS WSTRING PTR = *cws
PRINT *p
That is similar to the first one, but using * instead of STRPTR.
But we can also use the ** shortcut:
DIM cws AS CWSTR = "Test string"
PRINT **cws
that does
DIM p AS WSTRING PTR = *cws
PRINT *p
in a single step.
I always have found ANNOYING to have to use VARPTR and STRPTR (too much typing for my taste), and was jealous of C++ programers that can use & and *.
Anyway, with the latest changes you can use LEFT, RIGHT, VAL and & without having to use **cws.
The Capacity allows to preallocate the size of the buffer if you know the size of the result string or even an approximation to avoid multiple allocations.
The CodePage is not useless. Just because you can build a CWSTR concatenating strings with different code pages, doesn't mean that you have to. If you really need the use of a code page, you will have to use a function like AfxUcode, that allows to specify the code page. Free Basic should add an optional code page parameter to STR and WSTR.
These are the results of your test in my computer. As I said, using + or ** with CWSTR is faster than using &, so why not just use +?. The other differences are not significant: just a few milliseconds in a million of concatenations and assignments.
Finally, in your faster & version, if I'm not wrong you're creating memory leaks.
You allocate a buffer
pNewBuffer = allocate((ust1.m_BufferLen + ust2.m_BufferLen + 1) * sizeof(wstring))
and you return a string
RETURN DWSTR(cast(ubyte ptr, pNewBuffer), ust1.m_BufferLen + ust2.m_BufferLen)
constructed using this code
ResizeBuffer(size1)
m_BufferLen = len1
if len1 + 1 > size1 THEN m_BufferLen = size1 - 1
memcpy(m_pBuffer , cast(any ptr, pub), m_BufferLen * 2)
' Mark the end of the string with a double null
m_pBuffer[m_BufferLen] = 0
and pNewBuffer is never freed.
sorry Jose you are wrong, pNewBuffer is deallocated by the normal DWSTR destructor
check the code ,used with the & constructor we dont put size1 var ; so size1 = -1 ; so you enter in the second condition
as you can see if you add the lines to print "here 1";"here 2";"here 3" on the constuctor
PRIVATE CONSTRUCTOR DWSTR (BYREF pub AS UBYTE PTR, BYREF len1 AS LONG, BYREF size1 AS LONG = -1) '****
DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR UBYTE - " & WSTR(pub))
IF size1 = 0 THEN
this.ResizeBuffer(m_GrowSize)
print "here 1"
elseif size1 < 0 THEN ' we enter here with my & operator
this.m_BufferLen = len1
this.m_pBuffer = cast(wstring ptr, pub)
this.m_Capacity = len1 + 1
print "here 2"
ELSE
ResizeBuffer(size1)
m_BufferLen = len1
if len1 + 1 > size1 THEN m_BufferLen = size1 - 1
memcpy(m_pBuffer , cast(any ptr, pub), m_BufferLen * 2)
' Mark the end of the string with a double null
m_pBuffer[m_BufferLen] = 0
print "here 3"
END IF
DWSTR_DP("-END- DWSTR CONSTRUCTOR UBYTE - " & WSTR(this.m_pBuffer))
END CONSTRUCTOR
that means the pointer is not copied to another mem , it is just affected to m_pBuffer and when the destructor will act the m_pBuffer ( so in fact the original pNewBuffer) will be deallocated normally
it would be even faster if we could avoid the following
LET action wich reallocate again ( for nothing)
i've tried to avoid the LET after but it is too complex for the benefit...
see on your nice debug trace system
QuoteOPERATOR & DWSTR : WSTRING & DWSTR
+++BEGIN- DWSTR CONSTRUCTOR UBYTE - 3361376
here 2
-END- DWSTR CONSTRUCTOR UBYTE - 3361376
DWSTR LET DWSTR
DWSTR Clear
DWSTR Add DWSTR - LEN = 26
DWSTR AppendBuffer 0 26
--END - DWSTR AppendBuffer 26
***DWSTR DESTRUCTOR - buffer: 3361376
DWSTR & new operator : same or faster than de-referenced solution
DWSTR CAST BYREF AS WSTRING - buffer: 3359768
completing the analyse :
QuoteUsing the FB string data type, we can do:
DIM s AS STRING = "Test string"
DIM p AS ZSTRING PTR = STRPTR(s)
PRINT *p
Using CWSTR, we can do:
DIM cws AS CWSTR = "Test string"
DIM p AS WSTRING PTR = *cws
PRINT *p
Using DWSTR, we can do:
DIM dws AS DWSTR = "Test string"
DIM pw AS WSTRING PTR = STRPTR(dws)
PRINT *pw
That is exactly as the first one
we can also use the ** shortcut:
DIM cws AS CWSTR = "Test string"
PRINT **cws 'explicit double dereferenced
' or better PRINT cws ' because the implicit cast : byref to wstring
let's see with DWSTR
DIM dws AS DWSTR = "Test string"
PRINT *dws 'explicit dereferenced noticed only 1 dereferecing
'or better PRINT dws 'same implicit cast: byref to wstring
that does for CWSTR
DIM p AS WSTRING PTR = *cws
PRINT *p
with normal string
DIM ps AS ZSTRING PTR = strptr(s)
PRINT *ps
and for DWSTR
DIM pdw AS WSTRING PTR = strptr(dws) ' a agree strptr is longuer to type than *
PRINT *pdw
but with DWSTR :
STRPTR working as with normal string to get the WSTRING ptr
VARPTR working as with normal string to get the DWSTR ptr, or even just : @DWSTR
2 different approchs
CWSTR more c++ behaviour , coherent with all your framework and their types construction
DWSTR a dynamic WSTRING with almost "native" BASIC type behaviour
my purpose from the begining : to have a dynamic Wstring and use it as we do with normal string, without speed cost (or very marginal)
QuoteThe CodePage is not useless. Just because you can build a CWSTR concatenating strings with different code pages, doesn't mean that you have to.
If you really need the use of a code page, you will have to use a function like AfxUcode, that allows to specify the code page. Free Basic should add an optional code page parameter to STR and WSTR.
m_codePage on CWSTR type :
why is it needed to store it, as the resulting CWSTR can be a mix with possible different codepage ?
what the benefit for CWSTR , or for the output to convert it back if it could be completly wrong ?
AfxUcode is very good function , i've tried to overload the wstr with it but without sucess
in the reverse , converting CWSTR to ansi, the only way i can considere
is defining explicitly the codepage if you want a specific one,
or use the normal system codepage
so what the need for storing the codepage information into CWSTR, if we can't trust that info ,
and in fact it is not even needed , your afxAcode (if correct) is playing perfectly the role
I think that it is evident: it is used by the Add and Insert functions if you pass an ansi string and don't specify a code page when you call them. Which benefit will you have removing it?
> my purpose from the begining : to have a dynamic Wstring and use it as we do with normal string
I almost never use what you call a "normal" string, which is everything but normal, because its format is proprietary. The only use that I have for it is as a buffer for binary content if I don't want to allocate the memory using Allocate.
I ill see what I can do with the & operator.
You seem only interested in CWSTR, but I'm also interested in CBSTR, and what you suggest for CWSTR can't be applied to CBSTR, and I like consistency. If I have removed it from the posted framework is only because nobody is willing to test, so I will keep my research about COM programming with FB private. Doesn't make sense to inundate this forum with new code if only I use it.
> it would be even faster if we could avoid the following LET action wich reallocate again ( for nothing)
i've tried to avoid the LET after but it is too complex for the benefit...
It does not reallocate. It is called to assign the result to the CWSTR in "cws =". The problem with functions that return strings is that they must be copied to the target variable. The fastest way is to pass a variable by reference, as most Windows API functions do, but it is much less comfortable to use.
Jose,
just to let you know, my DWSTR , is posted on the FreeBasic Forum
http://www.freebasic.net/forum/viewtopic.php?f=17&t=24070&start=15#p226207 (http://www.freebasic.net/forum/viewtopic.php?f=17&t=24070&start=15#p226207)
intended to work with Windows & Linux
Thanks for all your support, I will see if some interrest on the subject.
Marc