• Welcome to PlanetSquires Forums.
 

CWindow Release Candidate 29

Started by José Roca, July 01, 2017, 04:21:09 PM

Previous topic - Next topic

José Roca

Please note that because my CWindow version  is DPI aware, the graphics are rendered with better quality than yours. If you are working at 96 DPI, you won't notice the difference, but at 192 DPI it is very appreciable.

José Roca

I have modified the default value for numeric variants from LongInt to Long. Long is more widely used in COM servers, whereas LongInt has some problems with some servers.

I also have adapted my CDicObj class (Dictionary object - associative arrays), to use CVAR. Seems to work fine.

Example:


'#define _CBDICOBJ_DEBUG_ 1
#define _CVAR_DEBUG_ 1
#include "Afx/CDicObj.inc"
using Afx

' // Create an instance of the CDicObj class
DIM pDic AS CDicObj
IF pDic.DispPtr = NULL THEN END

pDic.Add "a", "Athens"
pDic.Add "b", "Madrid"
pDic.Add "c", "Roma"

print pDic.Count
print pDic.Exists("a")
print pDic.Item("a").ToStr
DIM s AS STRING = pDic.Item("b").ToStr
print s, "..."

print
print "-------------"
print

DIM cvItems AS CVAR = pDic.Items
FOR i AS LONG = cvItems.GetLBound TO cvItems.GetUBound
   print cvItems.GetVariantElem(i).ToStr
NEXT

print
print "-------------"
print

DIM cvKeys AS CVAR = pDic.Keys
print "---------"
print "Keys count: ", cvKeys.GetElementCount
print "Dimensions: ", cvKeys.GetDim
print "Ipper bound: ", cvKeys.GetUBound
FOR i AS LONG = cvKeys.GetLBound TO cvKeys.GetUBound
   print cvKeys.GetVariantElem(i).ToStr
NEXT

print
print "-------------"
print

pDic.RemoveAll
DIM cv AS CVAR
cv = CVAR(929292929929299292, "LONGINT")
pDic.Add 1, cv
print pDic.Item(1).ToStr
pDic.Add 2, 1234567.12
print pDic.Item(2).ToStr
pDic.Add 123.12, "Roma"
print pDic.Item(123.12).ToStr
pDic.Add 1234567890123456789, "*** Roma ***"
print pDic.Item(1234567890123456789).ToStr

PRINT
PRINT "Press any key..."
SLEEP


José Roca

I have finished the new CBSTR class.

For those that like to use **, I have added LeftChars, RightChars, MidChars and several Valxxx methods.

Therefore, instead of LEFT(**cbs, 3), you can use cbs.LeftChars(3). The attemps to workaround it is what messed my previous code until I said enough.

Assignment from a CBSTR to a CWSTR, or viceversa, can be done using *

DIM cws = "Test string"
DIM cbs AS CBSTR = *cws

or using cbs = cws.sptr

A new function to check if it is a BSTR:


' ========================================================================================
' // Checks if the passed pointer is a BSTR.
' // Will return FALSE if it is a null pointer.
' // If it is an OLE string it must have a descriptor; otherwise, don't.
' // Get the length in bytes looking at the descriptor and divide by 2 to get the number of
' // unicode characters, that is the value returned by the FreeBASIC LEN operator.
' // If the retrieved length if the same that the returned by LEN, then it must be an OLE string.
' ========================================================================================
FUNCTION AfxIsBstr (BYVAL pv aS ANY PTR) AS BOOLEAN
   IF pv = NULL THEN RETURN FALSE
   DIM res AS DWORD = PEEK(DWORD, pv - 4) \ 2
   IF res = LEN(*cast(WSTRING PTR, pv)) THEN RETURN TRUE
END FUNCTION
' ========================================================================================


Now that we have variants and BSTR support, I can recover the CSafeArray and the ADO classes. Later I will start working with the WMI classes, that promise to be very powerful.

The next version will be as hot as this summer :)

aloberr

  Yes that goes, I thought that the initialization of GDIplus was done internally in the principal class, and same USING GDIPLUS too.  Finally I did not pay attention. 
A remark why the pointers CGpPen Ptr in the Drawline procedure for example that could have been simply CGpPen

José Roca

> A remark why the pointers CGpPen Ptr in the Drawline procedure for example that could have been simply CGpPen

It could have been if FB had not the nasty habit of passing an address to optional BYREF parameters. In PowerASIC I can check with VARPTR if the parameter has been omitted, e.g. IF VARPTR(x) = 0 THEN..., but with FB not because VARPTR(x) will always return a value <> 0. As in many of the methods I need to know if the parameter is null, I have used the same syntax in all of them. It would have been baffling to have to use one syntax with some methods and another with others.

Years ago, there was an attempt to adopt this FB VARPTR behavior in PowerBASIC, but I strongly opposed to it and my arguments won. it is silly to pass the address of a temporary variable for an omitted optional BYREF parameter instead of passing a null pointer, only to please some programmers that complain if a program crashes because they have tried to deference a null pointer.

José Roca

> I thought that the initialization of GDIplus was done internally in the principal class

There is not a principal class. They are independent. They follow as close as possible the C++ GDI+ classes.

> and same USING GDIPLUS too.

It is. They include AfxGdiPlus.inc, where you can find:


#ifdef __FB_64BIT__
    #inclib "gdiplus"
    #include once "win/gdiplus-c.bi"
#else
    #include once "win/ddraw.bi"
    #include once "win/gdiplus.bi"
    using gdiplus
#endif


José Roca

#36
A prerelease of version 30.

This is a big update:

Added support for BSTR: CBSTR class in CWSTR.inc.
Added suport for variants: CVAR class in CVAR.inc.
CDispInvoke class in CDispInvoke.inc: Allows to work with COM Automation.
CSafeArray class in CSafeArray.inc: Safe arrays support.
CDicObj class in CDicObj.inc: Dictionary object (associative arrays).
ADO classes in various files: folder Afx/CADODB.
CWinHttpRequest: Modified to work with CBSTR.
CTextStream: Modified to work with CBSTR.
CFileSys: Modified to work with CBSTR.
CRegExp: Modified to work with CBSTR.
CCDOMail: Modified to worl with CBSTR.
CGraphCtx: Modified to support OPENGL.

Will have to do more tests and update the documentation before posting it as version 30.

