Hi
I want to make a call with my mda without using the RAS dialer. Direct access to the COM-ports does not work, as described in the document "Serial Communications". But that documents mentions that TAPI calls are possible with an xda.
It says:
"If you´ve establised a call, TAPI returns a handle for further data-i/o..."
Is there an example how to make a call using TAPI especially how to get that data-handle. And furthermore can I use that handle with WriteFile / ReadFile as I would use a handle to a COM-Port?
I did this for a terminal emulator I wrote. I pass a string to these functions which is COM1: for serial, TAPI for modem or RAS for TCPIP RAS.
I have simply copied some of my code here, work through it as if lpszPortName = "TAPI" and you should make sense of it. Once you have the port handle hPort which is returned by the Connected response of the TAPI call back function, then you can read and write as if that were a serial/file handle.
Cheers
Paul
#include <windows.h>
#include <string.h>
#include <tapi.h>
#include "toolbox.h"
//Initilise the port either the TAPI or COMM port as requested by lpszPortName
BOOL PortInitialize (LPTSTR lpszPortName)
{
DWORD dwError;
BOOL RetValue;
HKEY hKey;
DWORD RegKeyDisp, RegKeyType, RegKeySize;
DWORD dwTapiNumDevs;
LONG lTapiReturn;
char PrintString[80];
TCHAR TempString[20];
char CharString[20];
LINEDEVCAPS TapiLineDevCaps;
static DWORD dwLocalAPIVersion;
LINEEXTENSIONID LineExtensionID;
LINECALLPARAMS LineCallParams;
static DWORD ChoosenDevice;
TCHAR DialNumber[30];
#ifdef _WIN32_WCE_EMULATION
if (wcscmp(lpszPortName, TEXT("TAPI")) != 0)
return TRUE; //If running under emulator then unable to emulate
#endif //the serial port, so in this 'test' mode just echo.
if (COMMPORTSHUT == TRUE) //If this is True then we are shutting down the program
return TRUE; //So don't try to re-open port as it gets stuck in an infinate loop
if (wcscmp(lpszPortName, TEXT("TAPI")) == 0)
{
#ifdef DEBUGVERSION
VTPrint("Entering (TAPI)PortInitilize\r\n" , 0);
#endif
//Retrive the telephone number from the registry
// Fill in the Modem Telephone number
wcscpy(DialNumber, TEXT("T"));
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\PVG\\Terminal\\Settings"),
0, NULL, 0,0, NULL, &hKey, &RegKeyDisp) == ERROR_SUCCESS)
{
//Retrive the telephone number
RegKeyType = REG_SZ;
RegKeySize = sizeof(DialNumber);
RegQueryValueEx(hKey, TEXT("Telephone"),
NULL, &RegKeyType,
(PBYTE)DialNumber + sizeof(TCHAR),
&RegKeySize);
RegCloseKey(hKey);
}
//Confirm there is a telephone number
if (*(DialNumber + 1) == 0)
{
VTPrint("No Telephone Number\r\n", 0);
return FALSE;
}
// Use TAPI to open a DATAMODEM communication channel
if (ghLineApp == NULL)
{
#ifdef DEBUGVERSION
VTPrint("Tapi - LineInitialise\r\n",0);
#endif
memset(&TapiLineDevCaps, 0, sizeof(TapiLineDevCaps));
lTapiReturn = lineInitialize(&ghLineApp, hInst, TapiCallBackFunction, NULL, &dwTapiNumDevs);
if (lTapiReturn)
{
wsprintf (TempString,TEXT("%d"), lTapiReturn);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
VTPrint("TAPI failed to initialise\n\rError Code = ",0);
VTPrint(CharString, 0);
VTPrint("\n\r",0);
TAPIShutdown();
return FALSE;
}
//Loop through devices to find one that can do data at 9600 baud (GSM)
for (ChoosenDevice=0 ; ChoosenDevice < dwTapiNumDevs ; ChoosenDevice++)
{
TapiLineDevCaps.dwTotalSize = sizeof(TapiLineDevCaps);
//Ask for at least TAPI Version 1.4
lTapiReturn = lineNegotiateAPIVersion(ghLineApp, ChoosenDevice, 0x00010004,
0x00010004, &dwLocalAPIVersion, &LineExtensionID);
#ifdef DEBUGVERSION
if (lTapiReturn)
{
wsprintf (TempString,TEXT("%d"), lTapiReturn);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
VTPrint("TAPI failed to negotiate API Version\n\rError Code = ",0);
VTPrint(CharString, 0);
VTPrint("\n\r",0);
}
#endif
if (!(lTapiReturn))
{
lTapiReturn=lineGetDevCaps(ghLineApp, ChoosenDevice, dwLocalAPIVersion, 0, &TapiLineDevCaps);
#ifdef DEBUGVERSION
if (lTapiReturn)
{
wsprintf (TempString,TEXT("%d"), lTapiReturn);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
VTPrint("TAPI failed to get device capibility\n\rError Code = ",0);
VTPrint(CharString, 0);
VTPrint("\n\r",0);
}
else
#endif
#ifndef DEBUGVERSION
if (!(lTapiReturn))
#endif
{
if ((TapiLineDevCaps.dwBearerModes & LINEBEARERMODE_VOICE) &&
(TapiLineDevCaps.dwMaxRate >= 9600) &&
(TapiLineDevCaps.dwMediaModes & LINEMEDIAMODE_DATAMODEM))
break;
}
}
}
if (!((TapiLineDevCaps.dwBearerModes & LINEBEARERMODE_VOICE) ||
(TapiLineDevCaps.dwMaxRate >= 9600) ||
(TapiLineDevCaps.dwMediaModes & LINEMEDIAMODE_DATAMODEM)))
{
VTPrint("Unable to find a modem device\r\n", 0);
TAPIShutdown();
lineShutdown(ghLineApp);
ghLineApp = NULL;
return FALSE;
}
//Now we have found a device capable of a dial up modem
strcpy(PrintString, "TAPI Initilised (");
wsprintf (TempString,TEXT("%d"), ChoosenDevice);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
strcat(PrintString, CharString);
strcat(PrintString, "/");
wsprintf (TempString,TEXT("%d"), dwTapiNumDevs);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 3, NULL, NULL);
strcat(PrintString, CharString);
strcat(PrintString, ")\n\r");
VTPrint(PrintString, 0);
}
//Open the TAPI line device
if (ghLine == NULL)
{
#ifdef DEBUGVERSION
VTPrint("TAPI - Obtaining line handle\r\n", 0);
#endif
lTapiReturn = lineOpen(ghLineApp, ChoosenDevice, &ghLine, dwLocalAPIVersion, 0 , 0,
LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM, 0);
if (lTapiReturn)
{
wsprintf (TempString,TEXT("%d"), lTapiReturn);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
VTPrint("TAPI failed to Open Line\n\rError Code = ",0);
VTPrint(CharString, 0);
VTPrint("\n\r",0);
TAPIShutdown();
return FALSE;
}
//Request specific notification messages
lTapiReturn = lineSetStatusMessages(ghLine, LINEDEVSTATE_RINGING | LINEDEVSTATE_CONNECTED |
LINEDEVSTATE_DISCONNECTED | LINEDEVSTATE_OUTOFSERVICE |
LINEDEVSTATE_MAINTENANCE | LINEDEVSTATE_CLOSE |
LINEDEVSTATE_REINIT, LINEADDRESSSTATE_OTHER);
if (lTapiReturn)
{
wsprintf (TempString,TEXT("%d"), lTapiReturn);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
VTPrint("TAPI failed to Set Status Messages\n\rError Code = ",0);
VTPrint(CharString, 0);
VTPrint("\n\r",0);
TAPIShutdown();
return FALSE;
}
//Configure line device for a data modem
memset(&LineCallParams, 0, sizeof(LineCallParams));
LineCallParams.dwTotalSize = sizeof(LineCallParams);
LineCallParams.dwBearerMode = LINEBEARERMODE_VOICE;
LineCallParams.dwMediaMode = LINEMEDIAMODE_DATAMODEM;
//Expect the line to start out idle (we don't want to break into a current call)
LineCallParams.dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
//If multiple addresses on the line, use the first address
LineCallParams.dwAddressMode = LINEADDRESSMODE_ADDRESSID;
LineCallParams.dwAddressID = 0;
}
//Make the call
if (ghCall == NULL)
{
#ifdef DEBUGVERSION
VTPrint("TAPI Making the call\r\n", 0);
#endif
lTapiReturn = lineMakeCall(ghLine, &ghCall, DialNumber, 0, &LineCallParams);
if (lTapiReturn < 0)
{
wsprintf (TempString,TEXT("%d"), lTapiReturn);
WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);
VTPrint("TAPI failed to Make the Call\n\rError Code = ",0);
VTPrint(CharString, 0);
VTPrint("\n\r",0);
TAPIShutdown();
ghCall = NULL;
return FALSE;
}
}
VTPrint("Call Initiated\r\n",0);
return TRUE;
}
if (wcscmp(lpszPortName, TEXT("RAS")) != 0)
{
{
// Open the serial port.
hPort = CreateFile (lpszPortName, // Pointer to the name of the port
GENERIC_READ | GENERIC_WRITE,
// Access (read-write) mode
0, // Share mode
NULL, // Pointer to the security attribute
OPEN_EXISTING,// How to open the serial port
0, // Port attributes
NULL); // Handle to port with attribute
// to copy
// If it fails to open the port, return FALSE.
if ( hPort == INVALID_HANDLE_VALUE )
{
// Could not open the port.
COMMPORTSHUT=TRUE;
MessageBox (hMainWnd, TEXT("Unable to open the port"),
TEXT("Error"), MB_OK);
dwError = GetLastError ();
return FALSE;
}
}
RetValue=InitiliseCommHandle();
return RetValue;
}
return TRUE;
}
//Initilise the communication handle, this occurs once a communication channel has been
//opened, either direct to a serial port or via TAPI
BOOL InitiliseCommHandle(void)
{
DWORD dwError,
dwThreadID;
DCB PortDCB;
COMMTIMEOUTS CommTimeouts;
#ifdef DEBUGVERSION
char SendString[40];
sprintf(SendString, "Handle = %d\r\n", hPort);
VTPrint(SendString,0);
#endif
// Get the default port setting information.
PortDCB.DCBlength = sizeof (DCB);
GetCommState (hPort, &PortDCB);
// Change the DCB structure settings.
if (wcscmp(COMMPORTNAME, TEXT("TAPI")) == 0)
{
//TAPI (modem) Settings
PortDCB.BaudRate = 19200; // Current baud
PortDCB.ByteSize = 8; // Number of bits/byte, 4-8
PortDCB.Parity = NOPARITY; // Parity odd,even,mark,space
}
else
{
//Standard Serial Port Settings
PortDCB.BaudRate = 1200; // Current baud
PortDCB.ByteSize = 7; // Number of bits/byte, 4-8
PortDCB.Parity = EVENPARITY; // Parity odd,even,mark,space
}
PortDCB.fBinary = TRUE; // Binary mode; no EOF check
PortDCB.fParity = TRUE; // Enable parity checking
PortDCB.fOutxCtsFlow = FALSE; // CTS output flow control
PortDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
PortDCB.fOutxDsrFlow = FALSE; // No DSR output flow control
PortDCB.fDtrControl = DTR_CONTROL_ENABLE; //DTR output ON
PortDCB.fDsrSensitivity = FALSE; // DSR sensitivity
PortDCB.fTXContinueOnXoff = TRUE; // XOFF continues Tx
PortDCB.fOutX = FALSE; // No XON/XOFF out flow control
PortDCB.fInX = FALSE; // No XON/XOFF in flow control
PortDCB.fErrorChar = FALSE; // Disable error replacement
PortDCB.fNull = FALSE; // Disable null stripping
PortDCB.fAbortOnError = FALSE; // Do not abort reads/writes on
// error
PortDCB.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2
// Configure the port according to the specifications of the DCB
// structure.
if (!SetCommState (hPort, &PortDCB))
{
MessageBox (hMainWnd, TEXT("Unable to configure the port"),
TEXT("Error"), MB_OK);
dwError = GetLastError ();
return FALSE;
}
// Retrieve the time-out parameters for all read and write operations
// on the port.
GetCommTimeouts (hPort, &CommTimeouts);
// Change the COMMTIMEOUTS structure settings.
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 0;
CommTimeouts.ReadTotalTimeoutConstant = 0;
CommTimeouts.WriteTotalTimeoutMultiplier = 20;
CommTimeouts.WriteTotalTimeoutConstant = 1000;
// Set the time-out parameters for all read and write operations
// on the port.
if (!SetCommTimeouts (hPort, &CommTimeouts))
{
// Could set the timeouts.
MessageBox (hMainWnd, TEXT("Unable to set the port time-out parameters"),
TEXT("Error"), MB_OK);
dwError = GetLastError ();
return FALSE;
}
if (wcscmp(COMMPORTNAME, TEXT("TAPI")) != 0)
{
// Direct the port to perform extended functions SETDTR and SETRTS
// SETDTR: Sends the DTR (data-terminal-ready) signal.
// SETRTS: Sends the RTS (request-to-send) signal.
// EscapeCommFunction (hPort, SETDTR);
// EscapeCommFunction (hPort, SETRTS);
//Use the Swap Comms routine to set the serial port to the last open state
//This is a little messy to set the port first of all and then change some
//of the settings here
SwapComms(CurrentController);
}
// Create a read thread for reading data from the communication port.
if (hReadThread = CreateThread (NULL, 0, PortReadThread, 0, 0,
&dwThreadID))
{
CloseHandle (hReadThread);
}
else
{
// Could not create the read thread.
MessageBox (hMainWnd, TEXT("Unable to create the read thread"),
TEXT("Error"), MB_OK);
dwError = GetLastError ();
return FALSE;
}
return TRUE;
}
//TAPI sends status messages to this function.
void CALLBACK TapiCallBackFunction(DWORD dwdevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
DWORD lTapiReturn, dwStructSize;
VARSTRING *pvs, *pvsOld;
#ifdef DEBUGVERSION
char SendString[40];
#endif
pvs = NULL;
dwStructSize = sizeof (VARSTRING);
switch(dwMsg)
{
case LINE_CALLSTATE:
switch(dwParam1)
{
case LINECALLSTATE_IDLE:
if (hPort != INVALID_HANDLE_VALUE)
{
VTPrint("Line Idle\n\r",0);
//Change MENU back to COMM mode
ControllerMenu(TRUE);
//Shutdown TAPI
hPort = INVALID_HANDLE_VALUE;
lineDeallocateCall(ghCall);
ghCall = NULL;
ghLine = NULL;
}
break;
case LINECALLSTATE_CONNECTED:
//If a handle is already present then don't re-open
//This needs doing as multiple connected events can occur on connection
if (hPort != INVALID_HANDLE_VALUE)
break;
//Spin round loop till the structure is big enough
do
{ pvsOld = pvs;
if (NULL == (pvs = realloc (pvs, dwStructSize)))
break;
pvs->dwTotalSize = dwStructSize;
if (lTapiReturn = lineGetID (NULL, 0, ghCall, LINECALLSELECT_CALL,
pvs, TEXT("comm/datamodem")))
break;
}
while ((dwStructSize = pvs->dwNeededSize) > pvs->dwTotalSize) ;
if (pvs == NULL)
{
if (pvsOld)
free (pvsOld);
VTPrint("Failed to allocate port handle memory\n\r", 0);
TAPIShutdown();
break;
}
if (lTapiReturn)
{
free(pvs);
VTPrint("Failed to obtain port handle\n\r", 0);
TAPIShutdown();
break;
}
hPort = * (HANDLE*) ((char*) pvs + pvs->dwStringOffset);
free(pvs);
//Change Menu to Modem Mode
ControllerMenu(FALSE);
VTPrint("Connected\n\r", 0);
InitiliseCommHandle();
break;
case LINECALLSTATE_DIALING:
//VTPrint("Dialling\n\r", 0);
break;
case LINECALLSTATE_PROCEEDING:
//VTPrint("Waiting for an answer\n\r", 0);
break;
case LINECALLSTATE_DISCONNECTED:
ControllerMenu(TRUE);
//Change Menu to COMM mode
switch (dwParam2)
{
case LINEDISCONNECTMODE_UNREACHABLE:
VTPrint("Unreachable\r\n",0);
hPort = INVALID_HANDLE_VALUE;
break;
case LINEDISCONNECTMODE_BUSY:
VTPrint("Line Busy\n\r", 0);
break;
}
TAPIShutdown();
}
break;
default:
break;
}
}
[/code]
BOOL TAPIShutdown()
//Do the TAPI Shutdown Process
{
static bShuttingDown = FALSE;
//If we are not initilised then Shutdown unnecessary
if (ghLineApp == NULL)
return TRUE;
//Prevent Shutdown re-entrancy problems
if (bShuttingDown)
return TRUE;
bShuttingDown=TRUE;
if (ghCall != NULL)
lineDrop(ghCall, NULL, 0);
bShuttingDown = FALSE;
return TRUE;
}
Thanks for code posted. It is very helpful.
But I don't quite understand the logic how the "hPort" is created when receiveing a "CONNECTED" message callback.
Can you explain a little bit more?
- David
The handle to the port (hPort) is contained at the end of a variable length string (VARSTRING) that is returned from the call to LineGetID.
The fiddly thing about it is you give LineGetID a pointer to a VARSTRING called pvs and you have to fill in one of the member variables of pvs with the size allocated ( pvs->dwTotalSize ) to the variable length string.
Since I don't know how much to allocate to the VARSTRING, initially I just allocate enough memory from a sizeof(VARSTRING) result. Maybe you could just allocate a set size and not bother with the looping back, I think it is better to ask the system how much memory it wants as LineGetID may need more memory in later versions of the operating system.
Then you pass the VARSTRING with it's size stored in dwTotalSize to the function LineGetID.
The LineGetID function then fills in dwNeededSize with the size of the VARSTRING it needed to complete. That is why I loop back and re-allocate the VARSTRING to a new (bigger) size if NeededSize is greater than TotalSize.
Once the LineGetID function succeeds with a big enough VARSTRING then the Port Handle is at the end of the VARSTRING, as it is a variable length string you have to do maths to say the Port Handle is at the Address of the string PLUS the offset to the actural data wanted i.e. pvs + pvs->dwStringOffset The maths must be done with char* byte sizes and the final result is a handle, hence the casting HANDLE* on the final result.
Once the Port Handle is stored in hPort, the memory allocated to pvs is freed.
One other thing I noticed is that you can get multiple CONNECTED events, that is why I just exit the CONNECTED event if there is already a valid handle
I hope that makes sense.
Cheers
Paul
Do you have sample for the answering part?
thanks,
- David
No, sorry I have only ever written programs to dial out.
Hi,
I tried the sample code posted in this thread. It works only if I used
LineCallParams.dwBearerMode = LINEBEARERMODE_VOICE;
LineCallParams.dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
combination. I got the RING message. However, the original:
LineCallParams.dwBearerMode = LINEBEARERMODE_VOICE;
LineCallParams.dwMediaMode = LINEMEDIAMODE_DATAMODEM;
didn't seem to work.
Do I miss something? (I think I have successfully killed the cprog.exe already)
Thanks,
- David
That means you are making a voice call.
So is the problem that you can not make a data(modem) type call ?
Check that you have data enabled on your SIM and also that under Settings -> Connections -> CSD Line Type it is set correctly. In the UK it is 9600 bps(v.32) and Non-tranparent, but I was told it is v110 is the states.
Let me know how you get on.
Is there any chance that somebody could compile this TAPI code as an object I could consume in C#? I have tried, but I am not even able to compile the example in C++... it's just not my world!
I would be eternally gratefull - and I bet a whole bunch of other guys who just want to quickly establish a data connection over GSM from C# (or VB?) would be also! It should have been in the phone.dll if you ask me!
compiled sample
Hey could somebody compile the tapi code for me in eVc++ or MFC ?? because i am really struggling. Plus what is toolbox.h i dont seem to have it ?? can somebody please help ??
thanks guys
This is an old post of mine, don't worry about Toolbox.h that was for the program I was writing and it has nothing to do with Tapi.
This sample code was not meant to compile, after you you won't have function VTPrint but I hope you can guess that just shows text. It was just posted to demonstate the order of functions needed to get a modem connection.
I built all this Tapi code into a DLL and I have it all presented with the eVC 3.0 build files in this post, this code will compile as presented and is just a zip of all the project files of a single sample application.
http://forum.xda-developers.com/viewtopic.php?t=18978
Have a look there.
Cheers
Paul
during a data call when using TAPI can u make some kind of AT command request ??? and retrieve the data from that answer ??? And do i need some TAPI app on the remote unit to send the data back ?
i am trying to access a remote unit ( GPS RX and GSM TX )
I don't really understand your question that well.
But AT commands have nothing to do with TAPI, so any question saying can TAPI send an AT command for etc -- The answer must be no.
All TAPI does is gives a Handle that can be used with ReadFile and WriteFile for receiving and sending data over the modem.
So TAPI will dial a number and establish a DataModem connection, then on Connect it can provide the handle for you to direct read/write requests to. Once the link is established you just treat the handle as if it was a file handle returned from a CreateFile command.
As said earlier all this has nothing to do with AT commands.
During a data call should ic onnect via COM 1 or COM 9 if i'm using TAPI ?? Would you have any sample code i could have a look at ? I'm a bit lost at the moment .... with LineGetID too ...
you can't use COM2 or COM9 via TAPI cause COM2 and COM9 masked by RIL
about lineGetID look http://forum.xda-developers.com/viewtopic.php?t=9761
ok thanks - so if i cant use COM1 or COM9 in this case then i'm guessing that i shall pass a string (lpszPortName) just like the example on this post.
Code:
// Open the serial port.
hPort = CreateFile (lpszPortName, // Pointer to the name of the port
GENERIC_READ | GENERIC_WRITE,
// Access (read-write) mode
0, // Share mode
NULL, // Pointer to the security attribute
OPEN_EXISTING,// How to open the serial port
0, // Port attributes
NULL); // Handle to port with attribute
// to copy
But what should the string lpszPortName be initialized to ? Or am i totally wrong ?[/quote]
L"COM9:" for COM9
but not with TAPI
with TAPI you have not open any COM-ports
you must use lineGetID
Code:
//PART 7 - TIMEOUT AND DCB SETTINGS
PortDCB.BaudRate = 115200;
PortDCB.fBinary = TRUE;
PortDCB.fParity = FALSE;
PortDCB.fOutxCtsFlow = FALSE;
PortDCB.fOutxDsrFlow = FALSE;
PortDCB.fDtrControl = DTR_CONTROL_ENABLE;
PortDCB.fDsrSensitivity = FALSE;
PortDCB.fTXContinueOnXoff = FALSE;
PortDCB.fOutX = FALSE;
PortDCB.fInX = FALSE;
PortDCB.fErrorChar = FALSE;
PortDCB.fNull = FALSE;
PortDCB.fRtsControl = RTS_CONTROL_DISABLE;
PortDCB.fAbortOnError = FALSE;
PortDCB.ByteSize = 8;
PortDCB.Parity = NOPARITY;
PortDCB.StopBits = ONESTOPBIT;
if (!SetCommState (hPort, &PortDCB))
{
MessageBox (_T("unable to configure com port "));
return FALSE;
}
GetCommTimeouts (hPort, &CommTimeouts);
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 0;
CommTimeouts.ReadTotalTimeoutConstant = 0;
CommTimeouts.WriteTotalTimeoutMultiplier = 20;
CommTimeouts.WriteTotalTimeoutConstant = 1000;
if (!SetCommTimeouts (hPort, &CommTimeouts))
{
MessageBox (_T("unable to set comport parameters"));
return FALSE;
}
Are the COM and Timeout settings necessary when using TAPI ? because i am getting an error message at the !setCommState function.
I use lineGetID to retrieve the Handle to the Comm then i try to use readFile but the operation is unsuccesful.
Anyhelp from out there ? thanks.
Are you sure that you call lineGetID only after connection?
Maybe you are right. I just do lineMakeCall then do lineGetId without waiting for a LINECALLSTATE_CONNECTED message.
I think its because i have troubles implementing the lineCallBackFunc() i dont understand the parameters that need to be passed to it.
Code:
void CALLBACK lineCallbackFunc(
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
Anyhelp for this ?? Cheers.
As IE, MSN, when it launches, it creates a GPRS connection to Internet automaticly.
How can I do such thing in my own program in eVC 4.0 ?
Thanks.
All right.
No one answers my question, so I do it by myself. :evil:
Code:
#include <windows.h>
#include <winsock.h>
#if (WIN32_PLATFORM_PSPC>300 || WIN32_PLATFORM_WFSP )
#include <objbase.h>
#include <initguid.h>
#include <connmgr.h>
typedef HRESULT(*CONNMGRCONNECTIONSTATUS) (HANDLE hConnection,
DWORD * pdwStatus);
typedef HRESULT(*CONNMGRRELEASECONNECTION) (HANDLE hConnection,
LONG lCache);
typedef HRESULT(*CONNMGRESTABLISHCONNECTION) (CONNMGR_CONNECTIONINFO *
pConnInfo,
HANDLE * phConnection,
DWORD dwTimeout,
DWORD * pdwStatus);
#endif
HANDLE phWebConnection;
BOOL EstablishDatacall(TCHAR * IPout);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// TODO: Place code here.
TCHAR MyIP[20];
if (EstablishDatacall(MyIP) == TRUE) {
TCHAR Message[255];
wsprintf(Message, L"IP: %s", MyIP);
MessageBox(NULL, Message, L"Success", MB_OK);
} else {
MessageBox(NULL,
L"Data call could not be established or has been established",
L"Information", MB_OK);
}
return 0;
}
BOOL EstablishDatacall(TCHAR * IPout)
{
CHAR szHostname[255];
TCHAR IP[17];
HOSTENT *pHostEnt = NULL;
int nAdapter = 0;
IN_ADDR *tsim = NULL;
BOOL tried2Connect = FALSE;
IP[0] = 0; // Clear the IP Address
if (IPout != NULL) {
IPout[0] = 0;
}
tryagain:
nAdapter = 0;
gethostname(szHostname, sizeof(szHostname));
pHostEnt = gethostbyname(szHostname);
while (pHostEnt != NULL && pHostEnt->h_addr_list[nAdapter]) {
// in case a device has multiple ethernet cards
// i.e. 802.11, Bluetooth, USB-Cradle
// we need to go though all pHostEnt->h_addr_list[nAdapter]
tsim = (IN_ADDR *) pHostEnt->h_addr_list[nAdapter];
if (IPout != NULL) {
wsprintf(IPout, TEXT("%d.%d.%d.%d"),
tsim->S_un.S_un_b.s_b1,
tsim->S_un.S_un_b.s_b2,
tsim->S_un.S_un_b.s_b3, tsim->S_un.S_un_b.s_b4);
}
if (tsim->S_un.S_un_b.s_b1 == 192 ||
tsim->S_un.S_un_b.s_b1 == 169 ||
tsim->S_un.S_un_b.s_b1 == 127 || tsim->S_un.S_un_b.s_b1 == 255) {
// If you want to make sure you have a real Internet
// connection you cannot bet on IpAddresses starting with
// 127 and 255. 192 and 169 are local IP addresses and
// might be routed or proxied
nAdapter++;
} else {
wsprintf(IP, TEXT("%d.%d.%d.%d"),
tsim->S_un.S_un_b.s_b1,
tsim->S_un.S_un_b.s_b2,
tsim->S_un.S_un_b.s_b3, tsim->S_un.S_un_b.s_b4);
break;
}
}
// the next lines only work with Pocket PC Phone
// and Smartphone
#if (WIN32_PLATFORM_PSPC>300 || WIN32_PLATFORM_WFSP )
// Pocket PC Phone Edition has set WIN32_PLATFORM_PSPC to 310
if (IP[0] == 0 && tried2Connect == FALSE) {
CONNMGRCONNECTIONSTATUS g_hConnMgrConnectionStatus = NULL;
CONNMGRESTABLISHCONNECTION g_hConnMgrEstablishConnectionSync =
NULL;
// It is good practice to load the cellcore.dll
// dynamically to be able to compile the code even for
// older platforms
HINSTANCE hcellDll = LoadLibrary(TEXT("cellcore.dll"));
if (hcellDll) {
// We need the Status and a call to establish the
// connection
g_hConnMgrConnectionStatus =
(CONNMGRCONNECTIONSTATUS) GetProcAddress(hcellDll,
TEXT
("ConnMgrConnectionStatus"));
// The next line is just for debugging. You will have
// to decide what you want to do if this call fails
DWORD a = GetLastError();
g_hConnMgrEstablishConnectionSync =
(CONNMGRESTABLISHCONNECTION) GetProcAddress(hcellDll,
TEXT
("ConnMgrEstablishConnectionSync"));
//a = GetLastError();
// Here comes the main code:
// First we check if we might have a connection
DWORD pdwStatus;
(*g_hConnMgrConnectionStatus) (&phWebConnection, &pdwStatus);
if (pdwStatus == CONNMGR_STATUS_CONNECTED) {
//We are already connected!
//This code should never run since we should
//have a valid IP already.
//If you still get here, you probably have
//stale connection.
return TRUE;
} else {
//We are not connected, so lets try:
//The CONNECTIONINFO is the structure that
//tells Connection Manager how we want
//to connect
CONNMGR_CONNECTIONINFO sConInfo;
memset(&sConInfo, 0, sizeof(CONNMGR_CONNECTIONINFO));
sConInfo.cbSize = sizeof(CONNMGR_CONNECTIONINFO);
// We want to use the guidDestNet parameter
sConInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
// This is the highest data priority.
sConInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
sConInfo.dwFlags = 0;
// Lets be nice and share the connection with
// other applications
sConInfo.bExclusive = FALSE;
sConInfo.bDisabled = FALSE;
sConInfo.guidDestNet = IID_DestNetInternet;
// We want to wait until the connection was
// established successful but not longer then
// 60 seconds. You can use
// ConnMgrEstablishConnection to establish
// an asynchronous connection.
if ((*g_hConnMgrEstablishConnectionSync)
(&sConInfo, &phWebConnection, 60000,
&pdwStatus) == S_OK) {
//We are successfully connected!
//Now lets try to get the new IP address
tried2Connect = TRUE;
goto tryagain;
} else {
tried2Connect = FALSE;
//Doh! Connection failed!
}
}
}
}
#endif
return tried2Connect;
}
Everyone in this group has been extremely helpful, I would not be as far as I am without you, however, I still have a problem.
I am trying to control the control panel applet ctlpnl.exe ( menu 17, 1 the WLAN menau ) like so:
BOOL CSTControlPanel::Open( PROCESS_INFORMATION & pi )
{
CString strParam;
if (m_bTabSubpage)
{
strParam.Format(_T("cplmain.cpl,%d,%d"),
(int)m_dwControlPanelApplet, (int)m_dwAppletTab);
}
else
{
strParam.Format(_T("cplmain.cpl,%d"), (int)m_dwControlPanelApplet);
}
if (!::CreateProcess(_T("\\Windows\\ctlpnl.exe"), strParam, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi))
{
return FALSE;
}
return TRUE;
}
This opens the control panel, and I can send it keypresss successfully with this unsightly hack:
keybd_event( VK_DOWN, 0, KEYEVENTF_KEYDOWN, 0 );
Sleep(...)
keybd_event( VK_DOWN, 0, KEYEVENTF_KEYUP, 0 );
I want to send it a keypress message with PostMessage() or SendMessage() or something, but nothing works, I assume it is because I cannot find the window handle correctly or something. I have tried everything. First off, I am trying to send it the following messages:
SendMessage( hwnd, WM_KEYDOWN, VK_DOWN, 0 );
SendMessage( hwnd, WM_KEYUP, VK_DOWN, 0 );
SendMessage( hwnd, WM_KEYDOWN, 112, 0 );
SendMessage( hwnd, WM_KEYUP, 112, 0 );
and I also tried
PostMessage( hwnd, WM_KEYDOWN, VK_DOWN, 0 );
PostMessage( hwnd, WM_KEYUP, VK_DOWN, 0 );
PostMessage( hwnd, WM_KEYDOWN, 112, 0 );
PostMessage( hwnd, WM_KEYUP, 112, 0 );
I tiried the following approaches to no avail:
#1 - EnumWindows(), matching the process id to the one I created:
in the enumeration callback function:
GetWindowThreadProcessId( hwnd, &pid );
if ( pid == pi->dwProcessId )
{
global_window_handle = hwnd;
}
This handle didn't work.
I also tried FindWindow( NULL, _T("Settings") ) and this didn't work.
Also I tried GetWindowText( ) in the enumerator for EnumWindows() and there are about 6 windows called "Settings" ( the title ), and none seem to work...
I tried to get the current focus and all sorts of other things. What I am doing wrong here.... I know the EnumWindows() thing gives me the HWND of the whole applet at least 'cause if I send it a WM_CLOSE it does indeed close.
HELP
To send keystrokes via SendMessage you need the handle of the exact child window to receive them ie: edit, button etc.
Since WM does not support FindWindowEx and FindWindow does not search child windows, it is fairly difficult to find.
I know there is some way to enumerate child windows, but I do not remember how.
Use Remote Spy to find dialog title.
I have czech version of applet ...
"dirty" code without error checking and array bounds checking (use GetWindowsTextLength to accurately allocate title buffer)
void findWindows()
{
HWND hWLAN = NULL;
hWLAN = ::FindWindow(NULL, L"Wireless LAN"); //Try this or find dialog title using remote spy
if (hWLAN )
{
HWND hCHild = ::GetWindow(hWLAN, GW_CHILD);//Tab Main
hCHild = ::GetWindow(hCHild, GW_CHILD); //Controls on tab main
if (hCHild)
{
while(hCHild != NULL)
{
WCHAR title[1000];
//SSID, Mode, BSSID etc.
GetWindowText(hCHild, title, sizeof(title));
hCHild = ::GetWindow(hCHild, GW_HWNDNEXT);
}
}
}
}
closer and closer
Thanks again RStien, your advice is right on, once again, but I still don't have everything. I don't know how you know this stuff, but I want to go home and program in the kernel where I just get a friendly blue screen...
Anyway, with your advice, I successfully found the correct window handle for the "Configure Wireless Networks" list box, and successfully send the "down arrow ( VK_DOWN )" keypress anyway, using the following:
HWND wh = FindWindow( _T("Dialog"), _T("Settings") );
HWND cw = GetWindow( wh, GW_CHILD );
for ( cw = GetWindow( cw, GW_CHILD ); cw; cw = GetWindow( cw, GW_HWNDNEXT ) )
{
GetWindowText( cw, title, 256 );
GetClassName( cw, clname, 256 );
if ( lstrcmp( clname, _T("SysListView32") ) == 0 )
{
send_down_and_f1( cw );
}
}
Unfortunately, the "f1" part of "send_down_and_f1" does not work. I assume this is because I need to send f1 to a different handle than for the "VK_DOWN", I assume this handle is the menu at the bottom, but I have no idea what it would be called. When I found the "Dialog", "Settings" window, I used "Remote Spy" and I used "find window" and it magically popped up the correct window, but no such luck locating whatever I need to send the F1 press to for the "Connect". I am so close and yet so far here...
What should I try next, is there some way to use Remote Spy to find where the f1 keypress needs to go?
Some API for soft keys not working as expected, but here is the solulution for your problem.
Post message to cw dialog.
PostMessage(hWLAN, WM_COMMAND, 0x00003013, 0x0); //hWLAN is probably Your cw handle.
Well it almost works. 0x3013 is the "enter command" but 0x3012 seems to be the f1 command, however, it does not work the same way as pressing the F1 key.
so, if I first connect my access point, then send the 0x3013 message:
HWND wh = FindWindow( _T("Dialog"), _T("Settings") );
PostMessage(wh, WM_COMMAND, 0x00003012, 0);
I get the "connected" state to turn to "connecting", as I expect, but I do not get a connect. If I send an F1 key via the hack:
keybd_event( VK_DOWN, 0, KEYEVENTF_KEYDOWN, 0 );
Sleep(...)
keybd_event( VK_DOWN, 0, KEYEVENTF_KEYUP, 0 );
Sleep(...)
Then the state goes to "connecting" then eventually "connect"
What is the difference? The other thing I notice is that after sending 0x3012, if I press the "connect" menu with the stylus, instead of connecting, it brings up the "access point properties" dialog you usually get by pressing "enter". I don't get it...
Hello skk,
with small iterations I hope we are near the final target Let me know...
For me following code works perfectly. WLAN is connected, configuration dialog is not displayed. You must send menu bar HWND as LPARAM and post 0x00003011 as WPARAM.
HWND hwndMenuBar = SHFindMenuBar(hwndSettings); //probably wh form Your code
if (hwndMenuBar)
{
PostMessage(hWLAN, WM_COMMAND, 0x00003011, (LPARAM)hwndMenuBar); //hWlan is child of WH - not WH itself - strange, but works!!!!
}
Wow it works! Here is the code fragment for those of you like myself that have trouble keeping the window handles straight
HWND wh1 = NULL;
HWND wh2 = NULL;
HWND wh3 = NULL;
HWND wh4 = NULL;
wh1 = FindWindow( _T("Dialog"), _T("Settings") );
wh2 = GetWindow( wh1, GW_CHILD );
for ( wh3 = GetWindow( wh3, GW_CHILD ); wh3; wh3 = GetWindow( wh3, GW_HWNDNEXT ) )
{
GetClassName( cw, clname, 256 );
if ( lstrcmp( clname, _T("SysListView32") ) == 0 )
{
wh4 = SHFindMenuBar( wh );
send_f1( wh2, wh4 );
}
}
Where send_f1 is:
void send_f1( HWND wh, HWND ow )
{
PostMessage(wh, WM_COMMAND, 0x00003011, (LPARAM)ow);
}
and send_down is:
void send_down( HWND wh )
{
PostMessage( wh, WM_KEYDOWN, VK_DOWN, 0 );
PostMessage( wh, WM_KEYUP, VK_DOWN, 0 );
}
Thanks again RStein