![]() |
|
Published 1999-05-01 Printer-friendly version
OK, I admit it. If you hadnt already guessed from the title, this months column isnt about synchronous RS232 communication, for the simple reason I havent had time to write it yet.
Instead, Im going to give you the basic information you need to get the computer name and/or IP address, and calculate the other.
Why do you need this? Well, theres a number of valid reasons. You could use it as part of a copy-protection algorithm, or just for diagnostic purposes in a large network environment.
If your application uses soft-coded filenames that are dynamically assigned at execution time to put data files on a server, you can resolve the IP address of the server to a name and then use the machine name in the soft-coded file name.
And it even works on a larger scale. For example, given that we know a web site address (like www.clariononline.com ) we can find its IP address just by pinging the server. Once we know its IP address, we can look up the machine name. If that machine had a shared directory that we had write privileges to, we could use that name in an application and open data files on that machine, or just simply copy files to it.
We accomplish all of this by using the Winsock library. Winsock comes in 2 flavours, 16-bit and 32-bit, just like the rest of the Windows API. The example program is a 32-bit CW2003 application, but theres no reason why you cant change it to be 16-bit if necessary.
Winsock is actually the name given to the specification that defines the network programming interface for Microsoft Windows, which in turn is based on the "socket" standard introduced in the Berkeley Software Distribution (BSD) from the University of California at Berkeley.
We can use just over half-a-dozen Winsock API functions to get the name of the computer we are on, to get the IP address of any computer with a known name, and to get the name of any computer with a known IP address.
However, as with most things, there a couple of things you need to know.
First, the Winsock API often uses 4-byte IP addresses. You have to convert the regular "dotted-decimal" IP address we all know and love into something 4-bytes long before you can use it in a API call, and back again when you return from the function. Whether its a STRING(4) or a LONG shouldnt really matter as long its only 4 bytes.
Second, theres a lot of pointers involved. Clarion doesnt handle pointers too well you can create them easily enough using ADDRESS(), but de-referencing them isnt as easy as it could be. To do that we have to use the C runtime function MEMCPY(), which can get a little confusing, as Ill show later.
Third, youll need an import library to link against. Ive provided a 32-bit version in this months example, but you can always make your own by running LibMaker (provided as an example by the CW install) on the Winsock DLL from your \Windows or \WINNT directory.
Anyway, the Winsock APIs we will be using are:
! Initialise Winsock
WSAStartup(USHORT,LONG),LONG,PASCAL,NAME('WSASTARTUP'),DLL(dll_mode)
! Get the last error from the runtime
WSAGetLastError(),LONG,RAW,PASCAL,NAME('WSAGETLASTERROR'),DLL(dll_mode)
! Get the name of this machine
GetHostName(*CSTRING,LONG),LONG,RAW,PASCAL,NAME('GetHostName'),DLL(dll_mode)
! Look up a IP address from a name
GetHostByName(LONG),LONG,PASCAL,RAW,NAME('GetHostByName'),DLL(dll_mode)
! Look up a name by address
GetHostByAddr(Long,Long,Long),Long,Pascal,Raw,Name('GetHostByAddr'),Dll(dll_mode)
! Change the 4-byte address into dotted decimal
Inet_NToA(LONG),LONG,PASCAL,NAME('INet_NToA'),DLL(dll_mode)
! Change the dotted-decimal into a 4-byte address
INet_Addr(LONG),ULONG,PASCAL,NAME('INET_ADDR'),DLL(dll_mode)
Before you can use a Winsock function, you have to call WSAStartup first. It initializes the Winsock DLL, and returns to you details about the Winsock implementation (which Ill conveniently ignore). Once youve successfully done this, then you can go on and use the rest of the functions.
Getting the machine name is absurdly easy, as this code fragment shows Once Winsock is up and running, just call GetHostName. Pass it a CSTRING and the length of the string, and itll put the name in the string for you.
MachineName Cstring(100)
Code
If GetHostName(Loc:MachineName,100) = 0
Message('Local machine name is ' & Clip(Loc:MachineName))
End
A return code of 0 means it worked OK; a return code of 1 means it didnt. If it fails and you want to know why, call WSAGETLASTERROR() and it will the error code, which you can use to identify the problem.
This is a little more complicated. Remember the problem that CW has with pointers? This is where it really shows itself. To get the IP address, you call the GETHOSTBYNAME() function.
Pass it a pointer to the name, and it returns to you a pointer to a HOSTENT structure.
Loc:Pointer = GetHostByName( Address(Loc:MachineName) )
A HOSTENT structure is what is frequently used to communicate between you, the application, and Winsock. Its C definition is:
struct hostent {
char FAR * h_name;
char FAR * FAR * h_aliases;
short h_addrtype;
short h_length;
char FAR * FAR * h_addr_list; };
In Clarion terms its basically a group which contains 3 pointers and 2 shorts.
Assuming the call to GETHOSTBYNAME() worked, we now have a pointer to one of these groups. Were interested in the h_addr_list field, which is a pointer to a pointer to a 4 byte string containing the IP address. Once weve de-referenced all those pointers and got that 4-byte string, we still have to convert it into a dotted decimal IP address that we can read.
The Clarion code for that is:
If Loc:Pointer <> 0
Memcpy(ADDRESS(Loc:Ptr3),Loc:Pointer+12,4)
If Loc:Ptr3 <> 0
Memcpy(ADDRESS(Loc:StrPtr),Loc:Ptr3,4)
If Loc:StrPtr <> 0
Memcpy(ADDRESS(Loc:IPAddr),Loc:StrPtr,4)
Loc:StrPtr = INet_NToA(Loc:IPAddr)
If Loc:StrPtr <> 0
LStrCpy(Loc:IPAddress,Loc:StrPtr)
Message('IP address of machine is ' & Loc:IPAddress)
End
End
End
End
Lots of copying going on here. A pointer is 4 bytes long, so at the beginning we de-reference the first pointer. Assuming that worked and we didnt end up with an empty pointer, we do it again and get a pointer to the string. We de-reference that pointer, and get the 4-byte address, then call the Winsock function INET_NTOA(). It translates the 4-byte address into a dotted decimal IP address, and returns us another pointer which we then de-reference again to get the final IP address.
Basically the reverse of the above, but theres a lot fewer pointers to worry about. We first have to convert our dotted decimal IP address back into a Winsock 4-byte address by calling INET_ADDR() with a pointer to the dotted-decimal address. It returns a 4-byte address (or FFFFFFFFH if theres an error) which we then pass into GETHOSTBYADDR()
That function returns a pointer to a pointer to the string. We check that the call worked and that we have a valid pointer, then just de-reference the pointers and copy the first 35 bytes of the name into a local string.
Loc:IPAddr = INet_Addr( Address(Loc:IPAddress) )
If Loc:IPAddr = InAddr_None
! We can't convert the address for some reason
Else
Loc:HostPtr = GetHostByAddr(Address(Loc:IPAddr),4,PF_INET)
Loc:Error = WSAGetLastError()
If Loc:Error = 0 And Loc:HostPtr <> 0 Then
MemCpy( Address(Loc:NamePtr),Loc:HostPtr,4 )
MemCpy( Address(Loc:MachineName),Loc:NamePtr,35 )
Message('Local machine name is ' & Clip(Loc:MachineName))
Else
! Winsock couldn't look up the name for the IP address
End
End
Thats all there is to it. Not too difficult to implement, once you understand how to use MEMCPY() to de-reference pointers, and can actually follow the complicated pointer manipulation that Winsock imposes.
Synchronous RS232 communication using the Windows API honest!
The best place for information is MSDN. If you don't subscribe, you can still get to the information via the web at http://msdn.microsoft.com/
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $184
(includes all back issues since '99)
Renewals from $134
Two years: $274
Renewals from $224