Notifications
Clear all

[Closed] dotNet timer – struct issues

Hi guys,
I’m putting together a quite simple timer struct with dotNet, to avoid the interface timer. I run into some problems though. Following code shows the timer struct instanced and called from within another struct. It fires the error:

MAXScript dotNet event handler Exception
– Runtime error: Struct member access requires instance: theTimer

I’m a completely newbie when it comes to dotNet. What does the error mean? What I’m trying to do, is to call a function once, after a time interval.

Thank you very much.

struct DotNetTimer
(
    theTimerObject = (dotNetObject "System.Windows.Forms.Timer"), 

    function setOnTick onTick =
    (
        if (isKindOf onTick MAXScriptFunction) do
        (
            dotNet.removeAllEventHandlers theTimerObject
            dotNet.addEventHandler theTimerObject "tick" onTick
        )
    ),

    function setInterval iInterval =
    (
        theTimerObject.interval = iInterval
    ),

    function start =
    (
        theTimerObject.start()
    ),

    function stop =
    (
        theTimerObject.stop()
    ),
    
    function restart =
    (
        theTimerObject.stop()
        theTimerObject.start()
    )
)


struct DummyStruct
(
    theTimer = DotNetTimer(),
    
    function getTime =
    (
        print localTime
        theTimer.stop()
    ),
    
    function init =
    (
        theTimer.setInterval 5000
        theTimer.setOnTick getTime
    ),
    
    function runIt =
    (
        theTimer.start()
    )
)


d = DummyStruct()
d.init()

--WARNING uncomment this to give the code a try, but it hangs 3ds Max 
--d.runIt()
  • Enrico
6 Replies

Enrico,

  There are two types of struct use in MAXScript.
  If a struct contains ONLY functions, you can call these functions directly from the struct definition.
  
  For example,

      struct someStruct
      (
      fn funct1 = (10*20),
      fn funct2 =  (30*someStruct.funct1())
      )
      
      --you may say:
      
      someStruct.funct2()
     
     
     #Struct:someStruct(
       funct2:<fn>; Public,
       funct1:<fn>; Public)
     6000
     
  [b]NOTE [/b]that you MUST provide the struct name as prefix of the funct1() call so it knows where to look. Otherwise, you will get the error

 -- Runtime error: Struct member access requires instance: funct1
  But if your struct contains both properties and functions, you MUST first create an instance of the struct before accessing its properties (you can still call functions from the def or an instance)

      struct someStructDef
       (
      v1 = 42,
       fn funct1 = (v1),
      fn funct2 =  (funct1()*2)
       )
      
      theStruct = someStructDef()
      theStruct.v1
      theStruct.funct2()
     
     #Struct:someStructDef(
       funct2:<fn>; Public,
       v1:<data>; Public,
       funct1:<fn>; Public)
     (someStructDef v1:42)
     42
     84
     
  [b]NOTE [/b]that once you create an instance of the struct, you can access its members (properties and functions) from inside struct functions without prefixing the function name with the name of the struct.

Thanks Bobo for the very quick reply.

There’s still something I don’t get. I’m aware of the struct instancing, but I thought I made an instance of struct DotNetTimer, when created an instance of DummyStruct. DummyStruct is instanced as d = DummyStruct(). Don’t apply object composition rules? (C++)

What am I missing? Isn’t first definition in struct DummyStruct: theTimer = DotNetTimer() an instance of struct DotNetTimer?

How should I call DotNetTimer methods from within DummyStruct methods? d.init() works well, but when the timer ticks the call to DummyStruct.getTime() fails. I tried to fully specify the path: theTimer.setOnTick DummyStruct.getTime, but in that case it was simply ignored.

From another point of view, does the issue arise from DotNetTimer not knowing what’s the instance of DummyStruct, consequently not being able to call getTime method? How would I fix this?

Thank you very much

  • Enrico

I will check this out when I get to the office… Stay tuned.

