Wiki improvements

Forum for discussion about the documentation project.
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Wiki improvements

Postby fxm » Jun 02, 2018 8:51

Even after the last article "How to handle reusable FB procedures including source modules (vs. compiled modules)", proposed by Josep Roca, which caused some controversy and sometimes passionate exchanges, all other users should not be discouraged to propose some ideas of new articles (with material if possible).

To be positive, such articles (with controversy) are always useful because they induce many exchanges.
On the other hand, one could also ask the question about unanswered articles, unless one judges their interests by the number of views.
coderJeff
Site Admin
Posts: 3020
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Wiki improvements

Postby coderJeff » Jun 02, 2018 12:38

fxm, there are 3 patterns that I tend to use with types/objects when my program starts to get large and complex.
1) Reference Counting
2) Separate Interface from implementation
3) Single Interface with multiple run-time implementations (kind of like drivers)

A while back I started to write a tutorial for #1 & #2. This is as far as I got: Object lifetime with reference counting

#3 is a little harder to show as it tends to require a consistent structure across multiple modules and includes to set it up.

To a degree, fbc on it's own demonstrates #2 and #3 with FREEFILE/OPEN/PRINT #/CLOSE, so the concept can take different forms.

Not sure where to take it from here.
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Wiki improvements

Postby fxm » Jun 02, 2018 14:53

In response to questions from D.J.Peters, I also proposed a draft of "Reference counting base of all derived-types in inheritance hierarchy".
Josep Roca
Posts: 435
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: Wiki improvements

Postby Josep Roca » Jun 02, 2018 17:45

I did write a template to implement low-level COM servers that, contrarily to Automation servers, are lightweight, as fast as an ordinary DLL, can use any data type, don't need type libraries and can be used without registering them.

Must be compiled as a DLL. The generated xxx.dll.a file isn't needed for anything.

Code: Select all

' // Free Basic source code to a simple COM object, compiled into an ordinary
' // dynamic link library (DLL).

#include once "windows.bi"
#include once "win/ocidl.bi"

' Things to change:
' - The name of the interface
' - CLSID and IID of the inteface
'   - Replace CLSID_IExample and IID_IExample with the names of your election
'   - Replace CLSID_IExample in the DllGetClassObject function
'   - Replace IID_IExample in the QueryInteface method of the class
' - Our virtual functions
' - The variables to store data

' Things to keep:
' - The virtual methods QueryInterface, AddRef and Release
' - The static variables OutstandingObjects and LockCount

' // Our IExample CLSID (class identifier)
' // {6899A2A3-405B-44d4-A415-E08CEE2A97CB}
' // (*** change it***)
DIM SHARED CLSID_IExample AS GUID = TYPE(&h6899A2A3, &h405B, &h44D4, {&hA4, &h15, &hE0, &h8C, &hEE, &h2A, &h97, &hCB})

' // Our IExample IID (interface identifier)
' // {74666CAC-C2B1-4FA8-A049-97F3214802F0}
' // (*** change it***)
DIM SHARED IID_IExample AS GUID = TYPE(&h74666CAC, &hC2B1, &h4FA8, {&hA0, &h49, &h97, &hF3, &h21, &h48, &h02, &hF0})

' // A count of how many objects our DLL has created (by some
' // app calling our IClassFactory object's CreateInstance())
' // which have not yet been Release()'d by the app
' // (***keep it***)
STATIC SHARED OutstandingObjects AS DWORD

' // A count of how many apps have locked our DLL via calling our
' // IClassFactory object's LockServer()
' // (***keep it***)
STATIC SHARED LockCount AS DWORD

