Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

adding custom classes to com server class

Status
Not open for further replies.

AlastairP

Technical User
Feb 8, 2011
286
AU
I have created a com server exe program using a single PRG file
All is working fine -

However I want to expand the existing class with additional custom class objects
But I get an error "Class not found" or similar

For example this is the com server (abbreviated) - I can add a base class timer
Code:
DEFINE CLASS serverStart AS custom OLEPUBLIC

	ADD OBJECT timer1 as timer WITH ;
		interval = 1000,;
		enabled=.t.

	PROCEDURE timer1.timer
		this.enabled=.f.
		this.parent.run_check()
	ENDPROC 


ENDDEFINE

If I create another PRG with a custom class:

Code:
DEFINE CLASS databasemanager AS custom

	PROCEDURE getTablename
	   RETURN 'Found It'
	ENDPROC 


ENDDEFINE

Then try and add the class to the main class, I get an error class not found

Code:
DEFINE CLASS serverStart AS custom OLEPUBLIC

	ADD OBJECT timer1 as timer WITH ;
		interval = 100,;
		enabled=.f.

        ***********************************************  
	ADD OBJECT databasemanager1 as databasemanager 
        ***********************************************        

	PROCEDURE timer1.timer
		this.enabled=.f.
		this.parent.run_check()
	ENDPROC 


ENDDEFINE








 
It works within the EXE, as in an EXE all class definitions compiled into it are in the same file (the EXE) and thus known and found. To let this work within the IDE you need to add a SET PROCEDURE.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Hi Olaf,

I added my PRG files as normal and called the procedures with SET PROCEDURE TO. All good thanks
Alastair
 
Just a side note:

This error tells you something about the inner workings: The class definition does not include the other class at design time already. The class is composed while it's instantiated at runtime.

The way this works in visual classes (stored within a VCX library) is by a (relative) path reference to the class of the added object, so a VCX does not need a SET PROCEDURE for visual classes, but you need to make known where to find a user-defined class of a subobject or instantiation of the class fails.