I’m sorry Bobo, it still doesn’t work. I tried to create the two instances, then assigned the DotNetTimer instance to a property of the instance of DummyStruct, but it keeps giving the same error. I’m lost.

struct DotNetTimer
(
    theTimerObject = (dotNetObject "System.Windows.Forms.Timer"), 

    function setOnTick onTick =
    (
        if (isKindOf onTick MAXScriptFunction) do
        (
            dotNet.removeAllEventHandlers theTimerObject
            dotNet.addEventHandler theTimerObject "tick" onTick
        )
    ),

    function setInterval iInterval =
    (
        theTimerObject.interval = iInterval
    ),

    function start =
    (
        theTimerObject.start()
    ),

    function stop =
    (
        theTimerObject.stop()
    ),
    
    function restart =
    (
        theTimerObject.stop()
        theTimerObject.start()
    )
)

struct DummyStruct
(
    theTimer = undefined,
    
    function getTime =
    (
        print localTime
        theTimer.stop()
    ),
    
    function init =
    (
        theTimer.setInterval 5000
        theTimer.setOnTick getTime
    ),
    
    function runIt =
    (
        theTimer.restart()
    )
)

theTimerInstance = DotNetTimer()

theDummy = DummyStruct()

theDummy.theTimer = theTimerInstance
theDummy.init()

--WARNING uncomment this to give the code a try, but it hangs 3ds Max 
--theDummy.runIt()
  • Enrico

EDIT: Sorry I didn’t see your edit. Thank you very much for your effort.

I think I got it to work now.

Basically, you have a scope visibility issue:

theTimer.setOnTick getTime

passes the function getTime hosted by the instance of DummyStruct as the event handler of the timer. So when the timer starts ticking, it tries to call that function.
The function itself looks like

function getTime =
(
    print localTime
    theTimer.stop()
),

where theTimer is in the local scope of DummyStruct’s instance, but the handler lives in the OTHER struct and cannot see that, so right after printing the time, it complains it cannot access theTimer.

I declared the variable d as global before the two structs definitions and inside the getTime function, I had to prefix theTimer with d. because it is sent to work in a different scope when passed to the event handler.

This is of course not a very clean solution, but at least it does not fail.

Thank you Bobo, now I see the scope issue. I cannot really understand why it doesn’t work by fully specifying the path to DummyStruct.theTimer.stop(), as “DummyStruct” should be replaced by the struct instance, and so be accessible from outer scope.
Anyway, I found an alternative way to solve path issues avoiding global variables, even if it isn’t so clean either. Thanks again for your effort and tackling my problem.

(

struct DotNetTimer
(
    theTimerObject = (dotNetObject "System.Windows.Forms.Timer"), 

    function setInterval iInterval =
    (
        theTimerObject.interval = iInterval
    ),

    function start =
    (
        theTimerObject.start()
    ),

    function stop =
    (
        theTimerObject.stop()
    ),
    
    function restart =
    (
        theTimerObject.stop()
        theTimerObject.start()
    ),

    function setOnTick onTick =
    (
        if (isKindOf onTick MAXScriptFunction) do
        (
            format "inFunction: %
" onTick
            format "here!
"
        
            dotNet.removeAllEventHandlers theTimerObject
            dotNet.addEventHandler theTimerObject "tick" onTick
            
            format "here too!
"
        )
    )
)

struct DummyStruct
(
    theDummyTimer = undefined,
    
    function getTime =
    (
        print localTime
        ::theTimer.stop()
    ),
    
    function init &inTimer =
    (
        theDummyTimer = inTimer
        theDummyTimer.setInterval 5000
        theDummyTimer.setOnTick getTime
    ),
    
    function runIt =
    (
        theDummyTimer.restart()
    )
)

theTimer = DotNetTimer()
theDummy = DummyStruct()

theDummy.init &theTimer
theDummy.runIt()

)
  • Enrico