' ========================================================================================
' IExample object
' ========================================================================================
TYPE IExample EXTENDS OBJECT
   ' Functions for the IUnknown Interface (*** keep them ***)
   DECLARE VIRTUAL FUNCTION QueryInterface (BYVAL riid AS CONST IID CONST PTR, BYVAL ppvObj AS ANY PTR PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION AddRef () AS ULONG
   DECLARE VIRTUAL FUNCTION Release () AS ULONG
   ' Our functions (*** change them ***)
   DECLARE VIRTUAL FUNCTION SetString (BYVAL pwsz AS WSTRING PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION GetString (BYVAL pbuffer AS WSTRING PTR, BYVAL cch AS DWORD) AS HRESULT
   ' Constructor/destructor
   DECLARE CONSTRUCTOR
   DECLARE DESTRUCTOR
   ' Reference count
   cRef AS DWORD ' (*** keep it ***)
   ' Data (*** change it ***)
   buffer AS WSTRING * 80
END TYPE
' ========================================================================================

' ========================================================================================
' IExample constructor
' ========================================================================================
CONSTRUCTOR IExample
END CONSTRUCTOR
' ========================================================================================

' ========================================================================================
' IExample destructor
' ========================================================================================
DESTRUCTOR IExample
END DESTRUCTOR
' ========================================================================================

' ========================================================================================
' IExample's QueryInterface
' (***change IID_IExample***)
' ========================================================================================
FUNCTION IExample.QueryInterface (BYVAL riid AS CONST IID CONST PTR, BYVAL ppvObj AS ANY PTR PTR) AS HRESULT
   ' // Check if the GUID matches the IID of our interface or the IUnknown interface.
   IF IsEqualIID(riid, @IID_IUnknown) = FALSE AND IsEqualIID(riid, @IID_IExample) = FALSE THEN
      ' // We don't recognize the GUID passed to us. Let the caller know this,
      ' // by clearing his handle, and returning E_NOINTERFACE.
      *ppvObj = 0
      RETURN E_NOINTERFACE
   END IF
   ' // Fill in the caller's handle
   *ppvObj = @this
   ' // Increment the count of callers who have an outstanding pointer to this object
   this.AddRef
   RETURN NOERROR
END FUNCTION
' ========================================================================================

' ========================================================================================
' IExample's AddRef
' ========================================================================================
FUNCTION IExample.AddRef() AS ULONG
   ' // Increment IExample's reference count, and return the updated value.
   this.cRef += 1
   RETURN this.cRef
END FUNCTION
' ========================================================================================

' ========================================================================================
' IExample's Release
' ========================================================================================
FUNCTION IExample.Release () AS ULONG
   ' // Decrement IExample's reference count
   this.cRef -= 1
   ' // If 0, then we can safely free this IExample now
   IF this.cRef = 0 THEN
      Delete @this
      InterlockedDecrement(@OutstandingObjects)
      RETURN 0
   END IF
   RETURN this.cRef
END FUNCTION
' ========================================================================================

' ========================================================================================
' IExample's SetString
' This copies the passed string to IExample's buffer
' ========================================================================================
FUNCTION IExample.SetString (BYVAL pwsz AS WSTRING PTR) AS HRESULT
   ' // Make sure that caller passed a buffer
   IF pwsz = NULL THEN RETURN E_POINTER
   ' // Copy the passed str to IExample's buffer
   this.buffer = *pwsz
   RETURN NOERROR
END FUNCTION
' ========================================================================================

' ========================================================================================
' IExample's GetString
' This retrieves IExample's buffer and stores its contents in a buffer passed by the caller.
' ========================================================================================
FUNCTION IExample.GetString (BYVAL pbuffer AS WSTRING PTR, BYVAL cch AS DWORD) AS HRESULT
   ' // Make sure that caller passed a buffer
   IF pbuffer = NULL THEN RETURN E_POINTER
   IF cch THEN
      ' // Let's copy IExample's buffer to the passed buffer
      IF cch > 79 THEN cch = 79
      memcpy(pbuffer, @this.buffer, cch)
   END IF
   RETURN NOERROR
END FUNCTION
' ========================================================================================

' ========================================================================================
' // The IClassFactory object ////////////////////////////////////////////////////////////
' ========================================================================================

' // Since we only ever need one IClassFactory object, we declare
' // it static. The only requirement is that we ensure any
' // access to its members is thread-safe
STATIC SHARED MyIClassFactoryObj As IClassFactory

' // IClassFactory's AddRef()
FUNCTION classAddRef (BYVAL pthis AS IClassFactory PTR) AS ULONG
   ' // Someone is obtaining my IClassFactory, so inc the count of
   ' // pointers that I've returned which some app needs to Release()
   InterlockedIncrement(@OutstandingObjects)

   ' // Since we never actually allocate/free an IClassFactory (ie, we
   ' // use just 1 static one), we don't need to maintain a separate
   ' // reference count for our IClassFactory. We'll just tell the caller
   ' // that there's at least one of our IClassFactory objects in existance
   RETURN 1
END FUNCTION

' // IClassFactory's QueryInterface()
FUNCTION classQueryInterface (BYVAL pthis AS IClassFactory PTR, BYVAL factoryGuid AS CONST IID CONST PTR, BYVAL ppv AS ANY PTR PTR) AS HRESULT
   ' // Make sure the caller wants either an IUnknown or an IClassFactory.
   ' // In either case, we return the same IClassFactory pointer passed to
   ' // us since it can also masquerade as an IUnknown
   IF IsEqualIID(factoryGuid, @IID_IUnknown) OR IsEqualIID(factoryGuid, @IID_IClassFactory) THEN
      ' // Call my IClassFactory's AddRef
      pthis->lpVtbl->AddRef(pthis)
      ' // Return (to the caller) a ptr to my IClassFactory
      *ppv = pthis
      RETURN NOERROR
   END IF
   ' // We don't know about any other GUIDs
   *ppv = 0
   RETURN E_NOINTERFACE
END FUNCTION

' // IClassFactory's Release()
FUNCTION classRelease(BYVAL pthis AS IClassFactory PTR) AS ULONG
   ' // One less object that an app has not yet Release()'ed
   RETURN InterlockedDecrement(@OutstandingObjects)
END FUNCTION

' // IClassFactory's CreateInstance() function. It is called by
' // someone who has a pointer to our IClassFactory object and now
' // wants to create and retrieve a pointer to our IExample
FUNCTION classCreateInstance(BYVAL pthis AS IClassFactory PTR, BYVAL punkOuter AS IUnknown PTR, _
   BYVAL riid AS CONST IID CONST PTR, BYVAL objHandle AS ANY PTR PTR) AS HRESULT

   DIM hr AS HRESULT
   ' // Assume an error by clearing caller's handle
   *objHandle = 0
   ' // We don't support aggregation in this example
   IF punkOuter THEN RETURN CLASS_E_NOAGGREGATION
   ' // Allocate our object (***change the name of the class***)
   DIM thisObj AS IExample PTR = NEW IExample
   ' // Increment the reference count so we can call Release() below and
   '  // it will deallocate only if there is an error with QueryInterface()
   thisobj->cRef = 1
   ' // Fill in the caller's handle with a pointer to the object we just allocated
   ' // above. We'll let the QueryInterface method of the object do that, because
   ' // it also checks the GUID the caller passed, and also increments the
   ' // reference count (to 2) if all goes well
   hr = thisObj->QueryInterface(riid, objHandle)
   ' // Decrement reference count. NOTE: If there was an error in QueryInterface()
   ' // then Release() will be decrementing the count back to 0 and will free the
   ' // IExample for us. One error that may occur is that the caller is asking for
   ' // some sort of object that we don't support (ie, it's a GUID we don't recognize)
   thisObj->Release
   ' // If success, inc static object count to keep this DLL loaded
   IF hr = S_OK THEN InterlockedIncrement(@OutstandingObjects)
   RETURN hr
END FUNCTION

' // IClassFactory's LockServer(). It is called by someone
' // who wants to lock this DLL in memory
FUNCTION classLockServer (BYVAL pthis AS IClassFactory PTR, BYVAL flock AS WINBOOL) AS HRESULT
   IF flock THEN InterlockedIncrement(@LockCount) ELSE InterlockedDecrement(@LockCount)
   RETURN NOERROR
END FUNCTION

STATIC SHARED MyClassFactoryVTbl AS IClassFactoryVTbl = TYPE(@classQueryInterface, _
   @classAddRef, @classRelease, @classCreateInstance, @classLockServer)

' ========================================================================================
' Implementation of the DllGetClassObject and DllCanUnloadNow functions.
' ========================================================================================

EXTERN "windows-ms"

#UNDEF DllGetClassObject
FUNCTION DllGetClassObject ALIAS "DllGetClassObject" (BYVAL objGuid AS CLSID PTR, _
   BYVAL factoryGuid AS IID PTR, BYVAL factoryHandle As VOID PTR PTR) AS HRESULT EXPORT

   DIM hr AS HRESULT
   ' // Check that the caller is passing our interface CLSID.
   ' // That's the only object our DLL implements
   ' // (***change CLSID_IExample***)
   IF IsEqualCLSID(objGuid, @CLSID_IExample) THEN
      ' // Fill in the caller's handle with a pointer to our IClassFactory object.
      ' // We'll let our IClassFactory's QueryInterface do that, because it also
      ' // checks the IClassFactory GUID and does other book-keeping
      hr = classQueryInterface(@MyIClassFactoryObj, factoryGuid, factoryHandle)
   ELSE
      ' // We don't understand this GUID. It's obviously not for our DLL.
      ' // Let the caller know this by clearing his handle and returning
      ' // CLASS_E_CLASSNOTAVAILABLE
      *factoryHandle = 0
      hr = CLASS_E_CLASSNOTAVAILABLE
   END IF
   RETURN hr

END FUNCTION

' * This is called by some OLE function in order to determine
' * whether it is safe to unload our DLL from memory.
' *
' * RETURNS: S_OK if safe to unload, or S_FALSE if not.

' // If someone has retrieved pointers to any of our objects, and
' // not yet Release()'ed them, then we return S_FALSE to indicate
' // not to unload this DLL. Also, if someone has us locked, return
' // S_FALSE

#UNDEF DllCanUnloadNow
FUNCTION DllCanUnloadNow ALIAS "DllCanUnloadNow" () AS HRESULT EXPORT
   RETURN IIF(OutstandingObjects OR LockCount, S_FALSE, S_OK)
END FUNCTION

' ========================================================================================

END EXTERN

' ========================================================================================
' Constructor of the module
' ========================================================================================
SUB ctor () CONSTRUCTOR
'   OutputDebugStringW "DLL loaded"
   ' // Clear static counts
   OutstandingObjects = 0
   LockCount = 0
   ' // Initialize my IClassFactory with the pointer to its VTable
   MyIClassFactoryObj.lpVtbl = @MyClassFactoryVTbl
END SUB
' ========================================================================================

' ========================================================================================
' Destructor of the module
' ========================================================================================
SUB dtor () DESTRUCTOR
'   OutputDebugStringW "DLL unloaded"
END SUB
' ========================================================================================
Last edited by Josep Roca on Jun 02, 2018 17:50, edited 2 times in total.
Josep Roca
Posts: 435
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: Wiki improvements

Postby Josep Roca » Jun 02, 2018 17:46

Usage example:
Change the name of the DLL if needed in DIM wszLibName AS WSTRING * MAX_PATH = ExePath & "\IExample2.dll"

Code: Select all

'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "windows.bi"
#include once "win/ocidl.bi"
'#include once "Afx/AfxCOM.inc"
DECLARE FUNCTION AfxSafeRelease (BYREF pv AS ANY PTR) AS ULONG
DECLARE FUNCTION AfxNewCom OVERLOAD (BYREF wszLibName AS CONST WSTRING, BYREF rclsid AS CONST CLSID, BYREF riid AS CONST IID, BYREF wszLicKey AS WSTRING = "") AS ANY PTR

' // Our IExample object's GUID
' // {6899A2A3-405B-44d4-A415-E0 8C EE 2A 97 CB}
DIM CLSID_IExample AS GUID = TYPE(&h6899A2A3, &h405B, &h44D4, {&hA4, &h15, &hE0, &h8C, &hEE, &h2A, &h97, &hCB})
' // Our IExample VTable's GUID
' // {74666CAC-C2B1-4FA8-A049-97F3214802F0}
DIM IID_IExample AS GUID = TYPE(&h74666CAC, &hC2B1, &h4FA8, {&hA0, &h49, &h97, &hF3, &h21, &h48, &h02, &hF0})

TYPE IExample EXTENDS OBJECT
   ' Methods of the IUnknown Interface
   DECLARE ABSTRACT FUNCTION QueryInterface (BYVAL vTableGuid AS CONST IID CONST PTR, BYVAL ppv AS ANY PTR PTR) AS HRESULT
   DECLARE ABSTRACT FUNCTION AddRef () AS ULONG
   DECLARE ABSTRACT FUNCTION Release () AS ULONG
   ' Our methods
   DECLARE ABSTRACT FUNCTION SetString (BYVAL pwsz AS WSTRING PTR) AS HRESULT
   DECLARE ABSTRACT FUNCTION GetString (BYVAL pbuffer AS WSTRING PTR, BYVAL cch AS DWORD) AS HRESULT
END TYPE

' // Initialize the COM library
CoInitialize NULL

DIM wszLibName AS WSTRING * MAX_PATH = ExePath & "\IExample2.dll"  ' ---> change me
DIM pExample AS IExample PTR = AfxNewCom(wszLibName, CLSID_IExample, IID_IExample)
IF pExample THEN
   DIM hr AS HRESULT
   hr = pExample->SetString("José Roca")
   DIM wsz AS WSTRING * 80
   hr = pExample->GetString(@wsz, SIZEOF(wsz))
   PRINT wsz
END IF
AfxSafeRelease(pExample)

' // Uninitialize the COM library
CoUninitialize

' // Functions extracted from my WinFBX framework //

' ========================================================================================
' Decrements the reference count for an interface on an object.
' The function returns the new reference count. This value is intended to be used only
' for test purposes.
' When the reference count on an object reaches zero, Release must cause the interface
' pointer to free itself. When the released pointer is the only existing reference to an
' object (whether the object supports single or multiple interfaces), the implementation
' must free the object.
' ========================================================================================
FUNCTION AfxSafeRelease (BYREF pv AS ANY PTR) AS ULONG
   IF pv = NULL THEN RETURN 0
   FUNCTION = cast(IUnknown PTR, pv)->lpvtbl->Release(pv)
   pv = NULL
END FUNCTION
' ========================================================================================

' ========================================================================================
' Loads the specified library from file and creates an instance of an object.
' Parameters:
' - wszLibName = Full path where the library is located.
' - rclsid = The CLSID (class identifier) associated with the data and code that will be
'   used to create the object.
' - riid = A reference to the identifier of the interface to be used to communicate with the object.
' - wszLicKey = The license key.
' If it succeeds, returns a reference to the requested interface; otherwise, it returns null.
' Not every component is a suitable candidate for use under this overloaded AfxNewCom function.
'  - Only in-process servers (DLLs) are supported.
'  - Components that are system components or part of the operating system, such as XML,
'    Data Access, Internet Explorer, or DirectX, aren't supported
'  - Components that are part of an application, such Microsoft Office, aren't supported.
'  - Components intended for use as an add-in or a snap-in, such as an Office add-in or
'    a control in a Web browser, aren't supported.
'  - Components that manage a shared physical or virtual system resource aren't supported.
'  - Visual ActiveX controls aren't supported because they need to be initilized and
'    activated by the OLE container.
' Note: Do not use DyLibFree to unload the library once you have got a valid reference
' to an interface or your application will GPF. Before calling DyLibFree, all the
' interface references must be released. If you don't need to unload the library until
' the application ends, then you don't need to call DyLibFree because CoUninitialize
' closes the COM library on the current thread, unloads all DLLs loaded by the thread,
' frees any other resources that the thread maintains, and forces all RPC connections on
' the thread to close.
' ========================================================================================
FUNCTION AfxNewCom OVERLOAD (BYREF wszLibName AS CONST WSTRING, BYREF rclsid AS CONST CLSID, BYREF riid AS CONST IID, BYREF wszLicKey AS WSTRING = "") AS ANY PTR

   DIM hr AS LONG, hLib AS HMODULE, pDisp AS ANY PTR
   DIM pIClassFactory AS IClassFactory PTR, pIClassFactory2 AS IClassFactory2 PTR

   ' // See if the library is already loaded in the address space
   hLib = GetModuleHandleW(wszLibName)
   ' // If it is not loaded, load it
   IF hLib = NULL THEN hLib = LoadLibraryW(wszLibName)
   ' // If it fails, abort
   IF hLib = NULL THEN EXIT FUNCTION

   ' // Retrieve the address of the exported function DllGetClassObject
   DIM pfnDllGetClassObject AS FUNCTION (BYVAL rclsid AS CONST IID CONST PTR, BYVAL riid AS CONST IID CONST PTR, BYVAL ppv AS LPVOID PTR) AS HRESULT
   pfnDllGetClassObject = CAST(ANY PTR, GetProcAddress(hLib, "DllGetClassObject"))
   IF pfnDllGetClassObject = NULL THEN EXIT FUNCTION

   IF LEN(wszLicKey) = 0 THEN
      ' // Request a reference to the IClassFactory interface
      hr = pfnDllGetClassObject(@rclsid, @IID_IClassFactory, @pIClassFactory)
      IF hr <> S_OK THEN EXIT FUNCTION
      ' // Create an instance of the server or control
      hr = pIClassFactory->lpVtbl->CreateInstance(pIClassFactory, NULL, @riid, @pDisp)
      IF hr <> S_OK THEN
         pIClassFactory->lpVtbl->Release(pIClassFactory)
         EXIT FUNCTION
      END IF
   ELSE
      ' // Request a reference to the IClassFactory2 interface
      hr = pfnDllGetClassObject(@rclsid, @IID_IClassFactory, @pIClassFactory2)
      IF hr <> S_OK THEN EXIT FUNCTION
      ' // Create a licensed instance of the server or control
      hr = pIClassFactory2->lpVtbl->CreateInstanceLic(pIClassFactory2, NULL, NULL, @riid, @wszLicKey, @pDisp)
      IF hr <> S_OK THEN
         pIClassFactory2->lpVtbl->Release(pIClassFactory2)
         EXIT FUNCTION
      END IF
   END IF

   IF pIClassFactory THEN pIClassFactory->lpVtbl->Release(pIClassFactory)
   IF pIClassFactory2 THEN pIClassFactory2->lpVtbl->Release(pIClassFactory2)
   RETURN pDisp

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


PRINT
PRINT "Press any key..."
SLEEP
Last edited by Josep Roca on Jun 02, 2018 18:49, edited 2 times in total.
coderJeff
Site Admin
Posts: 3020
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Wiki improvements

Postby coderJeff » Jun 02, 2018 17:58

fxm wrote:In response to questions from D.J.Peters, I also proposed a draft of "Reference counting base of all derived-types in inheritance hierarchy".

I moved my draft out of the way and made some comments in that topic. Sorry, I don't know where to continue the discussion...
coderJeff
Site Admin
Posts: 3020
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Wiki improvements

Postby coderJeff » Jun 02, 2018 18:39

@Josep Roca, nice, thanks. That brings back memories. Let anyone say what they will about windows, COM solved a hard problem. Do I still need this object? No? How do I kill it? Unless of course there are circular dependencies...

In one of my programs I create many different types, and add them to a "collection" of sorts. That all works fine until it is time to destroy objects. To solve, added a TYPE member which was a function pointer to a "destroy" proc. Which at the time had me wishing for a COM like interface to query the object.

FYI, I just happened to name my files IExample.bas and example.bas. I was so close, your usage code expects IExample2.dll.
Josep Roca
Posts: 435
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: Wiki improvements

Postby Josep Roca » Jun 02, 2018 18:48

> FYI, I just happened to name my files IExample.bas and example.bas. I was so close, your usage code expects IExample2.dll.

Sorry. I forgot to point that you must change the path to the DLL if needed:

Code: Select all

DIM wszLibName AS WSTRING * MAX_PATH = ExePath & "\IExample2.dll"
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Wiki improvements

Postby fxm » Jun 03, 2018 8:02

I thought to write a short article on:
"How to right choose between composition, aggregation, and inheritance, for UDTs in FB".
Last edited by fxm on Jun 04, 2018 8:59, edited 1 time in total.
Reason: Article now written!
Josep Roca
Posts: 435
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: Wiki improvements

Postby Josep Roca » Jun 03, 2018 11:58

@Josep Roca, nice, thanks. That brings back memories. Let anyone say what they will about windows, COM solved a hard problem. Do I still need this object? No? How do I kill it? Unless of course there are circular dependencies...


Another advantage is that COM is a binary standard. Therefore, we can implement the server using FreeBasic classes and we can use it with another language without having to write intermediate wrappers. For example, to use it with PowerBasic, we can do:

Code: Select all

#INCLUDE ONCE "windows.inc"

$CLSID_IExample = GUID$("{6899A2A3-405B-44D4-A415-E08CEE2A97CB}")
$IID_IExample = GUID$("{74666CAC-C2B1-4FA8-A049-97F3214802F0}")

INTERFACE IExample $IID_IExample : INHERIT IUnknown
   METHOD SetString (BYREF pwsz AS WSTRINGZ) AS LONG
   METHOD GetString (BYREF pbuffer AS WSTRINGZ, BYVAL cch AS DWORD) AS LONG
END INTERFACE

FUNCTION PBMAIN

DIM LibName AS STRING
LibName = Exe.Path$ & "IExample2.dll"
DIM pExample AS IExample
pExample = NEWCOM CLSID $CLSID_IExample LIB LibName
IF ISOBJECT(pExample) THEN
   DIM hr AS LONG
   hr = pExample.SetString("José Roca")
   DIM wsz AS WSTRINGZ * 80
   hr = pExample.GetString(wsz, SIZEOF(wsz))
   PRINT wsz
END IF

PRINT
PRINT "Press any key..."
WAITKEY$

END FUNCTION
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Wiki improvements

Postby fxm » Jun 05, 2018 9:19

badidea wrote:Another tutorial topic (for those with less knowledge of OOP) is maybe why one want forbid certain operations. Also, why make certain member variables private. Why people make getters and setters. And when to use 'property'. For me, this is all a bit vague and often looks like a lot of code without function.

Why not another little article whose title could be:
"How and Why to Make Abstraction by Object Encapsulation, using the FB Syntax in UDTs"

What do you all think?
MrSwiss
Posts: 3221
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Wiki improvements

Postby MrSwiss » Jun 05, 2018 11:24

fxm wrote:How and Why to make Abstraction by Object Encapsulation, using the FB Syntax in UDTs

Well, if I understand correctly, I've just written some code, that could be used,
as a show-case (the reason was this thread: Memory deallocation strange issue).

It is called Type image where the only two Type internal variables are:
1) Any Ptr (holding the image itself)
2) String (holding (optional) a description, title or name of the image)
both of them declared "Private", then a ctor/dtor and some "handlers".

