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 IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Setting Up A Com Server

Status
Not open for further replies.

BrendanChandler

Programmer
Sep 12, 2000
28
GB
I have a VFP 6 application containing several databases of names and numbers to describe it in simplistic terms. A want to link this system to a new telephone system that is being installed so that as well as users clicking 'Find' in my app to locate records using name, telephone no etc, when the phone rings I want the 3rd party phone system to send my app a message to tell me the number which is ringing so that I can return the name of the person in the database to whom the phone number belongs (there could be several in some instances). The phone software should present a list of possibles and if the user selects one it will pass details back to my app so that I can locate the selected record and display the appropriate input screen.

The telephone people suggest COM is the best way but as usual cannot really help with VFP and I have done no VFP COM server stuff before now other than some automation stuff to control Word and Outlook.

I have worked out so far that I can put the following code at the end of my top level PRG file in my app

DEFINE class phonestuff AS CUSTOM OLEPUBLIC
PROCEDURE phonesearch
Parameters ptelephone
do ismess with "Some one just called on "+ptelephone
ENDPROC
ENDDEFINE

I compile the app as MYAPP.EXE, ran it and accessed the server from another VFP session using

oTest = CREATEOBJECT("myapp.phonestuff")
cName = oTest.phonesearch("1234")

It worked! I thought this is easy then the problems started - as I started to expand the code to interrogate the database in the app, load the appropriate forms etc I found that tables that are open throughout the app where not accessible to the com object, neither were forms or other classes that are present throughout the app.

Question - what do I need to do in order to make everything visible to the COM object that has already been loaded in MYAPP.EXE? Are there any more pitfalls I should be aware of as I take this development further?

Any help really appreciated. Thanks
 
Mike,

Thanks for that. For anybody watching I have replaced my CONTACT method with the following now

parameters pref
locn = createobject("ADODB.Connection")
lors = createobject("ADODB.RecordSet")
locn.connectionstring = "DRIVER=Microsoft Visual FoxPro Driver;SourceType=DBF;SourceDB="+sys(5)+sys(2003)+"\bookit;Exclusive=NO;BackGroundFetch=NO;NULL=NO;Collate=MACHINE"
locn.open()
lors.activeconnection = locn
lors.open("select 'M' as ctype, salutation, firstname, lastname, compname from members where ref = '"+pref+"'")
return lors

Fingers crossed the telephone company can work with recordsets. Thanks again for everyone's help on this one.
 
Hi,

stop, stop, stop!

Brendon, your COM-server is fine.
The first instanciation with

Code:
otelephone = createobject("telephone.telephone")
otimer = createobject("bookittimer")

As a test proram manipuating the gotorec property use this:
Code:
otelephone = getobject(,"telephone.telephone")
cname = otelephone.contact("001001")
set procedure to progs\utillib
if isconfirm("Go to "+allt(cname)+"?")
    otelephone.gotorec = "001001"
    wait window otelephone.gotorec
endif

As I said: GETOBJECT to get the running instance. It's like with Word or Excel, you get an object reference to the running server...

Bye, Olaf.
 
The thing I said about single instance is nonsense - and even not single threaded comserver DLL.

Make it an EXE and let it stay by the default "multiple use"/"multiple instance" in the project info server tab.

Use this as a testserver

a) create a project tele.pjx
b) create a prg phone.prg:
Code:
DEFINE CLASS phone as Session olepublic
   datasession = 1
   gotorec=0
ENDDEFINE
c) if it isn't the main file of the project make it so.
d) compile as tele.EXE

Now run this simple test:
Code:
oTP = createobject("tele.phone")
oTP2 = GETOBJECT(,"tele.phone")
oTP.gotorec = 1
?oTP2.gotorec
&& prints 1

Bye, Olaf.
 
Olaf,

Just when I thought I had understood I am confused again! I kind of had something running but was assuming that one app could not set a property value in the COM server that another app can then see. Are you saying it can? Are you saying that if the telephoone people instantiate the COM object with their app, then when they set a property in the com server to indicate the selected record following a call to a method that has returned them a list of possible records, the timer in my main app will be able to inspect that property every few seconds and when it finds a non blank value there it can react to that then clear it? Or have I totally misunderstood?

