Can anyone able to use the "only one instance allowed of the procedure" property in Window Behaviour definition?

When I set the definition, global variables are created in the code, but I can open the same window over and over again.

I use Mike Hansons Super stuff for this kind of things

ABC free already has this type of controls. But I’m asking for anybody using Clarion default instance limit.

Yes, it is working in an example app I have in C10. I applied the option from Window Behavior button in a browse procedure STARTed from the main frame. The procedure now only starts once.

Where and how are you using the option?

Note: When I removed that option I received an error that the global variable no longer existed, so I deleted all my source code and recompiled, which fixed it. Maybe you can try that?

Edit: PS: It doesn’t work on the Frame procedure. I thought there was another template/option to limit an APP to a single instance, but I couldn’t find it?

2 Likes

:+1:t3: Yes, you’re right, thank you. It doesn’t work because I use AppFrame. I saw it working in a sample application without AppFrame.

Maybe you’re thinking of BeginUnique(AppName) and EndUnique() located in CwUtil.inc and clw ?
(Use Windows API CreateEventA )

Regards

2 Likes

Thank you very much for your kind attention @Olivier. BeginUnique and EndUnique functions allow the application to be singletonized. However, the “only one instance allowed of the procedure” in Window prevents the same screen from opening twice in the application. @PurpleEdge2214 said that the reason why it is not working is related to AppFrame and he is right. Unfortunately, this feature does not work when AppFrame is running.

If my memory serves well, I remember having used BeginUnique with Procedures (I used the Procedure Name), the string parameter of BeginUnique(String) is just used as a memory flag to get an event handle.

If BeginUnique() doesn’t work for you, you may try the cwutil WindowsExists(String) function ?

I do not know if you read the cwutil source for BeginUnique EndUnique and WindowExists
but here it is:

!-----------------------------------------------------------------------------!
!BeginUnique
! Set APP to run in Single Process
!-----------------------------------------------------------------------------!
BeginUnique        PROCEDURE( STRING sAppName )

szEventName        CSTRING(FILE:MaxFilePath),AUTO
hEvent             LONG,AUTO

  CODE
  szEventName = CLIP( sAppName ) & '_UEvent'

  hEvent = CreateEvent (, 0, 0, szEventName )
  IF hEvent = 0 OR GetLastError() = 183             !Already Exists
    RETURN 0
  ELSE
    RETURN hEvent
  END

!-----------------------------------------------------------------------------!
!EndUnique
! Un Set APP to run in Single Process
!-----------------------------------------------------------------------------!
EndUnique          PROCEDURE( LONG hUnique )

  CODE
  CloseHandle ( hUnique )
  RETURN


!-----------------------------------------------------------------------------!
!WindowExists
! Return wether a window exists or not (TRUE = Exists, FALSE = Does NOT)
!-----------------------------------------------------------------------------!

WindowExists       PROCEDURE (STRING sWindow)

hWnd               UNSIGNED,AUTO
szWindowName       CSTRING (128),AUTO

  CODE
  szWindowName = CLIP (sWindow)
  hWnd = FindWindow (,szWindowName)
  RETURN CHOOSE (hWnd <> 0)

There was the below template from Jim DeFabia of TopSpeed published in the Clarion for Window Journal. I actually use this …

#EXTENSION(SingleThread,'Limits child of MDI parent to one Start'),PROCEDURE
#BUTTON('Limit to Single Thread'),AT(10,,180)
  #BOXED('Limit to Single Thread')
  #DISPLAY('')
  #DISPLAY('This extension limits a child of an MDI parent to one Start')
  #DISPLAY('  - add it to each Procedure to be limited to one Start only')
  #DISPLAY('')
  #DISPLAY('Adapted from article by J.DeFabia in CWJ Vol1/No4 - Pg.25')
  #DISPLAY('')
  #ENDBOXED
#ENDBUTTON
#!============================================================
#AT(%GlobalData)
g:ThreadNumOf:%Procedure   BYTE #<! For SingleThread extension
#ENDAT
#LOCALDATA
p:ThisIsTHEVersion         BYTE   ! For SingleThread extension
#ENDLOCALDATA
#AT(%ProcedureInitialize)
! Initialisation for SingleThread extension
IF GlobalRequest <> SelectRecord
  IF g:ThreadNumOf:%Procedure = 0       #<! IF procedure not already running
    g:ThreadNumOf:%Procedure = THREAD() #<!  save thread number of procedure
    p:ThisIsTHEVersion = True           #<!  & flag this as THE version
  ELSE                                  #<! ELSE already running. So, give
    POST(EVENT:GainFocus,,g:ThreadNumOf:%Procedure)  #<! that version focus
    p:ThisIsTHEVersion = False          #<!  and quit this one
    DO ProcedureReturn                  #<!  (this is not THE version)
. .
#ENDAT
#AT(%WindowEventHandling,'GainFocus')
%Window{PROP:Active} = True             #<! Give the Window focus.
IF %Window{PROP:Iconize} = True         #<! IF procedure is Iconized
  %Window{PROP:Iconize} = ''            #<!  then Restore it.
.
#ENDAT
#AT(%EndofProcedure)
IF p:ThisIsTHEVersion = True            #<! IF ending THE version
  g:ThreadNumOf:%procedure = 0          #<!  then clear the Thread holder.
.
#ENDAT

I keep thinking I should change this Global Data code:

#AT(%GlobalData)
g:ThreadNumOf:%Procedure   BYTE #<! For SingleThread extension
#ENDAT

To declare it as Local data that is STATIC:

#AT(%DataSection)    #! was GlobalData
g:ThreadNumOf:%Procedure   BYTE,STATIC #<! For SingleThread extension
#! also move this here--> p:ThisIsTHEVersion  BYTE 
#ENDAT

Otherwise every time you add this template the new Global Data causes a complete recompile. And I prefer to not have Globals if that scope is not really required.

What would be lost is the ability to check IF g:ThreadNumOfProcName ... to see if it is running before you START a new one. Typically I have multiple DLLs so that would not work unless those Globals were Exported,

1 Like

Thank you for your answer Olivier. The WindowExists function uses the FindWindowA function from the Windows library. Looking at the Microsoft documentation, this function looks for the name of the top-level window, it cannot be used for child windows. They recommend the FindWindowExA function for this, but it is not available in Clarion.

FindWindowA function (winuser.h) - Win32 apps | Microsoft Learn

Really, you just need the prototype for the API. That function is already in the WIN32.LIB that comes with Clarion.

Something like:

FindWindowEx(LONG, LONG, *CSTRING, *CSTRING),LONG,RAW,PASCAL,NAME('FindWindowExA')

Hi Jeff,

In one of my projects I use a thread manager class that provides support for restricting a procedure to one instance. For some reason I think this class originally came from you, like 25 years ago. There aren’t any copyright notice comments in the source.
Do you have a class like you distributed like that?

There was one that was in a Clarionmag article, I think. It was JSTM something. Not sure.

1 Like

@Rick_UpperPark - When the new threading model appeared, there were some things that needed to be made thread safe but I never looked into it. It’s been that many years since I even thought about it :slight_smile:

My version is very heavily modified (of course). I thought it might still be something you had.