An Example Using RasDial

So what would you do with RAS? Take as an example the application I mentioned above ”getting information from several remote sites to a central location. Listing 6-1 contains the code for a simple console mode application named RasDial.exe that allows a file to be copied from a local drive to a remote drive after the establishment of a RAS session. The complete project is included on the companion CD-ROM.

Listing 6-1

RasDial.cpp

 //RasDial.cpp:SimpleRASdialingexample... // #include"stdafx.h" //Prototypeforcallbackfunction... DWORDWINAPIRasDialFunc2(DWORDdwCallbackId,//User-definedvaluespecifiedin //RasDialcall DWORDdwSubEntry,//Subentryindexinmultilinkconnection HRASCONNhrasconn,//HandletoRASconnection UINTunMsg,//Typeofeventthathasoccurred RASCONNSTATErascs,//Connectionstateabouttobeentered DWORDdwError,//Errorthatmayhaveoccurred DWORDdwExtendedError//Extendederrorinformationfor //someerrors); HANDLEmuNotConnected; //-------------------------------------------------------------------- //Mainroutineforconsolemodeapplication... intmain(intargc,char*argv[]) { RASDIALPARAMSrdp; HRASCONNhrasconn=NULL; DWORDret; FILE*in; FILE*out; charbuffer[4096]; memset((void*)&rdp,' 
 //  RasDial.cpp:  Simple  RAS  dialing  example... // #include  "stdafx.h" //  Prototype  for  callback  function... DWORD  WINAPI  RasDialFunc2(DWORD  dwCallbackId,        // User -defined  value  specified  in                                                       //    RasDial  call     DWORD  dwSubEntry,                //  Subentry  index  in  multilink  connection     HRASCONN  hrasconn,              //  Handle  to  RAS  connection     UINT  unMsg,                            //  Type  of  event  that  has  occurred     RASCONNSTATE  rascs,            //  Connection  state  about  to  be  entered     DWORD  dwError,                      //  Error  that  may  have  occurred     DWORD  dwExtendedError        //  Extended  error  information  for                                                       //    some  errors); HANDLE  muNotConnected; //-------------------------------------------------------------------- //  Main  routine  for  console  mode  application... int  main(int  argc,  char*  argv[]) {         RASDIALPARAMS  rdp;         HRASCONN  hrasconn=NULL;         DWORD  ret;         FILE  *in;         FILE  *out;         char  buffer[4096]; memset ((void  *)&rdp,  '\0',  sizeof(RASDIALPARAMS));         rdp.dwSize  =  sizeof(RASDIALPARAMS);         strcpy(rdp.szEntryName,  "TEST");         strcpy(rdp.szPhoneNumber,  "");         strcpy(rdp.szCallbackNumber,  "");         strcpy(rdp.szUserName,  "Dougr");         strcpy(rdp.szPassword,  "SECRET"); strcpy (rdp.szDomain,  "ACCESS");         //  Create  the  mutex;  don't  worry  about  security  (first  NULL),         //    false  (initially  DO  NOT  own  the  mutex).         //    Second  NULL  means  object  not  named,  since  it  will  not          //    be  shared  among  processes...         muNotConnected  =  CreateMutex(NULL,  false,  NULL);         ret  =  RasDial(NULL,  NULL,  &rdp,  2,  RasDialFunc2,  &hrasconn);         if  (ret)         {                 char  buffer[255];                 RasGetErrorString(ret,  buffer,  255);                 printf("\nGetLastError=%d  %s",  GetLastError(),  buffer);                 return(0);         }         //  This  is  to  give  the  first  call  to  RasDialFunc2         //    time  to  get  mutex...         Sleep(1000);         //  Wait  a  long  time,  100  seconds.  This  is  likely  longer         //    than  needed  for  the  file  in  question  but  allows         //    extra  time  for  negotiation.         switch  (WaitForSingleObject(muNotConnected,  100000))         {         case  WAIT_ABANDONED:                 printf("\nWait abandoned ...Thread terminated without  giving"                                 "  up  control!");                 break;         case  WAIT_TIMEOUT:                 printf("\n100  seconds  elapsed...Bailing.");                 break;         case  WAIT_OBJECT_0:                 printf("\nObject acquired ...All  seems  well");                 in  =  fopen("c:\\service.log",  "r+t");                 //  Note  that  the  'c'  included  in  the  mode  parameter                 //    is  used  to  force  the  contents  to  be  written                  //    directly  to  disk  when  fflush  is  called.                 //    Obviously,  the path should  be  modified  for                 //    your  server.                 out  =  fopen("\\\\dougr\\shared\\service.log",  "w+tc");                 if  (in  ==  NULL    out==NULL)                 {                         printf("\nCannot open file...");                 }                 else                 {                         size_t  bytes;                         while  ((bytes  =  fread((void  *)buffer,  1,  4096,  in)))                         {                                 fwrite((void  *)buffer,  1,  bytes,  out);                         }                         fclose(in); fflush (out);                         fclose(out);                         printf("\nData  Copied!");                 }                 break;         }         RasHangUp(hrasconn);         CloseHandle(muNotConnected);         return  0; } //-------------------------------------------------------------------- //  Callback  function... DWORD  WINAPI  RasDialFunc2(DWORD  dwCallbackId,                //  User-defined  value  specified  in                                                               //    RasDial  call         DWORD  dwSubEntry,                    //  Subentry  index  in  multilink                                                               //    connection         HRASCONN  hrasconn,                  //  Handle  to  RAS  connection         UINT  unMsg,                                //  Type  of  event  that  has  occurred         RASCONNSTATE  rasconnstate,  //  Connection  state  about  to  be                                                               //    entered         DWORD  dwError,                          //  Error  that  may  have  occurred         DWORD  dwExtendedError            //  Extended  error  information  for                                                               //    some  errors) {         switch(rasconnstate)         {         case  RASCS_OpenPort:                 if  (WaitForSingleObject(muNotConnected,  0)  !=                         WAIT_OBJECT_0)                 {                         printf("\nShould  not  be  here!    Can't  get  mutex!");                 }                 else                 {                         printf("\nrasconnstate=RASCS_OpenPort");                 }                 break;         case  RASCS_PortOpened:                 printf("\nrasconnstate=RASCS_PortOpened");                 break;         case  RASCS_ConnectDevice:                 printf("\nrasconnstate=RASCS_ConnectDevice");                 break;         case  RASCS_DeviceConnected:                 printf("\nrasconnstate=RASCS_DeviceConnected");                 break;         case  RASCS_AllDevicesConnected:                 printf("\nrasconnstate=RASCS_AllDevicesConnected");                 break;         case  RASCS_Authenticate:                 printf("\nrasconnstate=RASCS_Authenticate");                 break;         case  RASCS_AuthNotify:                 printf("\nrasconnstate=RASCS_AuthNotify");                 break;         case  RASCS_AuthRetry:                 printf("\nrasconnstate=RASCS_AuthRetry");                 break;         case  RASCS_AuthCallback:                 printf("\nrasconnstate=RASCS_AuthCallback");                 break;         case  RASCS_AuthChangePassword:                 printf("\nrasconnstate=RASCS_AuthChangePassword");                 break;         case  RASCS_AuthProject:                 printf("\nrasconnstate=RASCS_AuthProject");                 break;         case  RASCS_AuthLinkSpeed:                 printf("\nrasconnstate=RASCS_AuthLinkSpeed");                 break;         case  RASCS_AuthAck:                 printf("\nrasconnstate=RASCS_AuthAck");                 break;         case  RASCS_ReAuthenticate:                 printf("\nrasconnstate=RASCS_ReAuthenticate");                 break;         case  RASCS_Authenticated:                 printf("\nrasconnstate=RASCS_Authenticated");                 break;         case  RASCS_PrepareForCallback:                 printf("\nrasconnstate=RASCS_PrepareForCallback");                 break;         case  RASCS_WaitForModemReset:                 printf("\nrasconnstate=RASCS_WaitForModemReset");                 break;         case  RASCS_WaitForCallback:                 printf("\nrasconnstate=RASCS_WaitForCallback");                 break;         case  RASCS_Projected:                 printf("\nrasconnstate=RASCS_Projected");                 break;         case  RASCS_SubEntryConnected:                 printf("\nrasconnstate=RASCS_SubEntryConnected");                 break;         case  RASCS_SubEntryDisconnected:                 printf("\nrasconnstate=RASCS_SubEntryDisconnected");                 break;         case  RASCS_Interactive:                 printf("\nrasconnstate=RASCS_Interactive");                 break;         case  RASCS_RetryAuthentication:                 printf("\nrasconnstate=RASCS_RetryAuthentication");                 break;         case  RASCS_CallbackSetByCaller:                 printf("\nrasconnstate=RASCS_CallbackSetByCaller");                 break;         case  RASCS_PasswordExpired:                 printf("\nrasconnstate=RASCS_PasswordExpired");                 break; #if  (WINVER  >=  0x500)         case  RASCS_InvokeEapUI:                 printf("\nrasconnstate=RASCS_InvokeEapU");                 break; #endif         case  RASCS_Connected:                 printf("\nrasconnstate=RASCS_Connected  -  Copy  the  data...");                 ReleaseMutex(muNotConnected);                 break;         case  RASCS_Disconnected:                 printf("\nrasconnstate=RASCS_Disconnected");                 break;         }         if  (dwError)         {                 printf("\nGot  here  error=%d  rasconnstate=%d",                         dwError,rasconnstate);         }         return  1;    //  Continue  getting  notifications... } 
