• Welcome to PlanetSquires Forums.
 

CWindow Release Candidate 31

Started by José Roca, August 06, 2017, 02:51:36 PM

Previous topic - Next topic

José Roca

#60
It is impossible to work with that tool at 192 DPI. I have uninstalled it.

They must think that to make an application DPI aware you only have to do scaling, without worrying about anything else, such making the controls accessible. Holy crap!



José Roca

#61
As an alternative to arrays and/or dictionary objects, we can use ADO to create in-memory recordsets. Some advantages of using ADO over SQLite for this task is that we don't need to use a third party DLL and that we can save a recordset to disk as a stream or as XML just calling Save or SaveAsXml.

A sort test:


#include "Afx/CADODB/CADODB.inc"
using Afx

' // Create an instance of the CAdoRecorset class
DIM pRecordset AS CAdoRecordset
' // Get a reference to the Fields collection
DIM pFields AS CAdoFields = pRecordset.Fields

pFields.Append("Key", adVarChar, 10)
pFields.Append("Item", adVarChar, 20)

pRecordset.CursorType = adOpenKeyset
pRecordset.CursorLocation = adUseClient
pRecordset.LockType = adLockOptimistic
pRecordset.Open

pRecordset.AddNew
   pRecordset.Collect("Key") = "One"
   pRecordset.Collect("Item") = "Item one"
'pRecordset.Update
' Don't call Update or it will add an additional empty record

pRecordset.AddNew
   pRecordset.Collect("Key") = "Two"
   pRecordset.Collect("Item") = "Item two"
'pRecordset.Update

print "Record count: ", pRecordset.Recordcount

pRecordset.MoveFirst
DO
   IF pRecordset.EOF THEN EXIT DO
   PRINT pRecordset.Collect("Key").ToStr
   PRINT pRecordset.Collect("Item").ToStr
   IF pRecordset.MoveNext <> S_OK THEN EXIT DO
LOOP

PRINT
PRINT "Press any key..."
SLEEP


Seek does not work, but we can use Find (to search by name) or AbsolutePosition (to search by ordinal). We can also use other ADO methods such Delete, Sort and Filter.

We can also get all the rows as a two-dimensional safe array, calling the GetRows method, or as an string calling the GetString method, that allows to specify the number of rows to read, a separator (default = tab) and a row delimiter (default = CRLF).

This is a good option for things that we can't do with normal arrays, such having a multi-dimensional array in which each dimension can be of any type.

Paul Squires

Jose do you think that you could add a small text file to your release candidates so that it would be easy to determine what version of the library we have installed? Periods of time pass and I keep forgetting what version of the library I am releasing with WinFBE. If the version was in a small text file, say, "_version.txt", then I could quickly see if I need to upgrade the library before posting a WinFBE release.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

In CWindow.inc you can find:

' File: CWindow.inc
' Version: 1.0
' Release candidate 31

Anyway, I think that it is time to release the first WinFBX version.

I have opened an account in GitHub and will upload the code to it, together with examples, tools or whatever.

I will try to do it using the web interface, because the GitHib Desktop tool is almost unusable at 192 DPI.

José Roca

#64
I have written a class, CCLRHost, to host the .NET 4 runtime. So far it works with .NET system classes, e.g.:


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CCLRHost.inc"
USING Afx

' // Create an instance of the CCLRHost class
DIM pCLRHost AS CCLRHost

' // Create an instance of the .NET stack collection
DIM pDisp AS CDispInvoke = pCLRHost.CreateInstance ("mscorlib", "System.Collections.Stack")
IF pDisp.DispPtr = NULL THEN END

pDisp.Invoke("Push", 1, CVAR("rocks!"))
pDisp.Invoke("Push", 1, CVAR("FreeBasic"))
print pDisp.Get("Pop").ToStr
print pDisp.Get("Pop").ToStr

' To get the count, call
' DIM nCount AS LONG = pDisp.Invoke("Count").ValInt

PRINT
PRINT "Press any key..."
SLEEP