Meanwhile, if somebody is willing to test it (you don't need a visual designer), I can post many examples. Otherwise, why bother?


José Roca

#37
If you look at the source code and find a function like this one


PRIVATE FUNCTION CFileSys.GetDriveName (BYREF cbsPathSpec AS CBSTR) AS CBSTR
   IF m_pFileSys = NULL THEN RETURN ""
   DIM bstrName AS AFX_BSTR
   SetResult(m_pFileSys->GetDriveName(cbsPathSpec, @bstrName))
   RETURN bstrName
END FUNCTION


It may lead you to think that there is a memory leak because bstrName is not being freed with SysFreeString. Nope. We must not free it because its contents aren't copied, but its handle is attached to the returning CBSTR class, that will free the BSTR when the class is destroyed.

I'm using this technique for speed, because FUNCTION = bstrXXX, followed by SysFreeString(bstrXXX) creates a temporary CBSTR that will be returned as the result of the function at the very end, when the BSTR handle is not longer valid because it has been freed. RETURN bstrXXX works correctly, BUT then SysFreeString is not executed. PROPERTY = bstrXXX works as we like, making the returned temporary CBSTR BEFORE SysFreeString is executed, BUT the number of parameters is limited to two... One workaround would be to make a temporary copy and return it, e.g. DIM cbs AS CBSTR = bstrXXX : FUNCTION = cbs : SysFreeString(bstrXXX), BUT this is slow because we copy the data twice, first when we create the temporary CBSTR, and later when we return it.

Therefore, I'm using an hack in the CBSTR constructor that receives the handle to check if the handle belongs to a BSTR or not, and if it does, it attaches the handle to the class.


PRIVATE CONSTRUCTOR CBStr (BYREF bstrHandle AS AFX_BSTR = NULL, BYVAL fAttach AS LONG = TRUE)
   CBSTR_DP("--BEGIN CBSTR CONSTRUCTOR AFX_BSTR - handle: " & .WSTR(bstrHandle) & " - Attach: " & .WSTR(fAttach))
   IF bstrHandle = NULL THEN
      m_bstr = SysAllocString("")
      CBSTR_DP("CBSTR CONSTRUCTOR SysAllocString - " & .WSTR(m_bstr))
   ELSE
      ' Detect if the passed handle is an OLE string
      ' If it is an OLE string it must have a descriptor; otherwise, don't
      ' Get the length in bytes looking at the descriptor and divide by 2 to get the number of
      ' unicode characters, that is the value returned by the FreeBASIC LEN operator.
      DIM Res AS INTEGER = PEEK(DWORD, CAST(ANY PTR, bstrHandle) - 4) \ 2
      ' If the retrieved length if the same that the returned by LEN, then it must be an OLE string
      IF Res = .LEN(*bstrHandle) AND fAttach <> FALSE THEN
         CBSTR_DP("CBSTR CONSTRUCTOR AFX_BSTR - Attach handle: " & .WSTR(bstrHandle))
         ' Attach the passed handle to the class
         m_bstr = bstrHandle
      ELSE
         CBSTR_DP("CBSTR CONSTRUCTOR AFX_BSTR - Alloc handle: " & .WSTR(bstrHandle))
         ' Allocate an OLE string with the contents of the string pointer by bstrHandle
         m_bstr = SysAllocString(*bstrHandle)
      END IF
   END IF
   CBSTR_DP("--END CBSTR CONSTRUCTOR AFX_BSTR - " & .WSTR(m_bstr))
END CONSTRUCTOR


Johan Klassen


Richard Kelly

Whatever examples you post Jose, I'll run 'em.

Rick

Johan Klassen

Jose Roca
I know how disappointing it is to have no participation, it's almost like trying to have a conversation with oneself, but I hope that you won't give up.

José Roca

I already have given alternatives to most of the PowerBASIC features missing in FreeBasic. TCP support is still missing, but I have some ideas and code for a lightweight class on top of WinSock.

However, the first item in my to do list are the WMI classes, because WMI is a very powerful technology. The WMI classes will allow me to develop further classes.

José Roca

#42
I already have a WMI class to get information, e.g.


#define _CVAR_DEBUG_ 1
#include "windows.bi"
#include "Afx/CWmiDisp.inc"
using Afx

' // Connect to WMI using a moniker
DIM pServices AS CWmiServices = $"winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2"
IF pServices.DispPtr = NULL THEN END

' // Execute a query
DIM hr AS HRESULT = pServices.ExecQuery("SELECT * FROM Win32_BIOS")
IF hr <> S_OK THEN PRINT AfxWmiGetErrorCodeText(hr) : END

' // Get the number of objects retrieved
DIM nCount AS LONG = pServices.ObjectsCount
' // Parse the collection
IF nCount THEN
   DIM pDispServ AS CDispInvoke = pServices.NextObject
   IF pDispServ.DispPtr THEN
      PRINT "BIOS version: : "; pDispServ.Get("BIOSVersion").ToStr
      PRINT "BIOS characteristics:"; pDispServ.Get("BIOSCharacteristics").ToStr
      PRINT "Build number: "; pDispServ.Get("BuildNumber").ToStr
      PRINT "Caption: "; pDispServ.Get("Caption").ToStr
      PRINT "Current language: "; pDispServ.Get("CurrentLanguage").ToStr
      PRINT "Description: "; pDispServ.Get("Description").ToStr
      PRINT "Identification code: "; pDispServ.Get("IdentificationCode").ToStr
      PRINT "Installable languages: "; pDispServ.Get("InstallableLanguages").ToStr
      PRINT "Install date: "; pDispServ.Get("InstallDate").ToStr
      PRINT "Language edition: "; pDispServ.Get("LanguageEdition").ToStr
      PRINT "List of languages: "; pDispServ.Get("ListOfLanguages").ToStr
      PRINT "Manufacturer: "; pDispServ.Get("Manufacturer").ToStr
      PRINT "Other target OS: "; pDispServ.Get("OtherTargetOS").ToStr
      PRINT "Primary BIOS: "; pDispServ.Get("PrimaryBIOS").ToStr
      PRINT "Release date: "; AfxWmiTimeToDateStr(pDispServ.Get("ReleaseDate"), "dd-MM-yyyy")
      PRINT "Serial number: "; pDispServ.Get("SerialNumber").ToStr
      PRINT "SMBIOS BIOS version: "; pDispServ.Get("SMBIOSBIOSVersion").ToStr
      PRINT "SMBIOS major version: "; pDispServ.Get("SMBIOSMajorVersion").ToStr
      PRINT "SMBIOS minor version: "; pDispServ.Get("SMBIOSMinorVersion").ToStr
      PRINT "SMBIOS present: "; pDispServ.Get("SMBIOSPresent").ToStr
      PRINT "Software element ID: "; pDispServ.Get("SoftwareElementID").ToStr
      PRINT "Software element state: "; pDispServ.Get("SoftwareElementState").ToStr
      PRINT "Target operating system: "; pDispServ.Get("TargetOperatingSystem").ToStr
      PRINT "Version: "; pDispServ.Get("Version").ToStr
   END IF
END IF

PRINT
PRINT "Press any key..."
SLEEP


Next I will write another class to set values, e.g.


' // Connect to WMI using a moniker
DIM pServices AS CWmiServices = $"winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2"
DIM pWmiObject AS CWmiObject = pServices.Get(<class name>, e.g. "Win32_Printer")
pWmiObject.Value(<property name>, e.g. "PortName") = <value>


These classes will replace the CWmiCli one, that only works with the local computer. Using a moniger apparently makes easy connecting to a remote computer, using something like "winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2" instead of "\\.\".

José Roca

#43
Another big advantage of WMI is that we only need to change the name of the class, e.g.:


DIM hr AS HRESULT = pServices.ExecQuery("SELECT * FROM Win32_CDROMDrive")


and now we can access the properties of the CDRom drive.


PRINT "Description: "; pDispServ.Get("Description").ToStr
PRINT "Manufacturer: "; pDispServ.Get("Manufacturer").ToStr
etc.


The rest of the code will be the same.

If pServices.ObjectsCount returns a value greater than 1, it means that there are several collections. We only need to change IF nCount THEN to FOR i AS LONG = 0 TO nCOunt -1.

This means that with almost identical code we can access most of the hardware without having to learn different convoluted APIs.

I don't know how so many Windows programmers can live without knowing how to work with COM when about two thirds of the technologies provided by Windows are in the form of COM servers.

aloberr

jose wrote
QuoteIt could have been if FB had not the nasty habit of passing an address to optional BYREF parameters. In PowerASIC I can check with VARPTR if the parameter has been omitted, e.g. IF VARPTR(x) = 0 THEN..., but with FB not because VARPTR(x) will always return a value <> 0. As in many of the methods I need to know if the parameter is null, I have used the same syntax in all of them. It would have been baffling to have to use one syntax with some methods and another with others.
why do you need optionnal parameter where byval can do the job , for example on the drawline procedure CgPen is the class and the first parameter and can not be optionnal, if it is not assign the procedure used value initialised in the constructor of the class.