Aka: Sub's & Function's, which allow Object manipulation (from extern) but,
the "rights" thus granted, are *fully controlled* by the Type's creator. This then
means:
1) Any Ptr (no write access from outside but, it can be read, mostly for debug/
test use)
2) String (read/write access from outside)

Im sum: ImageCreate/ImageDestroy/ImageInfo are taken out of programmers
hands, because the Type handles that internally. (guaranteed: no memory leaks!)
All that is needed in Main code, is a temporary (image) ptr, with New/Delete.

Interrested?
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Wiki improvements

Postby fxm » Jun 05, 2018 14:41

Yes, why not.
I planned to start with a simple example of a Type with encapsulation (user interface through only member procedures), which could then be extended by inheritance for specific needs, to thus show that encapsulation not only allows to help with abstraction, but also to facilitate the coding of polymorphism.
But maybe this is a bit too ambitious.
MrSwiss
Posts: 3221
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Wiki improvements

Postby MrSwiss » Jun 05, 2018 15:08

... but also to facilitate the coding of polymorphism. But maybe this is a bit too ambitious.
I'm not sure, wheter the image (Type) is going to be suitable for extending,
in such a way. (I think, it is a bit to much focused "as-is".)

By cons, a simple example might be better suited, to point out "the basics".
This could later on be used, as a sort of "stepping stone", for more complex
implementations (of even something, totally different), aka: Chapter 2.

The title therefore, slightly modified:
How and Why to make Abstraction by Object Encapsulation, using the FB Syntax in UDTs (basics)

What do others too, think about it?
MrSwiss
Posts: 3221
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Wiki improvements

Postby MrSwiss » Jun 05, 2018 16:12

There is just another thing, that occured to me:
In all those articles (already made, in planning, or otherwise) whouldn't it be a
advantage, when at the top (of the article) someting along the following lines:
- Intended Audience (beginners/advanced/experts)
- should be read before/after "Article-Title" (to give a "recommended" sequence) or:
required skills (must be able to understand ... e.g. procedures with one or more
parameter's)
- abbreviations/shortcuts/acronyms used (a short glossary)
be given (by the author), before the actual start of the article?

Return to “Documentation”

Who is online

Users browsing this forum: No registered users and 28 guests