The class also provides the method CreateInstanceFrom that will allow to load an assembly from disk and create an instance of the requested class. However, as I never have used .NET, I can't curently test it because i would need suitable assemblies (bit 32 and 64-bit) to test. If somebody is interested in using .NET assemblies with FreeBasic and can provide suitable assemblies to test, I will continue to develop the class. If there is no interest, it will end being a curiosity as it happened with PowerBasic.

The use of the "System.Collections.Stack" .NET class already allows to replace the PowerBasic Stack collection.

José Roca

BTW notice that I have used a class just to make it easier to use the code, but only an instance of the class can be created because the CLR runtime can only be loaded once in the same process.

Also, Automation and late binding must be used to call the methods and properties of the classes because they are dynamic properties (there is not a virtual table).

José Roca

#66
We an also use the .NET ArrayList class:


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CCLRHost.inc"
USING Afx

' // Create an instance of the CCLRHost class
DIM pCLRHost AS CCLRHost

' // Create an instance of the .NET ArrayList class
DIM pDisp AS CDispInvoke = pCLRHost.CreateInstance ("mscorlib", "System.Collections.ArrayList")
IF pDisp.DispPtr = NULL THEN END

pDisp.Invoke("Add", 1, CVAR("First string"))
pDisp.Invoke("Add", 1, CVAR("Second string"))
pDisp.Invoke("Add", 1, CVAR("Third string"))

DIM nCount AS LONG = pDisp.Invoke("Count").ValInt
FOR i AS LONG = 0 TO nCount - 1
   print pDisp.Get("Item", CVAR(i)).ToStr
NEXT

PRINT
PRINT "Press any key..."
SLEEP


See: https://msdn.microsoft.com/es-es/library/system.collections.arraylist(v=vs.110).aspx

Paul Squires

Wow, this is very interesting. There is so much in .Net that could be used especially if the speed is okay. the .Net runtime ships with Windows so unless you are trying to interface very new functionality then access to .Net functions should (in theory) be no different than accessing WinApi directly. I will definitely be looking into this further.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#68
For overloaded methods, when NET creates the COM callable wrapper it adds an underscore and an ordinal to each one. Therefore, the first overloaded Append method of the "System.Text.StringBuilder" class is called "Append", but the second one "Append_2" and so on.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CCLRHost.inc"
USING Afx

' // Create an instance of the CCLRHost class
DIM pCLRHost AS CCLRHost

' // Create an instance of the .NET StrigBuilder class
DIM pDisp AS CDispInvoke = pCLRHost.CreateInstance ("mscorlib", "System.Text.StringBuilder")
IF pDisp.DispPtr = NULL THEN END

pDisp.Invoke("Append_3", 1, CVAR("Hello"))
pDisp.Invoke("Append_3", 1, CVAR(" World!"))
print pDisp.Invoke("ToString").ToStr

PRINT
PRINT "Press any key..."
SLEEP


As we are using Automation, the call to pDisp.Invoke("ToString") does not return a string, but a VARIANT. Therefore, we have to use pDisp.Invoke("ToString").ToStr to convert the returned variant to a string.

José Roca

#69
> then access to .Net functions should (in theory) be no different than accessing WinApi directly

Well, it is very different. The complexity is hidden in the class. We have to host the .NET runtime and force it to create a COM callable wrapper on the fly that allows to call the methods of the .NET class as if it was a COM object. Also as they are dynamic, we must use Automation and late binding, which is slower than direct calls.

Paul Squires

Quote from: Jose Roca on September 07, 2017, 04:33:25 PM
> then access to .Net functions should (in theory) be no different than accessing WinApi directly

Well, it is very different. The complexity is hidden in the class. We have to host the .NET runtime and force it to create a COM callable wrapper on the fly that allows to call the methods of the .NET class as if it was a COM object. Also as they are dynamic, we must use Automation and late binding, which is slower than direct calls.

Hmmmm.... yes, that does sound like a significant amount of overhead. That's too bad because access to the wealth of functionality within the .net CLR would be quite awesome.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#71
But we are still in the beginning... There is still much to explore.

If we add these attributes to the public classes of our assembly and then we make a COM callable wrapper, that we can register win regasm, it will create dual interfaces that can we used as if it was a COM server, without having to host the .NET runtime.