My next problem now occurs having returned the ADO recordset from the COM to the telephone peoples app. They are now telling me they cannot read that structure from their app. I have copied their message below. Can anybody make any sense of it and suggest a solution

"Initial testing has shown that we are unable to traverse the recordset that is returned. This is because, the return type of the contact method is registered as an OLEVariant / Object. Casting this return type to a ADO Recordset / ADO Dataset is an illegal operation in both C# and Delphi.

Is it possibly for you to specify the return type in the COM Server? This will allow COM Clients to 'see' the actual type and map to a local variable of the same type.

If not, you could do the following:

Return the records as a IStrings type. This simply means a string list structure. COM will automatically convert the string list to an IStrings type."

This makes little sense to me having no knowledge of C# and Delphi and they obviously don't talk FoxPro very well!

Any further help appreciated. Thanks
 
Hi Brendan,

yes, I'm saying you can share one COM object with two apps. It's important though, that you decide which application is using CREATEOBJECT() to create the instance and which application is using GETOBJECT() to get a reference to the existing COM server object.

The first app running should do the CREATEOBJECT().
You may also do CREATEOBJECT(...) at startup of the computer and then both your and the telephone app yould use GETOBJECT().

...simply follow my instructions and you see...

Then there maybe no need for ADO anymore, as you may share the informations through properties (or Arrays of properteis) of that COM server.

Bye, Olaf.
 
Hi again Brendon,

Brendon said:
Fingers crossed the telephone company can work with recordsets. Thanks again for everyone's help on this one.

Well, they may have to learn quite a bit for .NET COM-Interop. Importing COM-Servers as Assemblies etc. But it shouldn't be a big problem to work with an ADODB.Recordset in C#.

If you do RETURN loRS in your CONTACT() method they get a recordsource _object_. That's of course no simple data type, it's an object, which has Methods - eg RS.MoveFirst(), RS.Movenext() - and Properties - eg RS.Fields("cType"), RS.Fields("salutation"), RS.Fields("firstname"), RS.Fields("lastname")...

Bye, Olaf.
 
Olaf,