',sizeof(RASDIALPARAMS)); rdp.dwSize=sizeof(RASDIALPARAMS); strcpy(rdp.szEntryName,"TEST"); strcpy(rdp.szPhoneNumber,""); strcpy(rdp.szCallbackNumber,""); strcpy(rdp.szUserName,"Dougr"); strcpy(rdp.szPassword,"SECRET"); strcpy(rdp.szDomain,"ACCESS"); //Createthemutex;don'tworryaboutsecurity(firstNULL), //false(initiallyDONOTownthemutex). //SecondNULLmeansobjectnotnamed,sinceitwillnot //besharedamongprocesses... muNotConnected=CreateMutex(NULL,false,NULL); ret=RasDial(NULL,NULL,&rdp,2,RasDialFunc2,&hrasconn); if(ret) { charbuffer[255]; RasGetErrorString(ret,buffer,255); printf("\nGetLastError=%d%s",GetLastError(),buffer); return(0); } //ThisistogivethefirstcalltoRasDialFunc2 //timetogetmutex... Sleep(1000); //Waitalongtime,100seconds.Thisislikelylonger //thanneededforthefileinquestionbutallows //extratimefornegotiation. switch(WaitForSingleObject(muNotConnected,100000)) { caseWAIT_ABANDONED: printf("\nWaitabandoned...Threadterminatedwithoutgiving" "upcontrol!"); break; caseWAIT_TIMEOUT: printf("\n100secondselapsed...Bailing."); break; caseWAIT_OBJECT_0: printf("\nObjectacquired...Allseemswell"); in=fopen("c:\service.log","r+t"); //Notethatthe'c'includedinthemodeparameter //isusedtoforcethecontentstobewritten //directlytodiskwhenfflushiscalled. //Obviously,thepathshouldbemodifiedfor //yourserver. out=fopen("\\dougr\shared\service.log","w+tc"); if(in==NULLout==NULL) { printf("\nCannotopenfile..."); } else { size_tbytes; while((bytes=fread((void*)buffer,1,4096,in))) { fwrite((void*)buffer,1,bytes,out); } fclose(in); fflush(out); fclose(out); printf("\nDataCopied!"); } break; } RasHangUp(hrasconn); CloseHandle(muNotConnected); return0; } //-------------------------------------------------------------------- //Callbackfunction... DWORDWINAPIRasDialFunc2(DWORDdwCallbackId,//User-definedvaluespecifiedin //RasDialcall DWORDdwSubEntry,//Subentryindexinmultilink //connection HRASCONNhrasconn,//HandletoRASconnection UINTunMsg,//Typeofeventthathasoccurred RASCONNSTATErasconnstate,//Connectionstateabouttobe //entered DWORDdwError,//Errorthatmayhaveoccurred DWORDdwExtendedError//Extendederrorinformationfor //someerrors) { switch(rasconnstate) { caseRASCS_OpenPort: if(WaitForSingleObject(muNotConnected,0)!= WAIT_OBJECT_0) { printf("\nShouldnotbehere!Can'tgetmutex!"); } else { printf("\nrasconnstate=RASCS_OpenPort"); } break; caseRASCS_PortOpened: printf("\nrasconnstate=RASCS_PortOpened"); break; caseRASCS_ConnectDevice: printf("\nrasconnstate=RASCS_ConnectDevice"); break; caseRASCS_DeviceConnected: printf("\nrasconnstate=RASCS_DeviceConnected"); break; caseRASCS_AllDevicesConnected: printf("\nrasconnstate=RASCS_AllDevicesConnected"); break; caseRASCS_Authenticate: printf("\nrasconnstate=RASCS_Authenticate"); break; caseRASCS_AuthNotify: printf("\nrasconnstate=RASCS_AuthNotify"); break; caseRASCS_AuthRetry: printf("\nrasconnstate=RASCS_AuthRetry"); break; caseRASCS_AuthCallback: printf("\nrasconnstate=RASCS_AuthCallback"); break; caseRASCS_AuthChangePassword: printf("\nrasconnstate=RASCS_AuthChangePassword"); break; caseRASCS_AuthProject: printf("\nrasconnstate=RASCS_AuthProject"); break; caseRASCS_AuthLinkSpeed: printf("\nrasconnstate=RASCS_AuthLinkSpeed"); break; caseRASCS_AuthAck: printf("\nrasconnstate=RASCS_AuthAck"); break; caseRASCS_ReAuthenticate: printf("\nrasconnstate=RASCS_ReAuthenticate"); break; caseRASCS_Authenticated: printf("\nrasconnstate=RASCS_Authenticated"); break; caseRASCS_PrepareForCallback: printf("\nrasconnstate=RASCS_PrepareForCallback"); break; caseRASCS_WaitForModemReset: printf("\nrasconnstate=RASCS_WaitForModemReset"); break; caseRASCS_WaitForCallback: printf("\nrasconnstate=RASCS_WaitForCallback"); break; caseRASCS_Projected: printf("\nrasconnstate=RASCS_Projected"); break; caseRASCS_SubEntryConnected: printf("\nrasconnstate=RASCS_SubEntryConnected"); break; caseRASCS_SubEntryDisconnected: printf("\nrasconnstate=RASCS_SubEntryDisconnected"); break; caseRASCS_Interactive: printf("\nrasconnstate=RASCS_Interactive"); break; caseRASCS_RetryAuthentication: printf("\nrasconnstate=RASCS_RetryAuthentication"); break; caseRASCS_CallbackSetByCaller: printf("\nrasconnstate=RASCS_CallbackSetByCaller"); break; caseRASCS_PasswordExpired: printf("\nrasconnstate=RASCS_PasswordExpired"); break; #if(WINVER>=0x500) caseRASCS_InvokeEapUI: printf("\nrasconnstate=RASCS_InvokeEapU"); break; #endif caseRASCS_Connected: printf("\nrasconnstate=RASCS_Connected-Copythedata..."); ReleaseMutex(muNotConnected); break; caseRASCS_Disconnected: printf("\nrasconnstate=RASCS_Disconnected"); break; } if(dwError) { printf("\nGothereerror=%drasconnstate=%d", dwError,rasconnstate); } return1;//Continuegettingnotifications... }

Take a look at the top of the listing. The main routine first declares several variables used for the RAS calls and for the actual file copying that is the objective of this exercise. The RASDIALPARAMS structure, rdp , is initialized next . First, dwSize is set to the size of the structure. This entry is used by RAS to determine the version of the structure being sent. The next entry, szEntryName , is the name of the dial-up connection as it appears on the Settings/Network and Dial-Up Connections menu. The szPhoneNumber entry is the phone number to dial if it differs from the phone number stored in the connection specified by szEntryName . The szCallbackNumber entry is used to specify a number for the remote site to call back to the originator of the call.

NOTE
In some previous Windows versions, szCallbackNumber could be set to an asterisk (*) to indicate that the number stored in the registry as a callback should be used. This is not a valid option in Windows 2000 or Windows NT version 4 or later. This callback number is ignored unless the user has "Set By Caller" permission for callbacks on the remote RAS server.

The next three entries are used for authenticating the RAS connection. The user name and password are self-explanatory, but the connection might not work exactly as you think it will. For example, the user name and password are used for access to the RAS server on the other end of the connection, but this does not mean you are logged on as that user on the remote network. If the user name and password on the local machine and the remote machine are the same, this gives an appearance of simultaneously logging on to the RAS server and some of the resources on the other end of the connection. This is only an appearance. If you need to access resources under an authority different from the user name and password on the local machine, the application might need to call LogonUser , as described in Chapter 4.