This also shows a pitfall you could have, if your project contains two PRG class definitions with same name (not the same PRG name, the same DEFINE [highlight #FCE94F]classname[/highlight] AS class), the search within the final EXE will put in the first of them it finds along current SET('PROCEDURE'), SET PATH and then the order of included files within the EXE.

This doesn't mean VCXes are better. There is the Session base class that's not available as a base class for VCXes, but you have the DataEnvironment class, which I think was added in VFP8. Before that, it only was available as an integral part of several base classes, which of course reminds me of some Microsoft jokes.

To get back to this pathing issue, the ADD OBJECT clause of a class definition lacks the feature you have in the core class definition line, to not just name the classname but classname OF filename. In the visual design, this is given most of the time by visually adding the subobject from a VCX into the class designer you want it, from your toolbar or simply from an expanded VCX class library node in the project manager, which makes clear from which file this class definition is.

In theory - I haven't tried -, when you have the same class in two different VCXes, on one side the path within the child class definition paints to exactly one of these libraries, on the other side once an EXE is compiled the meaning of paths vanishes. Though when you look inside an EXE with a hex editor you see paths of the original files are embedded, so the VFP runtime has a backreference to where the embedded files once were on your developer computer.


The main lesson to learn though: Classes are not inheriting at definition time. It would contradict the OOP principle, if they did, you couldn't modify the class you add into another one and updating any place it's used referenced as parent class or subobject of other classes or also forms. But the way VFP refers to the classes means you have ways of redifinitions in subclasses pointing to other parents - intentional or unintentional.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Hi Olaf,
Very informative - thank you.
This exercise of creating a com server has forced me to program non-visually, which is a good thing.

I previously learned of the importance of well organised class libraries, paying close attention to inheritance.
I have learned the hard way when copying vcx's into another project with references to the original locations . . .
So now I have a core 'base class' library that resides in the same location for all projects, and I base my projects classes from those.

But its still mostly visual design style even though I add objects at runtime with
SET CLASS LIBRARY TO myLib.vcx
This.addoject('somename', 'customclass') for example

Below is the proof of concept for using DEFINE CLASS in my com server


Code:
&& Testing define class
DEFINE CLASS server as Custom OLEPUBLIC

	oSession=NULL
	nCounter=0

	ADD OBJECT timer1 as timer WITH ;
		interval = 100,;
		enabled=.f.

	PROCEDURE INIT
		this.oSession = CREATEOBJECT("dataDE")
	ENDPROC 

	PROCEDURE StartTimer
		this.timer1.enabled=.t.
	ENDPROC 	

	PROCEDURE timer1.timer
		this.enabled=.f.
		this.parent.run_check()
	ENDPROC 

	PROCEDURE run_check
		this.nCounter=this.nCounter+1
		this.oSession.UpdateTable(this.nCounter)
		
		IF this.nCounter > 100
		      this.timer1.enabled=.f.
		ELSE
		      this.StartTimer()
		ENDIF 
		
	ENDPROC 

	PROCEDURE getCount
		ln=this.oSession.getmaxcnt()	
		RETURN ln	
	ENDPROC 


ENDDEFINE 


DEFINE CLASS dataDE as session 
	PROCEDURE init()
        	CREATE CURSOR cTest(lDate T, nCounter I)
	ENDPROC 

	PROCEDURE UpdateTable
		PARAMETERS lnCnt

		SELECT cTest
		APPEND BLANK
		replace lDate WITH DATETIME()
		replace nCounter WITH lnCnt
	ENDPROC

	PROCEDURE getmaxcnt
		SELECT cTest
		GO BOTTOM 
		RETURN nCounter 
	ENDPROC 

ENDDEFINE

Thanks
Alastair

 
Oh, you can also define a visual custom class as olepublic.

classinfo_ucqnid.jpg

olepublic_dxpzxi.jpg


The examples of OLEPUBLIC servers in the VFP help rather use the session base class, but then the data environment would also be a good basis and regarding datasessions, a COM server (no matter if EXE or DLL) will have it's own new default datasession, just like you also already have one in main.prg of a normal EXE.

So having an oSession subobject is overkill, your class code and all methods of it will run in the default data session of the new COM runtime and your oSession object will be it's own. That's not just overkill, it feels like it'll bite you when you can't access workareas within oSession from within methods of the com server.

I once thought as a DLL means in-process servers it'll cause the COM server instance to be in the current datasession, but it's not. So also a COM server class will always run in its own data session. If you think about it, it has to, because COM servers can be used from any programming language (which is capable of OLE) and when that's not VFP it doesn't have the datasession concept at all, so that's a thing the VFP runtime always ensures is existing, it's a prerequisite for the code language about workareas, which is a good portion of VFP.

Now, try an olepublic form class. Why doen't it show? Because the separate runtime starting for the COM server also means a separate _SCREEN.

When you compile as MTDLL you have to use the limited vfp9t.dll, but all other COM servers also use the normal vfp9r.dll and don't have these restrictions. You can't do an OCX with the VFP compiler as the VFP form surely inherits from the TForm base class of Winforms, but you don't have some other means to really make an OCX that would work.

What you could do, though is write an OLE automatable VFP application once you'd define an OLE class that sets _SCREEN.visible = .T., I gave that advice once here:

The only problem I had was because the framework I used was reacting to _VFP.Startmode=2 and has foreseen a whole different behaviour for running inside a COM Server. But without such a framework everything works in an EXE Com Server, you just don't get _SCREEN.visible = .T. automatically and onyl the COM class is created, there is no automatic run of a the main.prg of the EXE. You'll also want to set _vfp.OLERequestPendingTimeout and other _VFP.OLE.. properties of your ole autmatable application. And maybe go with the norma Microsoft uses for the automation server names and name your olepublic application base class Application.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top