VB.NET: <ClassInterface(ClassInterfaceType.AutoDual)>
C#:     [ClassInterface(ClassInterfaceType.AutoDual)]


With these attributes, the methods will be visible to COM browsers and we can create interface declarations and use direct calls, although I guess that the COM wrapper will marshall these calls to the .NET assembly.

José Roca

There is a .NET assembly that comes with the .NET framework, called System.dll. It has allowed me to test if the CreateInstanceFrom method of the CCLRHost class works, and it does:


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CCLRHost.inc"
USING Afx

' // Create an instance of the CCLRHost class
DIM pCLRHost AS CCLRHost

' // Create an instance of the .NET WebClient class
DIM pDisp AS CDispInvoke = pCLRHost.CreateInstanceFrom( _
   $"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.dll", _
   "System.Net.WebClient")
IF pDisp.DispPtr = NULL THEN END

DIM cvAddress AS CVAR = $"http://www.jose.it-berater.org/webpages_images/h_2.jpg"
DIM cvFileName AS CVAR = ExePath & $"\h_2.jpg"
DIM cvRes AS CVAR = pDisp.Invoke("DownloadFile", 2, cvAddress, cvFileName)
IF pDisp.GetLastResult <> S_OK THEN
   print "Error: &H"; HEX(pDisp.GetErrorCode)
ELSE
   print "Picture saved"
END IF

PRINT
PRINT "Press any key..."
SLEEP


If we register it with regasm, then we can use it as:


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/AfxCOM.inc"
USING Afx

' // Create an instance of the .NET WebClient class
DIM pDisp AS CDispInvoke = AfxNewCom("System.Net.WebClient")
IF pDisp.DispPtr = NULL THEN END

DIM cvAddress AS CVAR = $"http://www.jose.it-berater.org/webpages_images/h_2.jpg"
DIM cvFileName AS CVAR = ExePath & $"\h_2.jpg"
pDisp.Invoke("DownloadFile", 2, cvAddress, cvFileName)

PRINT
PRINT "Press any key..."
SLEEP


Paul Squires

Quote from: Jose Roca on September 07, 2017, 04:05:04 PM
We an also use the .NET ArrayList class:


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#INCLUDE ONCE "Afx/CCLRHost.inc"
USING Afx

' // Create an instance of the CCLRHost class
DIM pCLRHost AS CCLRHost

' // Create an instance of the .NET ArrayList class
DIM pDisp AS CDispInvoke = pCLRHost.CreateInstance ("mscorlib", "System.Collections.ArrayList")
IF pDisp.DispPtr = NULL THEN END

pDisp.Invoke("Add", 1, CVAR("First string"))
pDisp.Invoke("Add", 1, CVAR("Second string"))
pDisp.Invoke("Add", 1, CVAR("Third string"))

DIM nCount AS LONG = pDisp.Invoke("Count").ValInt
FOR i AS LONG = 0 TO nCount - 1
   print pDisp.Get("Item", CVAR(i)).ToStr
NEXT

PRINT
PRINT "Press any key..."
SLEEP


See: https://msdn.microsoft.com/es-es/library/system.collections.arraylist(v=vs.110).aspx

Works perfectly. I also love how your msdn link takes us to the Spanish webpage version :) :)
Here's the English in case anyone is interested: https://msdn.microsoft.com/en-ca/library/system.collections.arraylist(v=vs.110).aspx

I am surprised with how fast it is even as you say that it must create an instance and use dispatch interface. I added 100,000 strings in about a second or so.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Paul Squires

Quote from: Jose Roca on September 07, 2017, 04:33:25 PM
> then access to .Net functions should (in theory) be no different than accessing WinApi directly

Well, it is very different. The complexity is hidden in the class. We have to host the .NET runtime and force it to create a COM callable wrapper on the fly that allows to call the methods of the .NET class as if it was a COM object. Also as they are dynamic, we must use Automation and late binding, which is slower than direct calls.

Yeah, that's a hell of an interesting looking code base! Amazing.
Is the creating of an instance of the class a time consuming process? Seems to me based on my limited tests that everything runs half decently fast as it is.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer