TAPI data calls. - Windows Mobile Development and Hacking General

1) Hi, we works with Pocket PC 2003 (Embedded Visual C++ 4.0). Our target is perform a data call over integrated cellular line. We read that the right mean is TAPI (XDA-Developers forum) because COM2 seem to be owned by RIL layer. We wrote a test program which handles a data call answer. We get the serial port handle returned by "lineGetID()" function for current data call, but when we try to use it through standard communication APIs ("WriteFile()" and "ReadFile()") it doesn't work (no data is read or write from port handle, but there aren't errors returned by APIs). Same code seems to work fine on a HP IPAQ 2210 with a Compact Flash GSM/GPRS card. How can we transfer data so?
2) We noticed a phone answering process is alive (CPROG.EXE) that takes calls' control. We kill this process when we make our tests, but it backs to life after some minutes. How can we take real calls' control?
3) Is the integrated cellular modem Hayes compatible? Is there a way to directly send AT commands to integrated cellular modem?
4) DCB structure "PortDCB" we pass to "SetCommState()" is the following:
PortDCB.DCBlength = sizeof (DCB);
PortDCB.BaudRate = 9600;
PortDCB.fBinary = TRUE;
PortDCB.fParity = TRUE;
PortDCB.fOutxCtsFlow = FALSE;
PortDCB.fOutxDsrFlow = FALSE;
PortDCB.fDtrControl = DTR_CONTROL_ENABLE;
PortDCB.fDsrSensitivity = FALSE;
PortDCB.fTXContinueOnXoff = TRUE;
PortDCB.fOutX = FALSE;
PortDCB.fInX = FALSE;
PortDCB.fErrorChar = FALSE;
PortDCB.fNull = FALSE;
PortDCB.fRtsControl = RTS_CONTROL_ENABLE;
PortDCB.fAbortOnError = FALSE;
PortDCB.ByteSize = 8;
PortDCB.Parity = NOPARITY;
PortDCB.StopBits = ONESTOPBIT;
Is it correct?
Can anybody help me?
Matthew

This DCB params seem do right job....
Code:
PortDCB.BaudRate = 115200;
PortDCB.fBinary = TRUE;
PortDCB.fParity = FALSE;
//PortDCB.fParity = TRUE;
PortDCB.fOutxCtsFlow = FALSE;
PortDCB.fOutxDsrFlow = FALSE;
PortDCB.fDtrControl = DTR_CONTROL_ENABLE;
PortDCB.fDsrSensitivity = FALSE;
//PortDCB.fDsrSensitivity = TRUE;
PortDCB.fTXContinueOnXoff = TRUE;
//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;
problem one shuold be solved ...Hi

Your DCB parameters worked fine.
Hi Matthew.
Your parameters worked fine with my application, i was teting an IMate and you helped me a lot with my next product version.
Thank you.
Cesar Bremer Pinheiro
Bremer Serv. Emp. Ltda.
Raseac Division.
http://www.raseac.com.br

Dear Matthew,
Hi,
I created a similar application to make a call from one PPC to another using data link. The problem is my program couldn't detect incoming calls. Would you please help me solve this issue?
Do you have any source code that I can learn from it? Also which method did you use? TAPI? COM port? RIL?
I'm very confused. Please help....
Best regards,
A. Riazi

Why is your baudrate 115200 when a CSD connection through TAPI is only at 9600 ?

cause it's TAPI
you can't select bearer capabilities
if you wana select rate use RIL or direct access to COM-ports

mathews help!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
hi,
i am facing a similar problem cant send data through writefile api on cellular line
these r code snippets form my cod e
if(dwReturn =lineOpen (
g_hLineApp, // Usage handle for TAPI
g_dwCurrentLineID, // Cannot use the LINEMAPPER value
&g_CurrentLineInfo.hLine, // Line handle
g_CurrentLineInfo.dwAPIVersion,
// API version number
0, // Must set to zero for Windows CE
0, // No data passed back
LINECALLPRIVILEGE_NONE, // Can only make an outgoing call
0, // Media mode
NULL))
g_MakeCallRequestID = lineMakeCall (g_CurrentLineInfo.hLine,
&g_hCall,
szDialablePhoneNum,
0,
NULL);
dwRet =lineGetID(g_CurrentLineInfo.hLine, 0, 0, LINECALLSELECT_LINE, lpVarString,TEXT("comm/datamodem"));
till this its ok it returns a valid comm handle
after that it sends th data over the connected call but doesnt receive anythinga t the other end ...
the problem might be to give a call handle and LINECALLSELECT_CALL in linegteid func tion i tried it but when i use it fails saying cannot obtain the handle .......now i don understand whether its the problem of not obtaining a handle or whether linehandle will work but problem is in communication ...
plz help and add some code snippets for the communication

firstly have you set up the following...
LPLINECALLPARAMS CallParams;
CallParams=(LPLINECALLPARAMS)malloc(sizeof(LINECALLPARAMS)+1024);
memset(CallParams,0,sizeof(LINECALLPARAMS)+1024);
CallParams->dwTotalSize = sizeof(LINECALLPARAMS)+1024;
// This is where we configure the line for DATAMODEM usage.
//its important to note that if you attempt to make a call
//using LINEMEDIAMODE_DATAMODEM, the line must be opened in
//that way to begin with or nothing will happen. ie open lines
//corresponding to what you plan to makecall with.
CallParams->dwBearerMode = LINEBEARERMODE_VOICE;//over voice call
CallParams->dwMediaMode = LINEMEDIAMODE_DATAMODEM;//data transmition
//LINEMEDIAMODE_INTERACTIVEVOICE;//for voice
//specify that we only want to use a call that is not alreay in use.
//Otherwise it can take over calls that are in progress
CallParams->dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
//specify to use the first address
CallParams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;
CallParams->dwAddressID = 0;
next check the line you want to use and make sure...
if(lpDevCaps->dwBearerModes & callModeData)//datamode
{
if(lpDevCaps->dwBearerModes & callModeVoice)
{
//then check the media mode
if(lpDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM)
{
if(lpDevCaps->dwMediaModes & LINEMEDIAMODE_INTERACTIVEVOICE)
{
//use it
}
}
}
}
next when you get an incoming call etc get the handle......
HANDLE myTapiManager::getHandle()
{
HANDLE hModem=NULL;
CString name="";
classType="comm/datamodem";
DWORD dwSize = sizeof(VARSTRING) + 2048;
DWORD dwRet = 0;
do
{
LPVARSTRING lpVarString = (LPVARSTRING) new BYTE[dwSize];
lpVarString->dwTotalSize = dwSize;
//the commented out values are what microsoft seem to say
//but cause it to return no handle but only the device class name
dwRet = ::lineGetID(/*hOpenedLine*/NULL, 0,hCall,LINECALLSELECT_CALL /*LINECALLSELECT_LINE*/, lpVarString,
classType);
if ( dwRet == 0 )
{
hModem= * (HANDLE*) ((char*) lpVarString + lpVarString->dwStringOffset);
name= * (LPTSTR) ((char*) lpVarString + lpVarString->dwStringOffset + sizeof(HANDLE));
if(hModem==NULL)
{
MessageBox(NULL,_T("null handle"),_T("handle"),MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
}
else
{
MessageBox(NULL,_T("non null handle, about to call init on port"),_T("handle"),MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
hSerialHandle=hModem;
//you must initialize the port but I do not show it here
initializeIOTimeOuts();
}
break;
}
else if ( dwRet == LINEERR_STRUCTURETOOSMALL )
{
dwSize = lpVarString->dwNeededSize;
delete lpVarString;
lpVarString = NULL;
continue;
}
else
{
// handle errors.........
//
hModem=NULL;
}
}
while (1);
return hModem;
}
this works for me. One problem you may be having is the timing. If you get the handle when the call is offering it will be useless. You must ensure that the call is connected, use the LINECALLSTATE_CONNECTED for this.
cprog dose not effect the opperation of your program, only its ui.

Have you looked at the zip I put in this post ?
http://forum.xda-developers.com/viewtopic.php?t=18978
It may help you with the sending part and getting a handle to Readfile/writefile.
I have never worked on the anwering side so I know nothing on that.

to answer the call that is offering, use LineAnswer. I have tried LinePickup but it has not done what I want. When using lineanswer be aware that the os has already set the number of rings it will ring before the call is actually answered after you answer it. It is normally changed through the phone settings but you can do it programatically if you wish.
Also, you have not mentioned what you are doing with regards to threads. The first time I tried using tapi I spent a lot of time avoiding using multiple threads but it is impracticle. A good number would be 1 for the ui 1 for the line status and 1 for data transfer. Getting your serial handle would the require carefull synchronization.
I have not found benefit in altering any of the port settings. The time-outs for the read/write can make a huge difference though.
Have you checked if there is a class name following the handle in the LPVARSTRING? I have found that if you are doing everything correct except the timing, the name is added but not the handle. This means that the name would be 4 places sooner than it should. In that case the name is in the place where the handle should be and the call still succeeded. Try writing to the port using a string instead of a handle and see how far you get(kidding). To test it just treat the handle you have got as if it where a string and put it in a messagebox. If you can read it then your timing is off.
Lastly your problem could come from how you are setting the api version. Have you been checking the errors generated after all your api calls. There are a lot for tapi that tell you most of what is going on. I have noticed that if you are using the event method for getting line state messages from tapi (ie the correct time to grab the handle) there is something wrong with the way the api version gets negotiated. The event method is part of tapi 2.0 i think so it should be expected that an api version of at least 2.0 is a good version to aim for. For win ce 3.0 docs say that 2.0 is fully supported and parts of 2.1. This is not what actually happens though, I have found that I can't get the event method to work at all (among other things) if I don't pass a min version of 1.1 and a max version of 1.3. This is bizar because by that input tapi should not use any features above 1.3 :?:

guys i ve been trying to set up call params
//Configure line device for a data modem
memset(&LineCallParams, 0, sizeof(LineCallParams));
LineCallParams.dwTotalSize = sizeof(LineCallParams);
LineCallParams.dwBearerMode = LINEBEARERMODE_VOICE;
LineCallParams.dwMediaMode = LINEMEDIAMODE_DATAMODEM;
LineCallParams.dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
//If multiple addresses on the line, use the first address
LineCallParams.dwAddressMode = LINEADDRESSMODE_ADDRESSID;
LineCallParams.dwAddressID = 0;
the problem is when i pass the lpparam instead of null it reaches to dialing the number and says disconnected: unreachable ..
There is no error while lineopen is passed with LINEMEDIAMODE_DATAMODEM as the 8th parameter i think it shoudl have given an error of media mode not supported at that point only ....but it didnt .....it opened the line fine
when i did not set the datamodem media mode in call params and rest remain as it is ..........the call works fine and is connected ....is it because the datacall facility is not supported and if it is not how can it be activated ....
lReturn = lineOpen(m_hLineApp, m_dwDeviceID, &m_hLine, m_dwAPIVersion, 0 , 0,
LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM, 0);
g_MakeCallRequestID = lineMakeCall (g_CurrentLineInfo.hLine,
&g_hCall,
szDialablePhoneNum,
0,
NULL); // NULL for default voice call
/**************************/
// lpCallParams);

Firstly, is the phone you are trying to dial a ppc. If yes... the ppc you wish to be the one that answers the call must have opened the line the same way as the dialing one, ie supporting data. If not then it will not even ring when a data call is offering and you will never know if your call was really made.
next look at lineopen.
long opnResult=lineOpen(hLineApp,
deviceID,
&hOpenedLine,
apiNegotiatedForUsedDevice,
0,//not used at all
1,//not used by tapi, but is passed back to this //application to help identify the source of the messages.
//in each line state message. It can be used
//to make sure the message is from a line
//opened by this app.
LINECALLPRIVILEGE_OWNER,// tells it can //accept calls.
LINEMEDIAMODE_DATAMODEM,
NULL);
almost the same but try LINECALLPRIVILEGE_OWNER. This must be set on the receiving end but I also set it on the dialing end.
If the media mode is not supported you will get an error message to tell so. That is the primary way my programs determine the identity of the line to use.
What is the device you are using? I have got bi-directional data transfer to work between ppc 2002 (xda I) and wm2003 se (xda mini). Before 2002 not as many features are supported.

also your call params may be too small
try adding a litle to the end
LPLINECALLPARAMS CallParams;
CallParams=(LPLINECALLPARAMS)malloc(sizeof(LINECALLPARAMS)+1024);
memset(CallParams,0,sizeof(LINECALLPARAMS)+1024);
CallParams->dwTotalSize = sizeof(LINECALLPARAMS)+1024;
.
.
.
.
and what about the number to dial in the call params
// Address to dial
//set its location to be after the normal end of the structure
CallParams->dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
CallParams->dwDisplayableAddressSize = strlen(szPhNumber);
strcpy((LPSTR)CallParams+sizeof(LINECALLPARAMS), szPhNumber));
then pass the szPhNumber into linemakecall, remember that ppc version
takes unicode.

i am trying to pass data from pocket pc to my pc
this is whole part
LPLINETRANSLATEOUTPUT lpTransOutput = NULL;
// Call translate address before dialing.
do
{
// Allocate memory for lpTransOutput.
if (!(lpTransOutput = (LPLINETRANSLATEOUTPUT) LocalAlloc (
LPTR,
dwSizeOfTransOut)))
{
ErrorBox(TEXT("translate fails "));
goto exit;
}
lpTransOutput->dwTotalSize = dwSizeOfTransOut;
if (dwReturn = lineTranslateAddress (
g_hLineApp, // Usage handle for TAPI
g_dwCurrentLineID, // Line device identifier
g_CurrentLineInfo.dwAPIVersion,
// Highest TAPI version supported
lpszPhoneNum, // Address to be translated
0, // Must be 0 for Windows CE
0, // No associated operations
lpTransOutput)) // Result of the address translation
{
ErrorBox(TEXT("lineTranslateAddress fails "));
goto exit;
}
if (lpTransOutput->dwNeededSize <= lpTransOutput->dwTotalSize)
break;
else
{
dwSizeOfTransOut = lpTransOutput->dwNeededSize;
LocalFree (lpTransOutput);
lpTransOutput = NULL;
}
} while (TRUE);
dwSizeOfCallParams += lpTransOutput->dwDisplayableStringSize;
if (!(lpCallParams = (LPLINECALLPARAMS) LocalAlloc (
LPTR,
dwSizeOfCallParams)))
{
ErrorBox(TEXT("lineTranslateAddress open fails "));
goto exit;
}
ZeroMemory(lpCallParams, dwSizeOfCallParams);
// Set the call parameters.
lpCallParams->dwTotalSize = dwSizeOfCallParams;
lpCallParams->dwBearerMode = LINEBEARERMODE_VOICE;
lpCallParams->dwMediaMode = LINEMEDIAMODE_DATAMODEM ;
lpCallParams->dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
lpCallParams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;
lpCallParams->dwAddressID = g_dwCurrentLineAddr;
lpCallParams->dwDisplayableAddressSize =
lpTransOutput->dwDisplayableStringSize;
lpCallParams->dwDisplayableAddressOffset = sizeof (LINECALLPARAMS);
// Save the translated phone number for dialing.
lstrcpy (szDialablePhoneNum,
(LPTSTR) ((LPBYTE) lpTransOutput +
lpTransOutput->dwDialableStringOffset));
memcpy((LPBYTE) lpCallParams + lpCallParams->dwDisplayableAddressOffset,
(LPBYTE) lpTransOutput + lpTransOutput->dwDisplayableStringOffset,
lpTransOutput->dwDisplayableStringSize);
// Make the phone call. lpCallParams should be NULL if the default
// call setup parameters are requested.
g_MakeCallRequestID = lineMakeCall (g_CurrentLineInfo.hLine,
&g_hCall,
szDialablePhoneNum,
0,
NULL); // NULL for default voice call
/**************************/
// lpCallParams);
plz suggest if i need to change anything but as the call is successfully connected when only media mode is not set i guess problem is not with the memory location or anything but lies with the provider not sure ...............

or better maybe if u can suggest how i can do settings on my pocket pc for data calls

If you can't make a Data Modem type call, but you can make a voice call - Then are you sure you have Data Enabled on your Sim.
I'm sure I had that un-reachable error ages ago and it was due to not having Data Enabled. You have to contact your Sim provider to do that.

I have never included the lineTranslateAddress, i just put it in the way the user entered it( with a little error checking). If it works for voice though it must be OK.
With the sim, I have done this using 3 different sims and never had to set anything special on it. I could be lucky I guess and had sims already enabled. I doubt it though because one is 7 years old pre paid and not had any change since purchase. The second in a new pre paid and the 3rd is a full gprs enabled(not that gprs has anything to do with this). There should not be anything in the os to set either as when you do what you have done it is doing the "setup" just programatically.
I remember reading somewhere that the network had to support it but I can't be more specific.
What is your network? What is your device? what is your os?
When you say the voice call is connected what do you mean?..
1)your program at the other end answered.
2)the phone app on the other end answered.
3)your dialing end app received the connected message.
4)the returned value from linmakecall was > (-1).
5)some phone hardware on a real phone answered.
6) (my favourite way) the monitor next to your phone started buzzing, proving something was going on. Thats how I test my gprs connectivity :lol:

Related

GPRS troubles with lineOpen on Magician

For MortRing, I use the common code to enable a line callback:
Code:
if ( lineInitialize( &LineApp, theApp.m_hInstance, LineCallback, theApp.m_pszAppName, &LineHandleCount ) == 0 )
{
LineHandles = new HLINE[LineHandleCount];
for(DWORD i = 0; i < LineHandleCount; i++)
{
if ( lineNegotiateAPIVersion( LineApp, i, 0x00010000, 0x00020000, &ver, &extensionID ) == 0 )
{
rc = lineOpen( LineApp, i, &LineHandles[i], ver, 0, (DWORD)this, LINECALLPRIVILEGE_MONITOR|LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_INTERACTIVEVOICE, NULL);
if ( rc < 0 )
LineHandles[i] = NULL;
}
}
}
Strangely, this leads to troubles with the Magician: You can connect and disconnect a GPRS connection once, but the second try will be aborted ("Connection cannot be established" or s.th. like that...).
There's nothing wrong in the callback function - I even tried it with an empty dummy, and the problem still remained.
Once the line handle were freed (i.e. program closed + restarted) everything works fine again - for the next GPRS connection...
So, any idea what's going wrong? Anybody with the same problem?
Some more information...
Closing and re-opening the line handles (and lineShutdown/Initialize - don't know if it's necessary) between the connections seems to help.
Also, I traced the callbacks, and found that the only difference between the working and the next (failing) connection is an addional LINECALLSTATE_PROCEEDING (between "dialing" and "connected") for the failing connection.
Could you please send this to the iMATE dev team since they have the same problems with their own ROMs ! ;o)

Catching an incoming Phone Call

Hi there
I'm writing an PocketPC Application which also uses the Phone functions.
But how could I catch an incoming call with number?
I just need to get an event to get the number of the incoming call, to make something like an log entry
Can anyone give me some hints?
lineGetCallInfo(....)
and event LINE_CALLINFO
Thanks for your reply
And how can I catch this event in C# ?
1)init TAPI (search on this forum)
2)in cycle call lineGetMessage
in struct LINEMESSAGE check dwMessageId
3)if dwMessageId == LINE_APPNEWCALL - incoming call
4)if dwMessageId == LINE_CALLINFO - call info changed
call lineGetCallInfo
in struct LINECALLINFO check dwCallerIDFlags
5)if you work with different media modes (voice, datamodem, fax and etc) check dwMediaMode in LINECALLINFO structure
using this, how would i know if the phone is on a call already? would i be concerned with dwMessageId also here and what constant would i be comparing to?
thanks.
Thanks for your help
But because I'm new to the .NET stuff , do you have some sample code on how to implement this ?
source codes from vangelderp:
http://forum.xda-developers.com/download.php?id=3598
about geting caller number:
Code:
LPLINECALLINFO lpCallInfo = NULL;
unsigned size = sizeof(LINECALLINFO);
LPLINECALLINFO tmp;
/*lpCallInfo place right size of struct to dwNeededSize*/
while (tmp = (LPLINECALLINFO)realloc(lpCallInfo, size))
{
lpCallInfo = tmp;
lpCallInfo->dwTotalSize = size;
if (lineGetCallInfo(hCall, lpCallInfo))
{
free(lpCallInfo);
return;
}
if(lpCallInfo->dwNeededSize <= size)
break;
size=a->dwNeededSize;
}
if (!tmp)
{
free(lpCallInfo);
return;
}
/*dwCallerIDOffset is offset in bytes from top of struct*/
if (lpCallInfo->dwCallerIDFlags & LINECALLPARTYID_ADDRESS)
_tcsncpy(number, (LPTSTR)((BYTE*)lpCallInfo + lpCallInfo->dwCallerIDOffset), 256);
/*256 symbols only for example*/
using this, how would i know if the phone is on a call already? would i be concerned with dwMessageId also here and what constant would i be comparing to?
thanks.
you can't
use RIL_GetLineStatus from RIL
Has someone experience about using the TAPI Wrapper with .NET CF2.0 ?
Here it doesn'T work at all :-(

CSD GSM communication...

Hi!
Could someone give me point about CSD GSM communication?
What is done:
I have connected 2 Qtek S100 through data-call:
- use TAPI - get “connected”
- take handle to COMM
- init COMM: 9600/ e.t.
- successfully transfer data
But several strange things I have:
1. average transfer rate – channel capacity - for transparent mode (S100 allows to manipulate transparent/non-transparent mode) is 6.4k bit/sec. – and this is write to COMM. speed! i.e. transferring side shows this speed by simple WriteFile() operation for chunk of data for example 64k bytes. Some times I receive corrupted data – but it’s ok for me. – transfer rate is strange!
2. average transfer rate for non-transparent mode is 8.8k bit/sec.- some times I see dropped series of bytes and equation: received + dropped == transferred is correct – so all is ok and speed is ok too ~1100 data bytes per sec +1 for stop-bits and +start-bit(? – not sure).
3. _most_ interesting for me – after some number of errors the connection is dropped – I achieve Line IDLE state.
So – most interesting for me is 3. – I don’t wanna lost connection – I am agree to have some errors in communication – but I don’t want to make call again.
As I understand construction (coarsely) is
COMM->RIL
So does it mean I have to go inside RIL to set something to preserve “connection state”?
And same for transparent mode – does it something inside RIL to set for “improving” channel capacity from 6.4k close to expected 9.6k bits/sec?
Here is my DCB settings
Code:
PortDCB.BaudRate = CBR_9600; // Current baud
PortDCB.ByteSize = 8; // Number of bits/byte, 4-8
PortDCB.Parity = NOPARITY; // Parity odd,even,mark,space
PortDCB.fBinary = TRUE; // Binary mode; no EOF check
PortDCB.fParity = FALSE; // No parity checking
PortDCB.fOutxCtsFlow = FALSE; // CTS output flow control
PortDCB.fOutxDsrFlow = FALSE; // No DSR output flow control
PortDCB.fDtrControl = DTR_CONTROL_ENABLE; //DTR output ON
PortDCB.fRtsControl = RTS_CONTROL_ENABLE; //RTS_CONTROL_HANDSHAKE;
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
how to stay connected?
Thanks for any advice.
a guess.....
Try transferring less data per write. Also you probably do not need to alter any of the port settings. They normally work fine the way they are. You would still need to set com time outs for the read/write opperation.
As an experiment just omit all your port settings and try transfering 1 byte at a time. If it still drops out it could be your quality of service.
yes - use smaller portion may help me.
but I think it just masks effect of line drop - interest is: what a reason to line-drop and what parameters influences on it?
some level of error per some period of time - or what?
thanks anyway.

Win32 Mode Failure to set text in static control

I have a problem in setting controls in a Win32 application.
When I use SendMessage to set a text string in a static control I get error number 120 which translated means: "This function is only valid in Win32 mode."
Here's the background:
I'm using Visual Studio 2005, I've installed the Windows Mobile 5 SDK.
The project was created using the New Project Wizard, with the following settings:
Visual C++/Smart Device
Win32 Smart Device Project
Platform SDK is: Windows Mobile 5.0 Pocket PC SDK
Windows application
I've added a dialog template resource and can bring up a dialog based on that resource. It contains a static control that I can read the text from, but attempting to set the text gives me the above error.
The relevent code (within the WM_INITDIALOG message handler) is:
Code:
char buffer[100];
HWND hCtrl = ::GetDlgItem(hDlg, IDC_TITLE);
LRESULT result = ::SendMessage(hCtrl, WM_GETTEXT, (WPARAM)80, (LPARAM)buffer);
This works, I get the correct text (ie the text I have placed into the static control using the dialog template editor) copied into the buffer. What follows is:
Code:
buffer[0] = 'A'; // Just to set up a different string
result = ::SendMessage(hCtrl, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
if (result == 0)
{
LPVOID lpMsgBuf;
DWORD error = ::GetLastError();
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
::MessageBox(NULL, (LPCTSTR)lpMsgBuf, (LPCWSTR)"LError", MB_OK | MB_ICONINFORMATION);
::LocalFree(lpMsgBuf);
}
The attempt to set the changed text string fails (ie result == 0), and the last error is 120, which is translated to the error message above.
This works the same in both the Windows Mobile 5 emulator, and if I run it on my Atom.
What does this mean? How could it be anything other than Win32 mode?
Is there a function or something I have to call to put it into Win32 mode?
Or is it a project setting in VS2005 that I haven't been able to find?
I'd very much appreciate any help on this.
Peter
Hi Peter.
The only problem I can see in your code is that you are using char instead of WCHAR. Windows Mobile devices only use UNICODE for API calls.
I don't think it is causing the error, but it is strange that you manage to get the correct text out using it.
The only thing I can think of is using GetWindowText and SetWindowText APIs instead of sending the message directly.
Maybe this will solve your problem.
I also notice that you did not initialize the buffer. The string needs to be null terminated so try initializeing all the elements before putting your 'A' in.
I thought 120 was not supported on this system ERROR_CALL_NOT_IMPLEMENTED.
Guys,
Thanks very much for the suggestions.
As for not initializing the buffer, I think the WM_GETTEXT call does that, all I do with the buffer[0] = 'A' call is to modify a single character that now contains the text that was copied out from the control.
But I take your point about using WCHAR, I'll do that when I get a chance.
And I'll try the Get and SetWindowText calls.
Thanks again.
Ok, How about this then?
The buffer is initialized by the WM_GETTEXT message, and it is set to the correct value (ie the initial contents of the control).
Changing the first character works.
The WM_SETTEXT message fails (result == 0) and the translated error (120) as displayed in the message box is "This function is only valid in Win32 mode."
The SetWindowText also fails with the same last error.
Does anyone know what "Win32 mode" is? I have spent time searching the MSDN and googling it. I've seen some references to it as an error message, but I haven't found an explanation.
Thanks,
Peter
Code:
WCHAR buffer[100];
HWND hCtl = ::GetDlgItem(hDlg, IDC_TITLE);
LRESULT result = ::SendMessage(hCtl, WM_GETTEXT, (WPARAM)80, (LPARAM)buffer);
buffer[0] = WCHAR('A');
//BOOL textResult = ::SetWindowText(hCtl, (LPCWSTR)buffer);
result = ::SendMessage(hCtl, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
if (result == 0)
{
WCHAR errorTitle[] = L"LastError";
LPVOID lpMsgBuf;
DWORD error = ::GetLastError();
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
::MessageBox(NULL, (LPCTSTR)lpMsgBuf, (LPCWSTR)errorTitle, MB_OK | MB_ICONINFORMATION);
::LocalFree(lpMsgBuf);
}
Ok, there is no apparent reason why your code doesn't work. Like OdeeanRDeathshead said according to MSDN error 120 means 'This function is not supported on this system.', something you definitely should not be getting.
One question: is this code inside a class member function? I am asking because of the '::' in front of every API.
Try removing them and see what happens.
Also, no need to use WCHAR('A'), just write L'A' save your self some typing in the long run.
P.S. In my year and a half of programming for Windows Mobile I never heard of a 'Win32 mode' or a function not working because of it. The error may be elsewhere in your code, some problem with name-spaces perhaps?
Are you running this on an actual device or just the emulator?
Hi levenum,
Thanks for the reply. Where to start?
Firstly I tend to use the scope resolution operator wherever possible. In this case ::SendMessage (for example), is meant to be a clear message to the reader that these functions are not member functions of the class, but instead exist in the global namespace. I have tried removing them and there is no difference.
You also asked if the code was in a class member function. Well, yes. It is a static member function of a specialization of a generic dialog class. Both the about box dialog and this setup dialog specialize this generic dialog class. Each has their own static dialog message processing function.
The code is in the part that handles the WM_INITDIALOG message. This is where I would normally (ie in Win32 on good ol' PC type windows) initialize controls before the dialog box is displayed. I have shown the dialog message function, and it is almost identical to the one for the about box that is generated when you create a Win32 project in the wizard.
One other point is that the GetLastError call and the format call are exactly out of the MSDN. If you look up the FormatMessage function, there is example code to translate the error code returned by GetLastError. That's exactly what I have here, and the message it gives is about not being in Win32 mode. So now I'm curious, how do you arrive at the 'This function is not supported on this system' comment. I looked for error and 120, and the only thing I could find was something about ARM messages "improper line syntax; wrong use of local label".
By the way, the SetWindowText function (commented out) gives exactly the same error code. As you say, surely both of these are allowed on this system.
I also appreciate you comment about not having heard of a Win32 mode.
But you have got me wondering if I can simplify the problem further. I will try the initial small project you get generated from the wizard, and in the about box dialog message processing function I will try to set the text in one of the two static controls on the about box.
Thanks again,
Peter
Code:
// Message handler for the setup dalog.
INT_PTR CALLBACK
DialogSetup::DialogSetupDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// Create a Done button and size it.
SHINITDLGINFO shidi;
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
shidi.hDlg = hDlg;
SHInitDialog(&shidi);
// Set up strings
//LRESULT result = ::SendDlgItemMessage(hDlg, IDC_TITLE, WM_SETTEXT, 0, (LPARAM)"Setup");
WCHAR buffer[100];
HWND hCtl = ::GetDlgItem(hDlg, IDC_TITLE);
LRESULT result = ::SendMessage(hCtl, WM_GETTEXT, (WPARAM)80, (LPARAM)buffer);
buffer[0] = WCHAR('A');
//BOOL textResult = ::SetWindowText(hCtl, (LPCWSTR)buffer);
result = ::SendMessage(hCtl, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
if (result == 0)
{
WCHAR errorTitle[] = L"LastError";
LPVOID lpMsgBuf;
DWORD error = ::GetLastError();
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
::MessageBox(NULL, (LPCTSTR)lpMsgBuf, (LPCWSTR)errorTitle, MB_OK | MB_ICONINFORMATION);
::LocalFree(lpMsgBuf);
}
}
return (INT_PTR)TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
}
break;
case WM_CLOSE:
EndDialog(hDlg, message);
return TRUE;
#ifdef _DEVICE_RESOLUTION_AWARE
case WM_SIZE:
{
DRA::RelayoutDialog
(
GetInstance(),
hDlg,
DRA::GetDisplayMode() != DRA::Portrait ? MAKEINTRESOURCE(IDD_SETUP_WIDE) : MAKEINTRESOURCE(IDD_SETUP)
);
}
break;
#endif
}
return (INT_PTR)FALSE;
}
Hi levenum,
Thanks for the reply. Where to start?
Firstly I tend to use the scope resolution operator wherever possible. In this case ::SendMessage (for example), is meant to be a clear message to the reader that these functions are not member functions of the class, but instead exist in the global namespace. I have tried removing them and there is no difference.
You also asked if the code was in a class member function. Well, yes. It is a static member function of a specialization of a generic dialog class. Both the about box dialog and this setup dialog specialize this generic dialog class. Each has their own static dialog message processing function.
The code is in the part that handles the WM_INITDIALOG message. This is where I would normally (ie in Win32 on good ol' PC type windows) initialize controls before the dialog box is displayed. I have shown the dialog message function, and it is almost identical to the one for the about box that is generated when you create a Win32 project in the wizard.
One other point is that the GetLastError call and the format call are exactly out of the MSDN. If you look up the FormatMessage function, there is example code to translate the error code returned by GetLastError. That's exactly what I have here, and the message it gives is about not being in Win32 mode. So now I'm curious, how do you arrive at the 'This function is not supported on this system' comment. I looked for error and 120, and the only thing I could find was something about ARM messages "improper line syntax; wrong use of local label".
By the way, the SetWindowText function (commented out) gives exactly the same error code. As you say, surely both of these are allowed on this system.
I also appreciate you comment about not having heard of a Win32 mode.
But you have got me wondering if I can simplify the problem further. I will try the initial small project you get generated from the wizard, and in the about box dialog message processing function I will try to set the text in one of the two static controls on the about box.
Thanks again,
Peter
Code:
// Message handler for the setup dalog.
INT_PTR CALLBACK
DialogSetup::DialogSetupDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// Create a Done button and size it.
SHINITDLGINFO shidi;
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
shidi.hDlg = hDlg;
SHInitDialog(&shidi);
// Set up strings
//LRESULT result = ::SendDlgItemMessage(hDlg, IDC_TITLE, WM_SETTEXT, 0, (LPARAM)"Setup");
WCHAR buffer[100];
HWND hCtl = ::GetDlgItem(hDlg, IDC_TITLE);
LRESULT result = ::SendMessage(hCtl, WM_GETTEXT, (WPARAM)80, (LPARAM)buffer);
buffer[0] = WCHAR('A');
//BOOL textResult = ::SetWindowText(hCtl, (LPCWSTR)buffer);
result = ::SendMessage(hCtl, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
if (result == 0)
{
WCHAR errorTitle[] = L"LastError";
LPVOID lpMsgBuf;
DWORD error = ::GetLastError();
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
::MessageBox(NULL, (LPCTSTR)lpMsgBuf, (LPCWSTR)errorTitle, MB_OK | MB_ICONINFORMATION);
::LocalFree(lpMsgBuf);
}
}
return (INT_PTR)TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
}
break;
case WM_CLOSE:
EndDialog(hDlg, message);
return TRUE;
#ifdef _DEVICE_RESOLUTION_AWARE
case WM_SIZE:
{
DRA::RelayoutDialog
(
GetInstance(),
hDlg,
DRA::GetDisplayMode() != DRA::Portrait ? MAKEINTRESOURCE(IDD_SETUP_WIDE) : MAKEINTRESOURCE(IDD_SETUP)
);
}
break;
#endif
}
return (INT_PTR)FALSE;
}
That error message comes from the free evc compilers. In the tool "error lookup" and in the help, it gives that mesage to correspond to that number.
I have just tested everthing you did. My dialog also uses a class with static methods for its routine. I used your exact code in the initial dialog handler and experimented with the origional contents of the controll. I tested on 2002 2003 2005 devices and can't make it fail. The worst i can get to happen is if there is no text to start with, just a square character displays after the A.
the only thing different is that you say two dialogs use that routine, but I am guessing that you are not doing bothe at once at this time.
Guys, thanks again for anything you can give me.
Lets see, I don't have the situation where two dialogs are using the same function. They both use a different message handling function (different name).
As I said in my last post (sorry I seem to have posted it twice), I was going to simplify the example. I've done that and I get the same problem.
I created another project with the same settings I described above, called "TestSetText". I made a change in only one place. In the function called "About", the message handling function for the about box dialog, I have inserted my example code to get and set the text for one of the static controls called IDC_STATIC_2. I have made no other changes to the code. So the modified About function is as below, and I have commented the section I have added:
Code:
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// Create a Done button and size it.
SHINITDLGINFO shidi;
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
shidi.hDlg = hDlg;
SHInitDialog(&shidi);
// Code to check setting text - only change
//LRESULT result = SendDlgItemMessage(hDlg, IDC_STATIC_2, WM_SETTEXT, 0, L"Example");
WCHAR buffer[100];
HWND hCtl = GetDlgItem(hDlg, IDC_STATIC_2);
LRESULT result = SendMessage(hCtl, WM_GETTEXT, (WPARAM)80, (LPARAM)buffer);
MessageBox(NULL, (LPCTSTR)buffer, (LPCWSTR)L"Text from control", MB_OK | MB_ICONINFORMATION);
// Modify the extracted text
buffer[0] = L'A';
//BOOL textResult = SetWindowText(hCtl, (LPCWSTR)buffer);
result = SendMessage(hCtl, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
if (result == 0)
{
WCHAR errorTitle[] = L"LastError";
LPVOID lpMsgBuf;
DWORD error = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, (LPCWSTR)errorTitle, MB_OK | MB_ICONINFORMATION);
LocalFree(lpMsgBuf);
}
// End of my change
}
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hDlg, message);
return TRUE;
#ifdef _DEVICE_RESOLUTION_AWARE
case WM_SIZE:
{
DRA::RelayoutDialog(
g_hInst,
hDlg,
DRA::GetDisplayMode() != DRA::Portrait ? MAKEINTRESOURCE(IDD_ABOUTBOX_WIDE) : MAKEINTRESOURCE(IDD_ABOUTBOX));
}
break;
#endif
}
return (INT_PTR)FALSE;
}
So now there are no double colons, no second dialog, no base class, nothing else to confuse the issue. When the app starts all I have to do is invoke the Help|About menu.
When the WM_GETTEXT message is complete, buffer contains exactly the text I would have expected "TestSetText Version 1.0", and this is what is displayed in the message box. So in principle the SendMessage function works and the control handle (hCtl) is correct. This leads me to suspect that all necessary controls are created by the time the WM_INITDIALOG message is processed, and SHOULD be a good place to modify the controls prior to displaying the dialog box.
Modifying the first character in the buffer (setting it to 'A') succeeds as I can see in the debugger.
The attempt to send the WM_SETTEXT message fails (ie result == 0) and GetLastError returns 120 again. The code as I have it there translates this to "This function is only valid in Win32 mode." And this is displayed in the next message box.
When you see the about box, the first character in the IDC_STATIC_2 control has not been changed.
I've also tried using the SetWindowText call which returns non zero (a failure), and the same thing happens (GetLastError returns 120).
One final comment is that if you look up in the MSDN the FormatMessage function, it gives you code (that I have copied) to translate the GetLastError into a string. This gives the Win32 mode message above.
On the other hand, I have found the 120 error in the section "System Errors - Numerical Order" and 120 is listed there as "This function is not valid on this platform." which both of you guys have mentioned.
Ok, two different error messages. The Win32 mode error I don't understand. The not valid error is much more understandable, but how could these functions not be valid?
If you have stayed with me this far then you have a lot of patience.
Thanks
Peter
perhaps you could work around this. Define a value to use as your own message like #define MY_SETUP_MESSAGE ...some value.
Then do nothing in the handler for WM_INITDIALOG except post your own setup message. Do all your normal stuff there. If that succeeds then take a closer look at the timing of WM_INITDIALOG, else there is something waky going on.
Hang on a second, you wrote:
I've also tried using the SetWindowText call which returns non zero (a failure), and the same thing happens (GetLastError returns 120).
Click to expand...
Click to collapse
Is that correct or is it a typo? According to MSDN non zero return for SetWindowText means success, not failure.
One thing you should try is calling SetLastError(0) before calling SetWindowText. Last error is no automatically reset in any way, so it is possible that error 120 is a result of a previous function. Although that still wouldn't account for not seeing the change text in the control.
In any case this is strange. I always initialize controls in WM_INITDIALOG handler and it never failed before.
Ah, you're right. Sorry, I screwed that up. I misread the MSDN there and did a GetLastError anyway, which returned 120.
SetWindowText returns a value of 1, which implies success, but it wasn't successful in the sense of setting the text, when you see the about box it is unchanged.
I also did a SetLastError to zero before the call, and did a GetLastError after it (even though the return code said success), and it was zero. So by all accounts (return code and GetLastError) it should have worked but didn't. The about box is unchanged.
I have also SetLastError to zero before the SendMessage to set the text. That returns zero, and I am sure that the MSDN says should be TRUE if the text is set, so an error. Trouble is, GetLastError then gives zero implying that there was no failure. When you see the about box, the text is not changed so it did fail.
I will have to experiment some more. Maybe I can't set text in a static for some reason. Do I need to call InitCommonControls for that or something (which I haven't done)?
I will also have to try setting text in an edit control.
I used the format message and got the same win32 mode stuff now. However, the text was modified to the new value. I think the win32 mode error message is a bug. Put the same code into the emulator and the error returned is invalid handle. At least invalid handle is something more definite. In both cases, emulator and real device, even though the returned value is 0 for WM_SETTEXT, and the error message is there, I do not think there is an error. Have you just tried it without any error checking?
Are you creating the dialog using creatdialog/dialogbox?
Hi OdeeanRDeathshead, thanks for the reply.
I don't think the invalid handle error is right here because I have used the same control window handle (hCtl) a couple of statements before to retrieve the text and that worked. I guess what I haven't checked is whether the control window handle is changed somehow as a result of the first call, but that would be really weird.
You said that even though SendMessage/WM_SETTEXT returned 0, you thought it probably wasn't an error. Well, there is support for that in that when I SetLastError to zero just prior to calling it, and call GetLastError just after, the error code was zero, thus no error. But the reason I think that there WAS an error is that it did not modify the static control. This is what I get if I effectively don't do any error checking.
As for how I am creating the dialog, the only code I have modified from the original project created from the application wizard is in the WM_INITDIALOG section of the About function that I showed you above. The dialog is created in the WndProc message handling function for the main window, part of which is below. If it is the IDM_HELP_ABOUT command, from the menu, it calls DialogBox specifying the dialog message processing function 'About' where my modified code is.
The code below is produced by the application wizard.
Code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static SHACTIVATEINFO s_sai;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
[b]case IDM_HELP_ABOUT:
DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, About);
break;[/b]
case IDM_OK:
SendMessage (hWnd, WM_CLOSE, 0, 0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
etc
I think I am going to have to experiment some more on the weekend. Try some other controls etc.
By the way I meant to mention that exactly the same thing happens in both the emulator as well as my Atom. So far, in all of the combinations of code, I have not been able to programmatically change the text content of a static control on a dialog.
And I've just noticed that my attempt to highlight (bold) the relevent part of the code above didn't work.
D'oh!
Actually I have just thought of something that may be relevent.
The emulator and SDK that came by default with VisualStudio2005 is WindowsCE 2003. When I first tried my code in the emulator, it worked. I could not only set text in a static control, I was also successfully manipulating a date/time picker in a separate dialog. I did lots of GDI rendering in the main window as well. And this code also worked on a friend's WindowsCE2003 PDA.
Things looked good until I tried it on my Atom which is WindowsMobile 5.
None of the above worked (except the GDI rendering). I thought it may have been a problem with the development environment. So I downloaded the WindowsMobile 5 SDK which includes a WM5 emulator. I thought this is bound to fix the problem, ie generate the code with the WM5 SDK.
All of my problems above are with WM5 project and emulator. And I said I was using this way back in the first post.
OdeeanRDeathshead, I had a quick look at your profile and you seem to have some earlier XDA machines. Are you using Windows Mobile 5?
If the problem does turn out to be Windows Mobile 5 related, then that says something very bad about backwards compatibility for that platform. Could it also be a WM5 bug? Surely not something so fundamental as this?
I use evc++3 for a compiler, mainly because I am the kind of person who thinks - if its not broaken why change. I have an Atom and do use it to run code on. I have all os, ie 2002,2002pe,2003,2003se,2005, so I don't think this problem is with the device. It may be vis studio 2005, but others have said it works OK.
PPC operating systems have a nasty history of backwards compatibility issues. I could not say that this is the issue here, but it is definately one reason I stick to a lowest common denominator approach. Why not just get evc3 or evc4 and give them a try. They are free.
I guess I was hoping to do it with Visual Studio 2005 because that is what I use at work. These days I mainly program in C#/.NET.
I kind of miss the low level stuff that I used to do on windows a long time ago. But I reminisce.
I'm assuming that evc++ is embedded visual c++? It sounds like really good advice, I'll look for it and it sounds like it will fix all my problems. Especially if as you say, it works for WindowsMobile 5. Can you tell me if you are supposed to use the WindowsMobile 5 SDK with it?
So OdeeanRDeathshead and levenum, thanks a lot for your help guys.
Peter

Set Device Time from Server

Hi,
Does anybody know how to pull the time from a server and use that time to update the devices clock?
I want to lock the Clock down on the device so the best way to ensure the time is correct is to sync it with a server.
I'm using VB.net.
Give this a spin.
It sets the get the UTC time, from http://www.timeapi.org/ , but have a look at the site to see what else it can do.
Your device should deal with your timezone offset from UTC automatically.
I used .NET Reflector to translate into VB from C#, hence the odd definition of some variables.
Code:
Public Structure SYSTEMTIME
Public wYear As UInt16
Public wMonth As UInt16
Public wDayOfWeek As UInt16
Public wDay As UInt16
Public wHour As UInt16
Public wMinute As UInt16
Public wSecond As UInt16
Public wMilliseconds As UInt16
End Structure
Declare Function GetSystemTime Lib "CoreDll.dll" _
(ByRef lpSystemTime As SYSTEMTIME) As UInt32
Declare Function SetSystemTime Lib "CoreDll.dll" _
(ByRef lpSystemTime As SYSTEMTIME) As UInt32
Private Shared Sub Main(ByVal args As String())
Dim Buffer As Byte() = New Byte(&H19 - 1) {}
Dim DateItems As String() = New String(8 - 1) {}
Dim Separators As Char() = New Char() { "-"c, "-"c, "T"c, ":"c, ":"c, "+"c, ":"c }
Dim Now As New SYSTEMTIME
Program.GetSystemTime((Now))
Dim ResponseStream As Stream = WebRequest.Create("http://www.timeapi.org/utc/now").GetResponse.GetResponseStream
ResponseStream.Read(Buffer, 0, &H19)
ResponseStream.Close
DateItems = Encoding.UTF8.GetString(Buffer, 0, &H19).Split(Separators)
Now.wYear = Convert.ToUInt16(DateItems(0))
Now.wMonth = Convert.ToUInt16(DateItems(1))
Now.wDay = Convert.ToUInt16(DateItems(2))
Now.wHour = Convert.ToUInt16(DateItems(3))
Now.wMinute = Convert.ToUInt16(DateItems(4))
Now.wSecond = Convert.ToUInt16(DateItems(5))
Now.wMilliseconds = 0
Program.SetSystemTime((Now))
End Sub
The original C# code is here. You will have to convert the C# 'using' statements to VB 'Imports'
Code:
using System;
using System.Net;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace GetTime
{
class Program
{
[DllImport("coredll.dll")]
private extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime);
[DllImport("coredll.dll")]
private extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);
private struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
static void Main(string[] args)
{
byte[] Buffer = new byte[25];
string DateString;
string[] DateItems = new string[8];
char[] Separators = new char[7] { '-', '-', 'T', ':', ':', '+', ':' };
SYSTEMTIME Now = new SYSTEMTIME();
GetSystemTime(ref Now);
Stream ResponseStream = WebRequest.Create("http://www.timeapi.org/utc/now").GetResponse().GetResponseStream();
ResponseStream.Read(Buffer, 0, 25);
ResponseStream.Close();
DateString = Encoding.UTF8.GetString(Buffer, 0, 25);
DateItems = DateString.Split(Separators);
Now.wYear = Convert.ToUInt16(DateItems[0]);
Now.wMonth = Convert.ToUInt16(DateItems[1]);
Now.wDay = Convert.ToUInt16(DateItems[2]);
Now.wHour = Convert.ToUInt16(DateItems[3]);
Now.wMinute = Convert.ToUInt16(DateItems[4]);
Now.wSecond = Convert.ToUInt16(DateItems[5]);
Now.wMilliseconds = 0;
SetSystemTime(ref Now);
}
}
}
Here's how it works: The call to the URL returns the date/time as a char buffer.
"2011-12-02T14:56:38+00:00" as an example.
After converting this to a string we convert it into an array of strings in DateItems[] as
2011
12
02
14
56
38
00
00
using Separators[] to split the fields apart.
These are then converted to ushort values in the time structure, before setting the time on the device with it.
Nice code stephj .
Cheers!!!
Thanks for that stephj, will give it a go.
i've tried this and i'm getting a :
The remote server returned an error: (407) Proxy Authentication Required.
any ideas?
I tried the above program on an emulated device connected to the net via its host PC's connection and it worked a treat.
To get through a proxy, you will need to add the following code.
Code:
Program.GetSystemTime((Now))
**************INSERT THIS ****************
Dim Proxy as new WebProxy("http://ProxyServer:80/",true)
proxy.Credentials = CredentialCache.DefaultCredentials
WebRequest.DefaultWebProxy = proxy
**************INSERT ENDS ****************
Dim ResponseStream As Stream = WebRequest.Create("http://www.timeapi.org/utc/now").GetResponse.GetResponseStream
Where the first parameter of the WebProxy constructor is the name of your proxy server, and the port it uses. Here at work, we can get away with just using our proxy server's internal network DNS name of "Internet:8080", to access the outside web through a Microsoft ISA Proxy/Firewall. Note in this case the proxy uses a different port number of 8080 as opposed to the default http port of 80. If yours is not 80 you will have to provide it. You may have to do some groundwork to track down the name of your proxy server, and the port it uses. I would start by looking at the proxy setup in Settings->Connections->'Set up my proxy server' for starters.
CredentialCache.DefaultCredentials, picks up your default login/credentials and passes them to the proxy server. If it works properly, you should go through it, seamlessly.
Good luck!
Hi steph,
i tweaked this slightly and set up a webreference in my program, i then pull the time down from that in the format of "2011-12-12T09:43:14", it then splits it.
The problem i am having is that sometimes it seems to add an hour to the time.
Any ideas?
What time zone is your device operating under?
Start->Settings->[System]->Clock & Alarms->[Time]
What's the zone, and is it Home or Visiting? - I'll see if I can duplicate it.
GMT London and is set as Home.
As a test I set the time on the device to: 01/06/1999 and time 01:00:00. When i run the program the first time i press my 'Update Time' button and it returns 12/12/11 and 12:53:04, so 1 hour ahead. If i press the button again I get the correct time.
I notice on your original post that the time returned had +00:00 on the end but you dont seem to use it when you break the string down, is this not required?
Thanks for the help, much appreciated
At least I can duplicate it, so that is a start. It only seems to throw the hour forward if the date has changed, which explains why the second call to it corrects the time.
If you just knock the time back a couple of hours, a single call of the program sets the correct time.
Interesting. I'll try and get to the bottom of it, might take a day or so, on and off.
Edit: Got it!
The 1st of June 1999 is in BST one hour in front of GMT/UTC. The first call changes the date and time using BST, leaving the time pushed forward one hour. The date is then set to today's date which is in GMT/UTC. The second call sets the time against the GMT Timezone which corrects the time.
Set the date time to 04:00 yesterday and it works fine.
I'll see if we can fix it a bit better. More later.........
Here's the cure: It's C#, I'll leave you to convert it to VB.
The secret is to set the time twice, but stall the thread for ten seconds inbetween. The device will sort itself out in the gap, as various time and date housekeeping tasks are triggered. The second SetTime(), will carry out the final fix of the time before the program finally ends.
This should also work in BST when changing from a GMT/UTC date, and should also work with all global time zones.
Add this:
Code:
using System.Threading;
SetSystemTime(ref Now);
Thread.Sleep(10000);
SetSystemTime(ref Now);
that's great steph, thanks again.
There is a minor problem with the previous code:- If the minute happens to roll over in the 10 seconds while the process is asleep, then the change of minute will be lost.
Here's a better solution. Create a DateTime object in which to store the returned date and time from the WebRequest().
Use it to set the system date/time, and then advance it forward ten seconds.
Set the date/time with it again, after the ten second sleep() has elapsed.
Seems to work O.K. Post bug reports to this thread, if you find it doesn't.
Code:
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
namespace GetTime
{
class Program
{
[DllImport("coredll.dll")]
private extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);
private struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
static void Main(string[] args)
{
byte[] Buffer = new byte[25];
string DateString;
string[] DateItems = new string[8];
char[] Separators = new char[7] { '-', '-', 'T', ':', ':', '+', ':' };
SYSTEMTIME Now = new SYSTEMTIME();
Stream ResponseStream = WebRequest.Create("http://www.timeapi.org/utc/now").GetResponse().GetResponseStream();
ResponseStream.Read(Buffer, 0, 25);
ResponseStream.Close();
DateString = Encoding.UTF8.GetString(Buffer, 0, 25);
DateItems = DateString.Split(Separators);
DateTime SaveTime = new DateTime(Convert.ToInt32(DateItems[0]),Convert.ToInt32(DateItems[1]),Convert.ToInt32(DateItems[2]),Convert.ToInt32(DateItems[3]),Convert.ToInt32(DateItems[4]),Convert.ToInt32(DateItems[5]));
Now.wYear = Convert.ToUInt16(SaveTime.Year);
Now.wMonth = Convert.ToUInt16(SaveTime.Month);
Now.wDay = Convert.ToUInt16(SaveTime.Day);
Now.wHour = Convert.ToUInt16(SaveTime.Hour);
Now.wMinute = Convert.ToUInt16(SaveTime.Minute);
Now.wSecond = Convert.ToUInt16(SaveTime.Second);
Now.wMilliseconds = 0;
SetSystemTime(ref Now);
SaveTime.AddSeconds(10);
Thread.Sleep(10000);
Now.wYear = Convert.ToUInt16(SaveTime.Year);
Now.wMonth = Convert.ToUInt16(SaveTime.Month);
Now.wDay = Convert.ToUInt16(SaveTime.Day);
Now.wHour = Convert.ToUInt16(SaveTime.Hour);
Now.wMinute = Convert.ToUInt16(SaveTime.Minute);
Now.wSecond = Convert.ToUInt16(SaveTime.Second);
SetSystemTime(ref Now);
}
}
}
C# .NET CF 2.0 WinMo 5.0 onwards executable is included in the zip file.
It is a console application, so don't expect anything much to happen until the 'hourglass' disappears.
If the first SetSystemTime() ends up changing the date across a Daylight Saving Boundary date, then, during the sleep() period, the device's housekeeping date/time/alarm services will probably retrigger any outstanding task and event reminders for 'today'.
P.S. GetSystemTime() can be dropped it is not required.
works like a charm steph, nice one. thanks.
@stephj, Thanks for the updated code .
Cheers!!!
Seem to be having a problem with this since UK has put the clocks forward an hour, every time I update using this code my device is adding an hour to the time.
The device is an M3 Mobile.
What are the settings on the Start-> Settings->Clock and Alarms->Time[Tab]
It should still have the Home radio button active and the time zone set to GMT London, Dublin. The mobile device should take care of daylight saving itself.
Is your M3 running WinMo 6.1 or 6.5? But I can't see it making much difference.
Works OK on a stock 6.1 Kaiser (Vodafone v1615)
Update: Also seems to work OK on the WM 6.5.3 Professional emulator.
wubbledoos said:
Hi,
Does anybody know how to pull the time from a server and use that time to update the devices clock?
I want to lock the Clock down on the device so the best way to ensure the time is correct is to sync it with a server.
I'm using VB.net.
Click to expand...
Click to collapse
Well on my Kaisers (running either wm 6.1 or 6.5) I use SKTsync to sync my device clock to one of the NIST or SNTP servers. It's freeware and it does it all for you, all you do is run the app. It's soooo simple. I also run an app called CT Scheduler lite. I use it to automatically run SKTsync every night.

Resources