I have created and returned an object now. It has a numeric property indicating number of records returned and an array property containing the member details as they were struggling to use record sets (sorry Craig, think XML may be another learning curve here so I'll not go down that route just now!).

I am still struggling with your getobject though to share properties between my app and their app once they decide which record they want to go to.

I have modified my test program as follows

lcrecord = "001001"
otelephone = createobject("telephone.telephone")
orecords = otelephone.contact(lccontact)

if orecords.irecords = 0 && no of records returned
? "Nothing found"
else
if isconfirm("Go to this record?")
otelephone.gotorec(orecords.amatches[1,1]) && array of returned record values
else
otelephone.gotorec(" ")
endif
endif

otel2 = getobject(,"telephone.telephone")

return

Ultimately the phone people will choose to go to any one of the records returned in the array orecords.amatches, however, for simplicity I am simply going to the first record. I get an error message on the otel2 line that says 'OLE error code 0x800401e3: Operation unavailable'. I am compiling using VFP6, I have built the com as both a single and multiple thread DLL (what is the difference?). My class is built visually in the class designer as OLEPUBLIC not programmatically as per your example. I see you have set datasession = 1 which is the default anyway I think. You mention an EXE at some point - I have been steered away from that route do I need to go back there now to use getobject? If I cannot get getobject to work in the same app as the object was first created I am not likely to get it to work in the other app. Am I doing something wrong here? Any ideas. Any more help really appreciated. Thanks
 
Brendan,

There is no learning curve to use XML to pass the data. You don't need to know anything about XML to do it. Just call CURSORTOXML() to convert to XML and save the data into a character variable. Pass that variable like any other to the other tier, then call XMLTOCURSOR() to convert back.

Craig Berntson
MCSD, Visual FoxPro MVP, Author, CrysDev: A Developer's Guide to Integrating Crystal Reports"
 
Craig,

Thanks for that. Yes I can see the value of that now. Problem is it will not be me who is converting back it will be the 3rd party who are using C++ and Delphi not VFP so it may not be as straightforward for them. I have just sent them a test server which passes the table contents as an array property. Hopefully they can easily use that but if not I will mention the XML approach and see if they can use it.

In the meantime still struggling to share the same com server property between the two apps. I can createobject OK in the first app but then my getobject is failing with

'OLE error code 0x800401e3: Operation unavailable'

Just done a web search on this error and messages from other development systems keep mentioning the ROT (running object table). What is this and is there something Olaf has ommitted to tell me that I need to do with this? Any further help appreciated. Thanks
 
Brandon,

please read my earlier advice: getobject ONLY works for EXE comserver, so you MUST compile the server as EXE, otherwise you get the OLE Error described...

Bye, Olaf.
 
Olaf,

I did try the EXE approach with my code and got a similar error. I have now gone back and created the EXE with the code exactly as per your example i.e. I have created a project tele.pjx, I have added a single prg file phone.prg. The code in there is

DEFINE CLASS phone as Session olepublic
datasession = 1
gotorec=0
ENDDEFINE

When I tried to change the project info tab I saw no class and could not change to multiuse, however, as soon as I compiled to an EXE these then became available so I selected the phone class and set to multi-use and recompiled the EXE and ran it from the command prompt.

I then opened a second copy of VFP 6, created a prg with the following code

oTP = createobject("tele.phone")
oTP2 = GETOBJECT(,"tele.phone")
oTP.gotorec = 1
?oTP2.gotorec

When I run this I get the error

OLE error code 0x800401e3: Operation unavailable.

It feels like I am so close to what I need but I am still stumped. Can you help? Thanks
 
The phone people have come back and suggested that the error above is common when the com server is not registered in the ROT. How do I do this? They have pointed me at a site for doing this in Delphi using some messy Windows API calls - how is it done in VFP? Is this where you run the EXE with the paramter /regserver or is that something else? I have done that and still get the error. Thanks.
 

I would be very surprised that registering the COM would resolve this issue. In my opinion you cannot call a VFP COM using GETOBJECT(). I followed your example and compiled on my machine (and compiling a OLE Public class automaticaly registers it on the development box) and I get the same error the GETOBJECT() line. But you can try using the following in the Start->Run area of windows:
Code:
regsvr32 "c:\myproject\myCom.exe"



Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
ReFox XI (www.mcrgsoftware.com)
 

There is an interesting chapter on VFP and COM in the MegaFox book (chapter 14) that describes in great lenght the COM and their use. From what I read VFP uses what is called thread-safe COM technology, which means that "each instance of a COM work in it's own thread and it's process". But what you are attempting is just the opposite.
I would suggest to create an insance of a COM, and if you only have a value to pick up from the second instance, just create a globalAtom, or even a Textfile containing the value.


Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
ReFox XI (www.mcrgsoftware.com)
 

One last thing that you might investigate is the use of Winsock, which allows to applications to exchange data, either locally or even over TCP/IP.
Here is an article (In VB) about it's use
Using the Winsock Control in Client/Server Applications



Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
ReFox XI (www.mcrgsoftware.com)
 
Don't know what your prolem is,

perhaps the dll you first did is still registered with the same name...

Here's a download of such a public comserver and the whole project (in vfp7 - so vfp7 runtimes needed or simply recompile as public.exe with vfp6/7/8/9): Simply register public.exe with the /regserver option and then try this:

Code:
oCom1 = createobject("public.comserver7")
?oCom1.nValue
oCom2 = getobject(,"public.comserver7")
oCom2.nValue = 1
?oCom1.nValue

I get "1" displayed for oCom1.nValue, although oCom1.nValue never was set. oCom2.nValue is the same as oCom2.nValue, because it's the same object reference! That's the way getobject() works.

Bye, Olaf.
 
Thanks for all the help here guys. I think at this point, though, I will stick with a text file to exchange the single 6 character field between the two apps (i.e. the member reference to be selected following the match on phone numbers). As getobject wouldn't work out of the box in VFP6 (BTW your tele.exe was new so wasn't already registered Olaf) and Winsock and TCP/IP is a whole new area for me it really is a case of avoiding a sledgehammer to crack a nut at this final part of the development.

I have everything I need to make things work now if I wrap a text file low level read and write on the end so thanks for everything. I expect to upgrade to VFP 9 very shortly and so may revisit getobject then to see if anything has changed.
 
Hi Brandan,

the project has soo little code in it and since COM is rather a windows than a Foxpro thing my project should work for you recompiled with vfp6.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top