You might or might not need to set the szDomain entry, depending on the identity of the server being called. If you're connecting to a Windows 2000 server, you are better off specifying a domain. Connecting to Internet servers running other operating systems, such as Unix, generally does not require a domain name to be specified. Setting an asterisk for the domain name specifies that the domain set in the phonebook entry should be used.

Controlling Multiple Threads

The mutex muNotConnected is created with a call to CreateMutex . (See Chapter 2 for details.) The mutex is created with a NULL passed as the security descriptor to get the default security descriptor, a false indicating that the calling thread does not want ownership of the object, and a final NULL indicating that no name is to be used. The name is not required because this mutex will not be used across process boundaries.

The next step is to actually dial the phone, using the RasDial Win32 API function. The prototype for this function is as follows :

 DWORDRasDial(LPRASDIALEXTENSIONSlpRasDialExtensions, //Pointertofunctionextensionsdata LPCTSTRlpszPhonebook,//Pointertofullpathandfilenameof //phonebookfile LPRASDIALPARAMSlpRasDialParams, //Pointertocallingparametersdata DWORDdwNotifierType,//SpecifiestypeofRasDialeventhandler LPVOIDlpvNotifier,//SpecifiesahandlerforRasDialevents LPHRASCONNlphRasConn//Pointertovariabletoreceive //connectionhandle); 

The first parameter, lpRasDialExtensions , is used to set some advanced options or can be set to NULL if no options need to be set. Features you can set using this structure include the use of a dialing prefix and suffix, the modem speaker, software compression, the use of scripting options, and Windows 2000 Extensible Authentication Protocol (EAP). In the RasDial.exe sample program, lpRasDialExtensions is set to NULL.

The second parameter, lpszPhonebook , is a pointer to the name of a phonebook file if the default phonebook is not to be used. Phonebook files have a .pbk extension. Next, the address of the RASDIALPARAMS structure filled in earlier is passed as the lpRasDialParams parameter. The fourth parameter, dwNotifierType , specifies the way in which asynchronous events generated by dialing are handled. Interestingly, there are no defined constants for these values. 0xFFFFFFFF specifies that a window procedure should receive notifications and that the fifth parameter, lpvNotifier , should contain a window handle. This parameter is not commonly used in server-based application development. A value of 0, 1, or 2 specifies that, respectively, lpvNotifier should contain a pointer to a RasDialFunc , RasDialFunc1 , or RasDialFunc2 function. The RasDial.exe example uses a dwNotifierType of 2 and therefore specifies a callback function, RasDialFunc2 , whose prototype is declared earlier. The name of the function could be anything; I use RasDialFunc2 here for clarity. Alternatively, lpvNotified could be NULL, in which case RasDial operates synchronously. This is seldom an ideal choice in a server application. RasDialFunc , RasDialFunc1 , and RasDialFunc2 behave similarly, with RasDialFunc only accepting uMsg , rasconnstate , and dwError and returning no value. RasDialFunc1 adds hrasconn and dwExtendedError . RasDialFunc2 has the prototype above, and the value returned allows the callback to indicate if it wishes to receive further notifications. Returning 0 indicates no further notifications should be sent; a nonzero value indicates notifications should continue.

The final parameter is lphRasConn , a handle that is used for other RAS operations, including RasHangUp . If an error occurs in the call to RasDial , a message is displayed using another RAS API function, RasGetErrorString. The prototype for this function is as follows:

 DWORDRasGetErrorString(UINTuErrorValue,//Errortogetstringfor LPTSTRlpszErrorString,//Buffertoholderrorstring DWORDcBufSize//Size,incharacters,ofbuffer); 

The uErrorValue parameter is the value returned by one of the RAS functions. The lpszErrorString parameter is the buffer to receive the error string, and cBufSize is the size, in characters, of the buffer.

Once RasDial returns with a value of 0, the function waits briefly to allow the callback function to acquire the mutex muNotConnected. One second is allowed for this, and the mutex will be acquired on the first call to RasDialFunc2 , when the port is opened.

NOTE
One unusual thing about copying files over a RAS connection became clear to me during testing of this application. Notice in main the unusual file open mode passed in the call to fopen to open the file on the remote server. The "w" indicates the file should be opened for writing, the "+" indicates the file should be created if it does not exist, and the "t" indicates this is a text mode file, not unreasonable for the data being copied. But what about that "c"? I have programmed in C and C++ since 1984, and I've never seen this. The Microsoft Visual C++ documentation indicates the "c" is used to force the waiting data to be flushed to the disk when a call to fflush is made. On some occasions, without this additional character in the open mode parameter, I received an error upon the termination of the RAS connection indicating that not all data had been written to the disk before the connection was terminated. So while RAS is almost like being attached via a traditional network, it is not exactly like it.

The main function next calls WaitForSingleObject on the muNotConnected mutex with a timeout of 100,000 to allow 100 seconds for the connection to take place. If WaitForSingleObject returns with a return value of WAIT_ABANDONED, the thread that owned the mutex (in this case the thread calling RasDialFunc2 ) has terminated without giving up ownership of the mutex. This indicates some catastrophic failure in the RAS system. If WaitForSingleObject returns WAIT_TIMEOUT, 100 seconds have elapsed and the application assumes that the operation has failed.

If the object is acquired, which WaitForSingleObject indicates by returning WAIT_OBJECT_0, the connection should be present and the file copy takes place. Finally, RasHangUp is called with the handle obtained during the call to RasDial , and the handle to the mutex object is freed.

The RasDialFunc2 Callback Function

RasDialFunc2 is one of three different callback functions that can be used to make RasDial operate asynchronously. In the example program, RasDialFunc2 is little more than a switch statement that displays messages as the dial-up process takes place. One element that is important in creating a callback such as this is that any operation must take place and quickly return to the caller so that actual work can be done. In the case of communications in which modem negotiations are taking place, any delay could cause RAS to think an error has occurred. Of course, in the event of an error, it is allowable to take some time in the function, even putting up a message box if needed. In server-based applications, however, this is not usually advisable.

The only case blocks that are anything beyond debugging aids are the handlers for opening the port and connecting. When the port is opened, the following code section is executed:

 caseRASCS_OpenPort: if(WaitForSingleObject(muNotConnected,0)!=WAIT_OBJECT_0) { printf("\nShouldnotbehere!Can'tgetmutex!"); } else { printf("\nrasconnstate=RASCS_OpenPort"); } break; 

This function waits for the mutex with a timeout of 0, meaning that the function should immediately return, even if the object has not been acquired. If the return is not a return value indicating the object has been acquired, a message is displayed indicating the mutex cannot be obtained. This should never occur. When the connection is made, the following code is executed.

 caseRASCS_Connected: printf("\nrasconnstate=RASCS_Connected-Copythedata..."); ReleaseMutex(muNotConnected); break; 

This releases the mutex, which lets the main routine know that the connection has been made and the copy can proceed. This is how the callback lets the rest of the program know that a connection has been established.

When the program is run, notifications are received as the dialing takes place. The specific order of the notifications cannot be counted on, though using the notification associated with opening the port and the establishment of connections seems reliable in practice.



Inside Server-Based Applications
Inside Server-Based Applications (DV-MPS General)
ISBN: 1572318171
EAN: 2147483647
Year: 1999
Pages: 91

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net