• 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

With the classes that use COM, no globals are needed because when you call CoInitialize, if the COM library has already been initialized it returns S_FALSE instead of failing, and you have to call CoUninitialize for each call to CoInitialize that returns S_OK or S_FALSE.

Therefore, on the constructor I do:


' // Initialize the COM library
DIM hr AS HRESULT = CoInitialize(NULL)
IF hr = S_OK OR hr = S_FALSE THEN m_bUninitCOM = TRUE


and in the destructor:


IF m_bUninitCOM THEN CoUninitialize


In the case of the ODBC classes, I also had to use a couple of globals:


DIM AFX_ODBC_hEnv AS SQLHANDLE        ' Environment handle
DIM AFX_ODBC_numConnections AS LONG   ' Number of connections


and it was unavoidable because each application can oly have an environment handle.

Anyway, given the names used for them, name conflicts are impossible.

Paul Squires

Quote from: Jose Roca on July 19, 2017, 06:25:04 PM
> Instead of using globals could you create the variables as STATIC member variables of the TYPE and then access them using the BASE keyword in subsequent class instances?

No. Each instance of a class gets its own allocation. Extending from another class you get access to its methods, but not to its data (that is, you save some coding because you don't have to reimplement the methods of the inherited class, but nothing else). Either I have to use globals or allow that each instance of any of the classes will initialize and shutdown GDI+. That works, but don't know what the speed penalty will be.


Hmmmm... then how about this approach. Create a base class called "ReferenceCount" that has methods for increment and decrement the count, and derive your GDI classes from that class. That approach would be similar to COM's use of IUnknown that has the two functions AddRef and Release.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#77
Same problem. Each class will get its own data. When you extend a FB type, you get a copy of its public methods and variables, but the variables are uninitialized. I think that it is normal behavior. Classes are different because you can implement different interfaces in the same class, but these FB types are not real classes and don't allow to implement interfaces.


José Roca

To make it clear. If we create an instance of the CGpGraphics type, that extends the CGdipBase, it initializes GDI+ and sets the reference count to 1. Now, if we create an instance of CGpPen, that also inherits from CGpBase, the variable that keeps the count, lets say cRef, won't have a value of 1, but 0. Therefore, CGpPen will also initialize GDI+ and keep a count of 1. To access the data of the instance of CGpGraphics, we would need to have a pointer to it and get the data using this pointer.

Paul Squires

Thanks Jose, I haven't been close to my development computer the past couple of days so I have been throwing out ideas without any actual testing whatsoever.  :)

The last non-global idea I'll toss out there is this....

How about creating a STATIC member function in the TYPE that uses STATIC variables within that function to track the reference count. A STATIC member function is shared throughout all instances of the class inheritance rather than creating duplicates each time.

Something like this (off the top of my head):

STATIC Function CGdipBase.ReferenceCount( Byval IncrementValue As Long ) As Long

   Static refCount As Long

   refCount = refCount + IncrementValue

   Select Case refCount
      Case 1
      ' Initialize
      Case 0
      ' Destroy/Uninitalize
   End Select

   Function = 0
End Function

In the Constructor
this.ReferenceCount(+1)

In the Destructor
this.ReferenceCount(-1)

Maybe I am way off base but you never know. I can actually test this tomorrow (Friday).

Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Paul Squires

Jose, here is some code that should demonstrate what I was trying to convey in the previous post. It should encapsulate the reference counting situation.



type MyObject
   Private:
      dummy as Long
     
   Public:
      declare static Function ReferenceCount( Byval IncrementValue As Long ) As Long
      declare constructor
      declare destructor
   
END TYPE


STATIC Function MyObject.ReferenceCount( Byval IncrementValue As Long ) As Long

   Static refCount As Long = 0    ' start as uninitialized

   if refCount = 0 THEN
      ' Initialize
         ? "Initialize"
   END IF
   refCount = refCount + IncrementValue

   ' If after decrementing the reference count we are at zero then
   ' call the destroy/unitialize functions.
   if refCount = 0 THEN
      ' Destroy/Uninitalize
         ? "Destroy"
   End if

   ? "Reference Count = "; refCount
   
   Function = 0
End Function

Constructor MyObject()
   this.ReferenceCount(+1)
end constructor

Destructor MyObject()
   this.ReferenceCount(-1)
end destructor



' TEST CODE
function CreateObjects() as long
   dim myobj(9) as MyObject     
   ' The DIM will create 10 instances of the object. When this function exits then
   ' the objects will go out scope thereby calling the destructors of each object
   ' and decrementing the reference count.
   function = 0
end function

CreateObjects

sleep


Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#81
Interesting. I didn't know that they will share the static data.

José Roca

I have changed the code to:


' ========================================================================================
' Default constructor
' ========================================================================================
PRIVATE CONSTRUCTOR CGpBase
   CGDIP_DP("CONSTRUCTOR CGpBase")
   ' // Increase the reference count
   this.RefCount(TRUE)
END CONSTRUCTOR
' ========================================================================================

' ========================================================================================
' Destructor
' ========================================================================================
PRIVATE DESTRUCTOR CGpBase
   CGDIP_DP("DESTRUCTOR CGpBase")
   ' // Decrease the reference count
   this.RefCount(FALSE)
END DESTRUCTOR
' ========================================================================================

' ========================================================================================
' Increases/decreases the reference count and initializes or shutdowns GDI+ when required.
' ========================================================================================
PRIVATE FUNCTION CGpBase.RefCount (BYVAL bIncrement AS BOOLEAN) AS LONG
   STATIC cRef AS LONG, token AS ULONG_PTR
'   CGDIP_DP("--- BEGIN CGpBase - RefCount - cRef = " & WSTR(cRef))
   IF cRef = 0 THEN
      ' // Initialize the GDI+ library
      DIM StartupInput AS GdiplusStartupInput
      DIM version AS ULONG = 1
      IF AfxWindowsVersion >= 600 THEN version = 2
      StartupInput.GdiplusVersion = version
      m_Status = GdiplusStartup(@token, @StartupInput, NULL)
      IF m_Status <> 0 THEN MessageBoxW(0, "GDI+ initialization failed - Error: " & STR$(m_Status), "Error", MB_OK OR MB_ICONERROR OR MB_APPLMODAL)
      CGDIP_DP("+++ CGpBase.RefCount Initialize GDI+ - version = " & WSTR(version) & "; token = " & WSTR(token))
   END IF
   ' // Increase or decrease the reference count
   IF bIncrement THEN cRef += 1 ELSE cRef -= 1
   CGDIP_DP("--- END CGpBase - RefCount - cRef = " & WSTR(cRef))
   ' // If the reference count reaches a value of 0, shutdown GDI+
   IF cRef = 0  THEN
      ' // Shutdown GDI+
      IF token THEN GdiplusShutdown(token)
      token = 0
      CGDIP_DP("+++ CGpBase.RefCount Shutdown GDI+")
   END IF
   RETURN cRef
END FUNCTION
' ========================================================================================



And it works fine.

Thanks very much for the idea.

Paul Squires

Quote from: Jose Roca on July 22, 2017, 04:17:05 AM
Thanks very much for the idea.

Feels good to be able to help you for a change :)  Wish i could contribute more to your efforts to create such a great code base for the FB language. 
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer