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

VFP COM servers and Winsock

Status
Not open for further replies.
May 24, 2012
7
VC
Gentle people, it is my first post here. It is also my first time trying to build a VFP COM server. Everything seem to be going on fine until it came across an instruction to instantiate a 'winsock' object. I use the winsock object to get info on IP Address and to do some Remote Control of Network PCs. I have the Winsock.ocx on a Container class. When I remove the Winsock control from the container the program runs fine.

Please advise.

 
If you get the "Class not licensed for Use" error, see here: [link fox.wikis.com/wc.dll?Wiki~ClassNotLicensedForUse][/url]
It's a known feature of the winsock control to only work on a form, not in a container class. So create a class with the winsock control put on a form.

Alternatively use different winsock controls or [URL unfurl="true"]http://www.vfpwinsock.com/[/url], which makes use of the WS2_32.DLL instead of the ocx.

Bye, Olaf.
 
Dan,Olaf

Thanks for your quick response. The error is "Object class is invalid for this container." That is strange though since it works well when I run the Application's EXE.

I will put it on a Form Class and will post the results later today. Thanks very much.



 
Olaf,

I tried your suggestion of using a 'Form' container instead and got "OLE error code 0x80004005: Unspecified error." Using a different Winsock control now is a bit tasking as I have codes in the Winsock.ocx already working fine.


Thanks, I will keep trying.

 
If you get an ole error code at least the ocx is up and running. I found different results on that ole error googling. Most of them point to a timeout issue, so it may work in the exe version in your trials, just because you had more luck then, better timings.

Bye, Olaf.
 
Hey Olaf,

The Ole Error is coming from Winsock you say. Hmmm. What is the timeout? I see no such property anywhere on the control.

Thanks for your efforts.
 
Gentlepeople

One more thing. I tried Olaf's idea of using the VFPsock. I created a custom class and created methods (for procedures) and properties according to the code the page that was referenced. The Com Server load well with no errors. However, when I tried to read the Ipaddres and Hostname from it I got empty strings.

 
Why should a timeout be a property?

First of all: An OLE error always comes from an OLE (aka ActiveX) control, so it comes from winsock.ocx. A timeout error happens after a timeout, eg if the server or pc you connect to does not respond, something like that.

If I were you, I'd not bother about finding out where to set or higher it, you certainly expect rather instant connections and data transfer via winsock, so check your programming in the debugger and see what happens, if you execute step by step.

Also: Do you have any error handler set via ON ERROR? See the help topic on that, you can make use of AERROR() to determine more info on the ole error, in case there is more info, see there. You can determine the line of the error via LINENO().

Bye, Olaf.
 
>However, when I tried to read the Ipaddres and Hostname from it I got empty strings.

Maybe you expect behaviour the VFPwindsock isn't programmed for or the WS2_32.DLL differs from the ocx in that respect.

What IPadress and hostname do you expect there? What also would help an awful lot is code you're executing, to reproduce and debug the problem. I'm aiming in the dark, here.

Bye, Olaf.
 

Olaf,

I am leaning towards the use of the following VFP socket that you referred to. I have on a custom object of the following codes. I was hoping that after it is initialized that I could READ the current PC IP address, HostName and Port. I am only able to READ the Port.

When my clients login to my app. They store those info. in a table on the server. With that info. The app can do things like shutdown PC or Application remotely. The Port would be a listening port that each app run creates.

The winsock.ocx was providing me with that facility.

Code:
**************************************************
*-- Class:        foxsock (c:\bit_apps\develop\classes\winsock.vcx)
*-- ParentClass:  custom
*-- BaseClass:    custom
*-- Time Stamp:   05/30/12 07:34:10 AM
*
#INCLUDE "c:\bit_apps\develop\includes\bitincl.h"
*
DEFINE CLASS foxsock AS custom


	Height = 20
	Width = 32
	*-- State of Connection
	state = 0
	bytesreceived = 0
	host = ""
	ip = .F.
	port = .F.
	hsocket = .F.
	cin = .F.
	waitforread = .F.

	*-- Winsock Property Name counterpart
	localhostname = .F.

	*-- Winsock Property Name counterpart
	localip = .F.

	*-- Winsock Property Name counterpart
	localport = .F.


	PROCEDURE connect
		LPARAMETERS tcServer, tnServerPort
		    LOCAL cBuffer, cPort, cHost, lResult

		    THIS.IP = THIS.GetIP(tcServer)
		    IF EMPTY(THIS.IP)
		      RETURN .F.
		    ENDIF
		    THIS.Host = tcServer
		    THIS.Port = tnServerPort

		    THIS.hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
		    IF THIS.hSocket = SOCKET_ERROR
		        RETURN .F.
		    ENDIF

		    THIS.State = 6
		    cPort = THIS.num2word(htons(THIS.Port))
		    nHost = inet_addr(THIS.IP)
		    cHost = THIS.num2dword(nHost)
		    cBuffer = THIS.num2word(AF_INET) + cPort + cHost + Repli(Chr(0),8)
		    lResult = (ws_connect(THIS.hSocket, @cBuffer, Len(cBuffer))=0)
		    IF lResult
		      THIS.State = 7
		    ELSE
		      THIS.State = 0
		    ENDIF
		  RETURN lResult
	ENDPROC


	PROCEDURE close
		    if THIS.hSocket<>SOCKET_ERROR
		      = closesocket(THIS.hSocket)
		    endif
		    THIS.hSocket = SOCKET_ERROR
		    THIS.State = 0
	ENDPROC


	PROCEDURE senddata
		LPARAMETERS cData

		LOCAL cBuffer, nResult
		cBuffer = cData
		nResult = send(THIS.hSocket, @cBuffer, Len(cBuffer), 0)
		IF nResult = SOCKET_ERROR
		    RETURN .F.
		ENDIF
		RETURN .T.
	ENDPROC


	*-- Retrieves data from the OLE drag and drop DataObject object.
	PROCEDURE getdata
		LPARAMETERS tcOutData
		* NOTE: tcOutData MUST be passed by reference, ie: Sock.GetData( @Outstr )
		tcOutData = THIS.cIn
		THIS.cIn = ''
	ENDPROC


	PROCEDURE bytesreceived_access
		    THIS.Rd()
		    RETURN LEN(THIS.cIn)
	ENDPROC


	PROTECTED PROCEDURE rd
		    LOCAL hEventRead, nWait, cRead, cRecv, nRecv, nFlags, lcRead
		    DO WHILE .T.
		        * creating event, linking it to the socket and wait
		        hEventRead = WSACreateEvent()
		        = WSAEventSelect(THIS.hSocket, hEventRead, FD_READ)

		        * 1000 milliseconds can be not enough
		        THIS.WaitForRead = WSAWaitForMultipleEvents(1, @hEventRead, 0, READ_FROM_SERVER_TIMEOUT, 0)
		        = WSACloseEvent(hEventRead)

		        IF THIS.WaitForRead <> 0 && error or timeout
		            EXIT
		        ENDIF

		        cRecv = Repli(Chr(0), READ_SIZE)
		        nFlags = 0
		        nRecv = recv(THIS.hSocket, @cRecv, READ_SIZE, nFlags)

		        IF nRecv>0
		          THIS.cIn = THIS.cIn + LEFT(cRecv, nRecv)
		        ENDIF
		    ENDDO
	ENDPROC


	PROTECTED PROCEDURE getip
		LPARAMETERS pcHost 
		      LOCAL nStruct, nSize, cBuffer, nAddr, cIP
		      nStruct = gethostbyname(pcHost)
		      IF nStruct = 0
		          RETURN ""
		      ENDIF
		      cBuffer = Repli(Chr(0), HOSTENT_SIZE)
		      cIP = Repli(Chr(0), 4)
		      = CopyMemory(@cBuffer, nStruct, HOSTENT_SIZE)
		      = CopyMemory(@cIP, THIS.buf2dword(SUBS(cBuffer,13,4)),4)
		      = CopyMemory(@cIP, THIS.buf2dword(cIP),4)
		RETURN inet_ntoa(THIS.buf2dword(cIP))
	ENDPROC


	PROCEDURE buf2dword
		LPARAMETERS lcBuffer
		   
		    RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
		        BitLShift(Asc(SUBSTR(lcBuffer, 2,1)), 8) +;
		        BitLShift(Asc(SUBSTR(lcBuffer, 3,1)), 16) +;
		        BitLShift(Asc(SUBSTR(lcBuffer, 4,1)), 24)
	ENDPROC


	PROCEDURE num2dword
		LPARAMETERS lnValue
		      
		      IF lnValue < 0
		          lnValue = 0x100000000 + lnValue
		      ENDIF
		      LOCAL b0, b1, b2, b3
		      b3 = Int(lnValue/2^24)
		      b2 = Int((lnValue - b3*2^24)/2^16)
		      b1 = Int((lnValue - b3*2^24 - b2*2^16)/2^8)
		      b0 = Mod(lnValue, 2^8)
		RETURN Chr(b0)+Chr(b1)+Chr(b2)+Chr(b3)
	ENDPROC


	PROCEDURE num2word
		LPARAMETERS lnValue
		    RETURN Chr(MOD(m.lnValue,256)) + CHR(INT(m.lnValue/256))
		 
	ENDPROC


	PROCEDURE host_assign
		LPARAMETERS vNewVal
		*To do: Modify this routine for the Assign method
		THIS.LocalHostName = vNewVal
	ENDPROC


	PROCEDURE ip_assign
		LPARAMETERS vNewVal
		*To do: Modify this routine for the Assign method
		THIS.LocalIp = vNewVal
	ENDPROC


	PROCEDURE port_assign
		LPARAMETERS vNewVal
		*To do: Modify this routine for the Assign method
		THIS.LocalPort = vNewVal
	ENDPROC


	PROCEDURE Init
		 * This class was written by William GC Steinford
		  *   based on code posted by AnatoliyMogylevets on fox.wikis.com
		  * This class is designed to mimic the features of the MSWINSCK.WinSock activeX control
		  *   which are used by SendSmtpEmail

		  * Public Interface Properties:
		  *   N - State
		  *   N - BytesReceived (read only)
		  *   C - Host          (read only)
		  *   C - IP            (read only)
		  *   N - Port          (read only)
		  *   C - cIn           (read/write)
		  *
		  * Public Interface Methods:
		  *   L - Connect( cServer, nServerPort )
		  *   L - Close()
		  *   L - SendData( cData )
		  *   L - GetData( @cDataOut )

		  * State property Values
		  *   0 Default. Closed
		  *   1 Open
		  *   2 Listening
		  *   3 Connection pending
		  *   4 Resolving host
		  *   5 Host resolved
		  *   6 Connecting
		  *   7 Connected
		  *   8 Peer is closing the connection
		  *   9 Error
		  
		  
		  * Performance Adjustable Constants:
		  #DEFINE READ_SIZE                16384
		  #DEFINE READ_FROM_SERVER_TIMEOUT 200

		  * API Constants:
		  #DEFINE SMTP_PORT 25
		  #DEFINE HTTP_PORT 80
		  #DEFINE AF_INET 2
		  #DEFINE SOCK_STREAM 1
		  #DEFINE IPPROTO_TCP 6
		  #DEFINE SOCKET_ERROR -1
		  #DEFINE FD_READ 1
		  #DEFINE HOSTENT_SIZE 16
		  
		  DECLARE INTEGER gethostbyname IN ws2_32 STRING host
		  DECLARE STRING inet_ntoa IN ws2_32 INTEGER in_addr
		  DECLARE INTEGER socket IN ws2_32 INTEGER af, INTEGER tp, INTEGER pt
		  DECLARE INTEGER closesocket IN ws2_32 INTEGER s
		  DECLARE INTEGER WSACreateEvent IN ws2_32
		  DECLARE INTEGER WSACloseEvent IN ws2_32 INTEGER hEvent
		  DECLARE GetSystemTime IN kernel32 STRING @lpSystemTime
		  DECLARE INTEGER inet_addr IN ws2_32 STRING cp
		  DECLARE INTEGER htons IN ws2_32 INTEGER hostshort
		  DECLARE INTEGER WSAStartup IN ws2_32 INTEGER wVerRq, STRING lpWSAData
		  DECLARE INTEGER WSACleanup IN ws2_32

		  DECLARE INTEGER connect IN ws2_32 AS ws_connect ;
		        INTEGER s, STRING @sname, INTEGER namelen

		  DECLARE INTEGER send IN ws2_32;
		        INTEGER s, STRING @buf, INTEGER buflen, INTEGER flags

		  DECLARE INTEGER recv IN ws2_32;
		        INTEGER s, STRING @buf, INTEGER buflen, INTEGER flags

		  DECLARE INTEGER WSAEventSelect IN ws2_32;
		        INTEGER s, INTEGER hEventObject, INTEGER lNetworkEvents

		  DECLARE INTEGER WSAWaitForMultipleEvents IN ws2_32;
		        INTEGER cEvents, INTEGER @lphEvents, INTEGER fWaitAll,;
		        INTEGER dwTimeout, INTEGER fAlertable

		  DECLARE RtlMoveMemory IN kernel32 As CopyMemory;
		        STRING @Dest, INTEGER Src, INTEGER nLength

		    IF WSAStartup(0x202, Repli(Chr(0),512)) <> 0
		      * unable to initialize Winsock on this computer
		      RETURN .F.
		    ENDIF
		   
		RETURN .T.

		  
	ENDPROC


	PROCEDURE Destroy
		=WSACleanup()
	ENDPROC
ENDDEFINE
*
*-- EndDefine: foxsock
**************************************************
 
OK, thanks for that code. It's not what I downloaded from vfpwinsock, you seem to have followed a link to fox wikis or news2news which were referred there as the initial sources of the vfpwinsock class. In the page I referred you click Downloads from the left side menu, to download vfpwinsock.

But doesn't matter for the moment.

First of all, do I understand your problem correctly? You are missing, that LocalIP is set in the instance of that foxsock class? That seems a feature really missing from ws2_32.dll, but you could get that ip in different other ways, eg calling the dos command ipconfig via RUN ipconfig >D:\ip.txt, then taking the ip adress from the line containing ipv4.

And ther computername is even easier to extract from ID(), or you call GETENV("Computername").

Would that solve your problem?

Bye, Olaf.
 
Olaf,

I worked around the VFPwinsock and used the "gethostbyname IN ws2_32" and "gethostname IN ws2_32" to populate the LocalHostName and LocalIP. Hence it is working for me. By the way, I got the last version of the VFPwinsock from the official site and has since edited the custom control code that I posted.

My next task is to cause VFPwinsock to do ALL what I got MsWinsock to do.


Thanks for your help. I think that this thread can be closed now.
 
OK. You can of course ask about further problems with the winsock class in further threads. Remember I also have the code, so you may refer to partial code of interest, specifically your code and what does not work.

In regard to closing the thread: There are no moderators here, at least not specific to this forum, and there is no closing process. Threads close automatic after reaching a certain age.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top