Programming Microsoft Windows Ce Net 3Rd [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Microsoft Windows Ce Net 3Rd [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








IrSock


I've alluded to IrSock a number of times as I've described functions. IrSock is essentially a socketlike API built over the top of the IrDA stack used for infrared communication. IrSock is the only high-level interface to the IrDA stack.


The major differences between IrSock and Winsock are that IrSock doesn't support datagrams, it doesn't support security, and the method used for addressing it is completely different from that used for Winsock. What IrSock does provide is a method to query the devices ready to talk across the infrared port, as well as arbitration and collision detection and control.


From a programmer's perspective, the main difference in programming IrSock and Winsock is that the client side needs a method of detecting which infrared-capable devices are within range and are ready to accept a socket connection. This is accomplished by calling getsockopt with the level parameter set to SOL_IRLMP and the optname parameter set to IRLMP_ENUMDEVICES, as in the following:


dwBuffSize = sizeof (buffer);
rc = getsockopt (hIrSock, SOL_IRLMP, IRLMP_ENUMDEVICES,
buffer, &dwBuffSize);


When called with IRLMP_ENUMDEVICES, getsockopt returns a DEVICELIST structure in the buffer. DEVICELIST is defined as


typedef struct _DEVICELIST {
ULONG numDevice;
IRDA_DEVICE_INFO Device[1];
} DEVICELIST;


The DEVICELIST structure is simply a count followed by an array of IRDA_DEVICE_INFO structures, one for each device found. The IRDA_DEVICE_INFO structure is defined as


typedef struct _IRDA_DEVICE_INFO {
u_char irdaDeviceID[4];
char irdaDeviceName[22];
u_char Reserved[2];
} IRDA_DEVICE_INFO;


The two fields in the IRDA_DEVICE_INFO structure are a device ID and a string that can be used to identify the remote device.


Following is a routine that opens an IR socket and uses getsockopt to query the remote devices that are in range. If any devices are found, their names and IDs are printed to the debug port.


//
// Poll for IR devices.
//
DWORD WINAPI IrPoll (HWND hWnd) {
INT rc, nSize, i, j;
char cDevice[256];
TCHAR szName[32], szOut[256];
DEVICELIST *pDL;
SOCKET irsock;
// Open an infrared socket.
irsock = socket (AF_IRDA, SOCK_STREAM, 0);
if (irsock == INVALID_SOCKET)
return -1;
// Search for someone to talk to; try 10 times over 5 seconds.
for (i = 0; i < 10; i++) {
// Call getsockopt to query devices.
memset (cDevice, 0, sizeof (cDevice));
nSize = sizeof (cDevice);
rc = getsockopt (irsock, SOL_IRLMP, IRLMP_ENUMDEVICES,
cDevice, &nSize);
if (rc)
break;

pDL = (DEVICELIST *) cDevice;
if (pDL->numDevice) {
Add2List (hWnd, TEXT ("%d devices found."), pDL->numDevice);
for (j = 0; j < (int)pDL->numDevice; j++) {
// Convert device ID.
wsprintf (szOut,
TEXT ("DeviceID \t%02X.%02X.%02X.%02X"),
pDL->Device[j].irdaDeviceID[0],
pDL->Device[j].irdaDeviceID[1],
pDL->Device[j].irdaDeviceID[2],
pDL->Device[j].irdaDeviceID[3]);
OutputDebugString (szOut);
// Convert device name to Unicode.
mbstowcs (szName, pDL->Device[j].irdaDeviceName,
sizeof (pDL->Device[j].irdaDeviceName));
wsprintf (szOut, TEXT ("irdaDeviceName \t%s"),
szName);
OutputDebugString (szOut);
}
}
Sleep(500);
}
closesocket (irsock);
return 0;
}


Just having a device with an IR port in range isn't enough; the remote device must have an application running that has opened an IR socket, bound it, and placed it into listen mode. This requirement is appropriate because these are the steps any server using the socket API would perform to configure a socket to accept communication.



Querying and Setting IR Socket Options




IrSock supports the getsockopt and setsockopt functions for getting and setting the socket options, but the options supported have little overlap with the socket options supported for a standard TCP/IP socket. To query socket options, use this function:


int getsockopt (SOCKET s, int level, int optname,
char FAR *optval, int FAR *optlen);


The first parameter is the handle to the socket, while the second parameter is the level in the communications stack for the specific option. The level can be at the socket level, SOL_SOCKET, or a level unique to IrSock, SOL_IRLMP. The options supported for IrSock are shown in the following lists.


For the SOL_SOCKET level, your option is





SO_LINGERQueries the linger mode





For the SOL_IRLMP level, your options are





IRLMP_ENUMDEVICESEnumerates remote IrDA devices





IRLMP_IAS_QUERYQueries IAS attributes





IRLMP_SEND_PDU_LENQueries the maximum size of send packet for IrLPT mode





The corresponding function with which to set the options is


int setsockopt (SOCKET s, int level, int optname,
const char FAR *optval, int optlen);


The parameters are similar to getsockopt. A list of the allowable options follows.


For the SOL_SOCKET level, your option is





SO_LINGERDelays the close of a socket if unsent data remains in the outgoing queue





For the SOL_IRLMP level, your options are





IRLMP_IAS_SET Sets IAS attributes





IRLMP_IRLPT_MODESets the IrDA protocol to IrLPT





IRLMP_9WIRE_MODE Sets the IrDA protocol to 9-wire serial mode





IRLMP_SHARP_MODESets the IrDA protocol to Sharp mode






Blocking vs. Nonblocking Sockets




One issue I briefly touched on as I was introducing sockets is blocking. Windows programmers are used to the quite handy asynchronous socket calls that are an extension of the standard Berkeley socket API. By default, a socket is in blocking mode so that, for example, if you call recv to read data from a socket and no data is available, the call blocks until some data can be read. This isn't the type of call you want to be making with a thread that's servicing the message loop for your application.


Although Windows CE doesn't support the WSAAsync calls available to desktop versions of Windows, you can switch a socket from its default blocking mode to nonblocking mode. In nonblocking mode, any socket call that might need to wait to successfully perform its function instead returns immediately with the error code WSAEWOULDBLOCK. You are then responsible for calling the would-have-blocked function again at a later time to complete the task.


To set a socket into blocking mode, use this function:


int ioctlsocket (SOCKET s, long cmd, u_long *argp);


The parameters are the socket handle, a command, and a pointer to a variable that either contains data or receives data depending on the value in cmd. The allowable commands for Windows CE IrSock sockets are the following:





FIONBIOSets or clears a socket's blocking mode. If the value pointed to by argp is nonzero, the socket is placed in blocking mode. If the value is 0, the socket is placed in nonblocking mode.





FIONREADReturns the number of bytes that can be read from the socket with one call to the recv function.





So to set a socket in blocking mode, you should make a call like this one:


fBlocking = FALSE;
rc = ioctlsocket (sock, FIONBIO, &fBlocking);


Of course, once you have a socket in nonblocking mode, the worst thing you can do is continually poll the socket to see whether the nonblocked event occurred. On a battery-powered system, this can dramatically lower battery life. Instead of polling, you can use the select function to inform you when a socket or set of sockets is in a nonblocking state. The prototype for this function is


int select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
fd_set FAR *exceptfds,
const struct timeval FAR *timeout);


The parameters for the select function look somewhat complex, which, in fact, they are. Just to throw a curve, the function ignores the first parameter. The reason it exists at all is for compatibility with the Berkeley version of the select function. The next three parameters are pointers to sets of socket handles. The first set should contain the sockets that you want to be notified when one or more of the sockets is in a nonblocking read state. The second set contains socket handles of sockets that you want informed when a write function can be called without blocking. Finally, the third set, pointed to by exceptfds, contains the handles of sockets that you want notified when an error condition exists in that socket.


The final parameter is a timeout value. In keeping with the rather interesting parameter formats for the select function, the timeout value isn't a simple millisecond count. Rather, it's a pointer to a TIMEVAL structure defined as


struct timeval {
long tv_sec;
long tv_usec;
};


If the two fields in TIMEVAL are 0, the select call returns immediately, even if none of the sockets has had an event occur. If the pointer, timeout, is NULL instead of pointing to a TIMEVAL structure, the select call won't time out and returns only when an event occurs in one of the sockets. Otherwise, the timeout value is specified in seconds and microseconds in the two fields provided.


The function returns the total number of sockets for which the appropriate events occur, 0 if the function times out, or SOCKET_ERROR if an error occurs. If an error does occur, you can call WSAGetLastError to get the error code. The function modifies the contents of the sets so that, on returning from the function, the sets contain only the socket handles of sockets for which events occur.


The sets that contain the events should be considered opaque. The format of the sets doesn't match their Berkeley socket counterparts. Each of the sets is manipulated by four macros defined in WINSOCK.H. These are the four macros:





FD_CLR Removes the specified socket handle from the set





FD_ISSETReturns TRUE if the socket handle is part of the set





FD_SET Adds the specified socket handle to the set





FD_ZERO Initializes the set to 0





To use a set, you have to declare a set of type fd_set. Then initialize the set with a call to FD_ZERO and add the socket handles you want with FD_SET. An example would be


fd_set fdReadSocks;
FD_ZERO (&fdReadSocks);
FD_SET (hSock1, &fdReadSocks);
FD_SET (hSock2, &fdReadSocks);
rc = select (0, &fdReadSocks, NULL, NULL, NULL);
if (rc != SOCKET_ERROR) {
if (FD_ISSET (hSock1, &fdReadSocks))
// A read event occurred in socket 1.
if (FD_ISSET (hSock2, &fdReadSocks))
// A read event occurred in socket 2.
}


In this example, the select call waits on read events from two sockets with the handles hSock1 and hSock2. The write and error sets are NULL, as is the pointer to the timeout structure, so the call to select won't return until a read event occurs in one of the two sockets. When the function returns, the code checks to see whether the socket handles are in the returned set. If so, that socket has a nonblocking read condition.


The last little subtlety concerning the select function is just what qualifies as a read, write, and error condition. A socket in the read set is signaled when one of the following events occurs:





There is data in the input queue, so recv can be called without blocking.





The socket is in listen mode and a connection has been attempted, so a call to accept won't block.





The connection has been closed, reset, or terminated. If the connection was gracefully closed, recv returns with 0 bytes read; otherwise, the recv call returns SOCKET_ERROR. If the socket has been reset, the recv function returns the error WSACONNRESET.


A socket in the write set is signaled under the following conditions:





Data can be written to the socket. A call to send still might block if you attempt to write more data than can be held in the outgoing queue.





A socket is processing a connect and the connect has been accepted by the server.


A socket in the exception set is signaled under the following condition:





A socket is processing a connect and the connect failed.






The MySquirt Example Program




To demonstrate IrSock, the following program, MySquirt, shows how to transfer files from one Windows system to another. It's similar to the IrSquirt program provided with the Pocket PC and Smartphone. The difference is that this program is designed to be compiled for and run on Windows CE and Windows XP systems[1]. So by running the program on these systems, you can send, that is, squirt, files from one system to another. MySquirt has a window that displays a list of status messages as the handshaking takes place between the two Windows systems. To use MySquirt, you'll need to have it running on two Windows systems. To transfer a file, enter the name of the file you want to send and press the Send button. The system transmits the name and size of the file to the receiving system, and if it's accepted, the file data is subsequently sent. Figure 14-1 shows MySquirt on an embedded Windows CE device after it has sent a file to a Pocket PC, while Figure 14-2 shows the results on the Pocket PC screen. The source code for the example is shown in Listing 14-1.




Figure 14-1: The MySquirt window on an embedded Windows CE device after a file has been sent




Figure 14-2: The MySquirt window on a Pocket PC after a file has been received


Listing 14-1: The MySquirt source code








MySquirt.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h"
#include "MySquirt.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "MySquirt.ico" // Program icon
//----------------------------------------------------------------------
// Main window dialog template
//
MySquirt DIALOG discardable 10, 10, 135, 110
STYLE WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU |
DS_CENTER | DS_MODALFRAME
CAPTION "MySquirt"
CLASS "MySquirt"
BEGIN
LTEXT "&File:" -1, 2, 11, 15, 12
EDITTEXT IDD_OUTTEXT, 17, 10, 71, 12,
WS_TABSTOP | ES_AUTOHSCROLL
PUSHBUTTON "&Send File" IDD_SENDFILE, 92, 10, 38, 12, WS_TABSTOP
LISTBOX IDD_INTEXT, 2, 25, 128, 80,
WS_TABSTOP | WS_VSCROLL
END


MySquirt.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
// Defines that are different between Windows CE and Desktop Windows
#ifdef _WIN32_WCE
// Windows CE-specific defines
#define LPCMDLINE LPWSTR
// On Windows CE, we call begin thread directly.
#define MyCreateThread CreateThread
// Desktop Windows defines
#else
#define LPCMDLINE LPSTR
// This macro calls beginthreadex when this program is compiled
// for the desktop.
typedef unsigned (__stdcall *PTHREAD_START)(void *);
#define MyCreateThread(psa, cbStack, pfnStartAddr, pvParam, fdwCreate,pdwThreadID)
((HANDLE) _beginthreadex ((void *)(psa), (unsigned)(cbStack), (PTHREAD_START)
(pfnStartAddr),(void *)(pvParam), (unsigned)(fdwCreate), (unsigned *)(pdwThreadID)))
#endif
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define ID_ICON 1
#define IDD_INTEXT 10 // Control IDs
#define IDD_SENDFILE 11
#define IDD_OUTTEXT 12
// Error codes used by transfer protocol
#define GOOD_XFER 0
#define BAD_FILEOPEN -1
#define BAD_FILEMEM -2
#define BAD_FILEREAD -3
#define BAD_FILEWRITE -3
#define BAD_SOCKET -4
#define BAD_SOCKETRECV -5
#define BAD_filesIZE -6
#define BAD_MEMORY -7
#define BLKSIZE 8192 // Transfer block size
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPCMDLINE, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoSizeMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoPocketPCShell (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
// Command functions
LPARAM DoMainCommandSend (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
// Thread functions
DWORD WINAPI MonitorThread (PVOID pArg);
DWORD WINAPI ReceiveThread (PVOID pArg);
DWORD WINAPI SendFileThread (PVOID pArg);


MySquirt.cpp
//======================================================================
// MySquirt - A simple IrSock application for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <stdlib.h>
#include <stdio.h>
#include <af_irda.h> // IrDA includes
#include <winsock.h> // Socket includes
#include "MySquirt.h" // Program-specific stuff
#ifndef _WIN32_WCE
#include <process.h> // Desktop multithread includes
#include <tchar.h>
#endif
#if defined(WIN32_PLATFORM_PSPC)
#include <aygshell.h> // Add Pocket PC includes.
#pragma comment( lib, "aygshell" ) // Link Pocket PC lib for menu bar.
#endif
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("MySquirt");
const char chzAppName[] = "MySquirt";
HINSTANCE hInst; // Program instance handle
HWND hMain; // Main window handle
BOOL fContinue = TRUE; // Server thread continue flag
BOOL fFirstSize = TRUE; // First WM_SIZE flag
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
SHACTIVATEINFO sai; // Needed for P/PC helper functions
#endif
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_SIZE, DoSizeMain,
WM_COMMAND, DoCommandMain,
WM_SETTINGCHANGE, DoPocketPCShell,
WM_ACTIVATE, DoPocketPCShell,
WM_DESTROY, DoDestroyMain,
};
// Command Message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
IDOK, DoMainCommandExit,
#else
IDOK, DoMainCommandSend,
#endif
IDCANCEL, DoMainCommandExit,
IDD_SENDFILE, DoMainCommandSend,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPCMDLINE lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;

// Initialize application.
hMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hMain == 0)
return TermInstance (hInstance, 0x10);
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
if ((hMain == 0) || !IsDialogMessage (hMain, &msg)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPCMDLINE lpCmdLine,
int nCmdShow){
HWND hWnd;
HANDLE hThread;
WNDCLASS wc;
WSADATA wsaData;
int rc;
hInst = hInstance; // Save program instance handle.
// For all systems, if previous instance exists, activate it instead
// of starting a new one.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)((DWORD)hWnd | 0x01));
return 0;
}
// Init Winsock
rc = WSAStartup (1, &wsaData);
if (rc) {
MessageBox (NULL, TEXT("Error in WSAStartup"), szAppName, MB_OK);
return 0;
}
// Register application main window class.
wc.style = 0; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = DLGWINDOWEXTRA; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL; // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Create main window.
hWnd = CreateDialog (hInst, szAppName, NULL, NULL);
// Return 0 if window not created.
if (!IsWindow (hWnd)) return 0;
// Create secondary threads for interprocess communication.
hThread = MyCreateThread (NULL, 0, MonitorThread, hWnd, 0, 0);
if (hThread == 0) {
DestroyWindow (hWnd);
return 0;
}
CloseHandle (hThread);
ShowWindow (hWnd, nCmdShow); // Standard show and update calls
UpdateWindow (hWnd);
SetFocus (GetDlgItem (hWnd, IDD_OUTTEXT));
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for main window
TCHAR szTitle[128];
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
SHINITDLGINFO shidi;
SHMENUBARINFO mbi; // For Pocket PC, create
memset(&mbi, 0, sizeof(SHMENUBARINFO)); // menu bar so that we
mbi.cbSize = sizeof(SHMENUBARINFO); // have a sip button.
mbi.dwFlags = SHCMBF_EMPTYBAR;
mbi.hwndParent = hWnd;
SHCreateMenuBar(&mbi);
SendMessage(mbi.hwndMB, SHCMBM_GETSUBMENU, 0, 100);
// For Pocket PC, make dialog box full screen with P/PC-
// specific call. Since this call is only on P/PC, we
// must use Loadlibrary, GetProcAddress to gain access
// to the function.
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLG | SHIDIF_SIPDOWN;
shidi.hDlg = hWnd;
SHInitDialog(&shidi);
sai.cbSize = sizeof (sai);
SHHandleWMSettingChange(hWnd, wParam, lParam, &sai);
#endif
GetWindowText (hWnd, szTitle, dim (szTitle));
return 0;
}
//----------------------------------------------------------------------
// DoSizeMain - Process WM_SIZE message for window.
//
LRESULT DoSizeMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
static RECT rectListbox;
RECT rect;
GetClientRect (hWnd, &rect);
if (fFirstSize) {
// First time through, get the position of the list box for
// resizing later. Store the distance from the sides of
// the list box control to the side of the parent window.
if (IsWindow (GetDlgItem (hWnd, IDD_INTEXT))) {
fFirstSize = FALSE;
GetWindowRect (GetDlgItem (hWnd, IDD_INTEXT), &rectListbox);
MapWindowPoints (HWND_DESKTOP, hWnd, (LPPOINT)&rectListbox, 2);
rectListbox.right = rect.right - rectListbox.right;
rectListbox.bottom = rect.bottom - rectListbox.bottom;
}
}
SetWindowPos (GetDlgItem (hWnd, IDD_INTEXT), 0, rect.left + 5,
rectListbox.top, rect.right - 10,
rect.bottom - rectListbox.top - 5,
SWP_NOZORDER);
#endif
return 0;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
LRESULT DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code)
return (*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
}
return 0;
}
//----------------------------------------------------------------------
// DoPocketPCShell - Process Pocket PC-required messages.
//
LRESULT DoPocketPCShell (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
if (wMsg == WM_SETTINGCHANGE)
return SHHandleWMSettingChange(hWnd, wParam, lParam, &sai);
if (wMsg == WM_ACTIVATE)
return SHHandleWMActivate(hWnd, wParam, lParam, &sai, 0);
#endif
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
fContinue = FALSE; // Shut down server thread.
Sleep (0); // Pass on timeslice.
PostQuitMessage (0);
return 0;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
SendMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandSend - Process Program Send File command.
//
LPARAM DoMainCommandSend (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
static TCHAR szName[MAX_PATH];
GetDlgItemText (hWnd, IDD_OUTTEXT, szName, dim(szName));
MyCreateThread (NULL, 0, SendFileThread, (PVOID)szName, 0, NULL);
return 0;
}
//----------------------------------------------------------------------
// Add2List - Add string to the report list box.
//
void Add2List (HWND hWnd, LPTSTR lpszFormat, ...) {
int i, nBuf;
TCHAR szBuffer[512];
va_list args;
va_start(args, lpszFormat);
nBuf = _vstprintf(szBuffer, lpszFormat, args);
i = SendDlgItemMessage (hWnd, IDD_INTEXT, LB_ADDSTRING, 0,
(LPARAM)(LPCTSTR)szBuffer);
if (i != LB_ERR)
SendDlgItemMessage (hWnd, IDD_INTEXT, LB_SETTOPINDEX, i,
(LPARAM)(LPCTSTR)szBuffer);
va_end(args);
}
//----------------------------------------------------------------------
// MySetWindowText - Set window title to passed printf style string.
//
void MySetWindowText (HWND hWnd, LPTSTR lpszFormat, ...) {
int nBuf;
TCHAR szBuffer[512];
va_list args;
va_start(args, lpszFormat);
nBuf =_vstprintf(szBuffer, lpszFormat, args);
SetWindowText (hWnd, szBuffer);
va_end(args);
}
//======================================================================
// MonitorThread - Monitors for connections; connects and notifies
// user when a connection occurs.
//
DWORD WINAPI MonitorThread (PVOID pArg) {
HWND hWnd = (HWND)pArg;
INT rc, nSize, i;
SOCKADDR_IRDA iraddr, t_iraddr;
SOCKET t_sock, s_sock;
Add2List (hWnd, TEXT("Monitor thread entered"));
// Open an infrared socket.
s_sock = socket (AF_IRDA, SOCK_STREAM, 0);
if (s_sock == INVALID_SOCKET) {
Add2List (hWnd, TEXT("Socket failed. rc %d"), WSAGetLastError());
return 0;
}
// Fill in irda socket address structure.
iraddr.irdaAddressFamily = AF_IRDA;
for (i = 0; i < dim (iraddr.irdaDeviceID); i++)
iraddr.irdaDeviceID[i] = 0;
memcpy (iraddr.irdaServiceName, chzAppName, sizeof (chzAppName) + 1);
// Bind address to socket.
rc = bind (s_sock, (struct sockaddr *)&iraddr, sizeof (iraddr));
if (rc) {
Add2List (hWnd, TEXT(" bind failed"));
closesocket (s_sock);
return 0;
}
// Set socket into listen mode.
rc = listen (s_sock, SOMAXCONN);
if (rc == SOCKET_ERROR) {
Add2List (hWnd, TEXT(" listen failed %d"), GetLastError());
closesocket (s_sock);
return 0;
}
// Wait for remote requests.
// Block on accept.
while (fContinue) {
nSize = sizeof (t_iraddr);
t_sock = accept (s_sock, (struct sockaddr *)&t_iraddr, &nSize);
if (t_sock == INVALID_SOCKET) {
Add2List (hWnd, TEXT(" accept failed %d"), GetLastError());
}
Add2List (hWnd, TEXT("sock accept..."));
MyCreateThread (NULL, 0, ReceiveThread, (PVOID)t_sock, 0, NULL);
}
closesocket (s_sock);
Add2List (hWnd, TEXT("Monitor thread exit"));
return 0;
}
//======================================================================
// ReceiveThread - Sends the file requested by the remote device
//
DWORD WINAPI ReceiveThread (PVOID pArg) {
SOCKET t_sock = (SOCKET)pArg;
HWND hWnd = hMain; // I'm cheating here.
int nCnt, nFileSize, rc;
TCHAR szFileName[MAX_PATH];
char szAnsiName[MAX_PATH];
PBYTE pBuff;
int i, nSize, nTotal;
DWORD dwBytes;
HANDLE hFile;
Add2List (hWnd, TEXT("receive thread entered"));
SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_ABOVE_NORMAL);
// Read the number of bytes in the filename.
rc = recv (t_sock, (LPSTR)&nCnt, sizeof (nCnt), 0);
if ((rc == SOCKET_ERROR) || (nCnt > MAX_PATH)) {
Add2List (hWnd, TEXT("failed receiving name size"));
closesocket (t_sock);
return 0;
}
// Read the filename. If Pocket PC, put file in my documents.
// Deal in ANSI here since it will be translated to Unicode later.
#if defined(WIN32_PLATFORM_PSPC)
strcpy (szAnsiName, "\\my documents\\"); //Ansi
#else
strcpy (szAnsiName, "\\"); //Ansi
#endif //defined(WIN32_PLATFORM_PSPC)
i = strlen (szAnsiName); //Ansi
rc = recv (t_sock, (LPSTR)&szAnsiName[i], nCnt, 0);
if (rc == SOCKET_ERROR) {
Add2List (hWnd, TEXT("failed receiving name"));
closesocket (t_sock);
return 0;
}
#ifdef _UNICODE
mbstowcs (szFileName, szAnsiName, strlen (szAnsiName) + 1);
#else
lstrcpy (szFileName, szAnsiName);
#endif
Add2List (hWnd, TEXT("name: %s"), szFileName);
pBuff = (PBYTE)LocalAlloc (LPTR, BLKSIZE); //Create buff for file.
//
// Receive file size.
//
rc = recv (t_sock, (LPSTR)&nFileSize, sizeof (nFileSize), 0);
Add2List (hWnd, TEXT("received file size of %d bytes"), nFileSize);
if ((rc != SOCKET_ERROR) && (nFileSize > 0)) {
// We should really check here to see if there is enough
// free space to receive the file.
// Create the file. Overwrite if user says so.
rc = 0;
hFile = CreateFile (szFileName, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
Add2List (hWnd, TEXT("File Open failed. rc %d"),
GetLastError());
rc = BAD_FILEWRITE;
}
// Send ack code.
Add2List (hWnd, TEXT("Sending size ack."));
send (t_sock, (LPSTR)&rc, sizeof (rc), 0);
//
// Receive file.
//
nTotal = nFileSize;
while ((!rc) && (nFileSize > 0)) {
MySetWindowText (hWnd, TEXT ("%02d%% received"),
(nTotal-nFileSize)*100/nTotal);
nCnt = min (BLKSIZE, nFileSize);
for (nSize = 0; nSize < nCnt;) {
i = recv (t_sock, (LPSTR)pBuff+nSize, nCnt-nSize, 0);
if (i == SOCKET_ERROR) {
Add2List (hWnd, TEXT("recv socket err %d"),
GetLastError());
rc = BAD_SOCKETRECV;
break;
}
nSize += i;
}
Add2List (hWnd, TEXT("recv'd %d bytes."), nSize);
if (i) {
if (!WriteFile (hFile, pBuff, nSize, &dwBytes, 0))
rc = BAD_FILEWRITE;
nFileSize -= dwBytes;
} else
Sleep(50);
// Send ack of packet.
send (t_sock, (LPSTR)&rc, sizeof (rc), 0);
}
} else if (rc == BAD_FILEOPEN)
Add2List (hWnd, TEXT("File not found."));
Add2List (hWnd, TEXT("receive finished"));
SetWindowText (hWnd, szTitle);
LocalFree (pBuff);
CloseHandle (hFile);
Add2List (hWnd, TEXT("receive thread exit"));
return 0;
}
//----------------------------------------------------------------------
// SendFile - Sends a file to the remote device
//
DWORD WINAPI SendFileThread (PVOID pArg) {
TCHAR *szFileName = (LPTSTR)pArg;
HWND hWnd = hMain;
SOCKET c_sock;
char szAnsiName[MAX_PATH];
HANDLE hFile;
INT rc, nSize, i, nFileSize, nTotal, nCnt;
char cDevice[256];
SOCKADDR_IRDA iraddr;
DEVICELIST *pDL;
LPSTR pPtr;
PBYTE pBuff;
// Open the file.
hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
Add2List (hWnd, TEXT("File open failed. rc %d"),
GetLastError());
return -1;
}
// Open an infrared socket.
c_sock = socket (AF_IRDA, SOCK_STREAM, 0);
if (c_sock == INVALID_SOCKET) {
Add2List (hWnd, TEXT("Sock failed. rc %d"), WSAGetLastError());
CloseHandle (hFile);
return 0;
}
// Search for someone to talk to.
for (i = 0; i < 5; i++) {
memset (cDevice, 0, sizeof (cDevice));
nSize = sizeof (cDevice);
rc = getsockopt (c_sock, SOL_IRLMP, IRLMP_ENUMDEVICES,
cDevice, &nSize);
if (rc)
Add2List (hWnd, TEXT("Getsockopt failed. rc %d"),
WSAGetLastError());
pDL = (DEVICELIST *) cDevice;
if (pDL->numDevice) {
Add2List (hWnd, TEXT("%d devices found."), pDL->numDevice);
break;
}
Sleep(500);
}
// If no device found, exit.
if (pDL->numDevice == 0) {
closesocket (c_sock);
CloseHandle (hFile);
Add2List (hWnd, TEXT("No infrared devices found in range."));
return -2;
}

//
// Copy address of found device.
//
memset (&iraddr, 0, sizeof (iraddr));
iraddr.irdaAddressFamily = AF_IRDA;
memcpy (iraddr.irdaDeviceID, pDL->Device[0].irdaDeviceID, 4);
//
// Now initialize the specific socket we're interested in.
//
memcpy (iraddr.irdaServiceName, chzAppName, sizeof (chzAppName)+1);
Add2List (hWnd, TEXT("Found: %hs"), pDL->Device[0].irdaDeviceName);
//
// Connect to remote socket.
//
rc = connect (c_sock, (struct sockaddr *)&iraddr, sizeof (iraddr));
if (rc) {
Add2List (hWnd, TEXT("Connect failed. rc %d"), WSAGetLastError());
closesocket (c_sock);
return -4;
}
Add2List (hWnd, TEXT("connected..."));
rc = 0;
nFileSize = GetFileSize (hFile, NULL);
// Allocate buffer and read file.
pBuff = (LPBYTE)LocalAlloc (LPTR, nFileSize);
if (pBuff) {
ReadFile (hFile, pBuff, nFileSize, (DWORD *)&nCnt, NULL);
if (nCnt != nFileSize)
rc = BAD_FILEREAD;
} else
rc = BAD_MEMORY;
if (rc) {
closesocket (c_sock);
CloseHandle (hFile);
Add2List (hWnd, TEXT("Error allocating buffer or reading file."));
return rc;
}
// Start transfer. First send size and get ack.
// Strip off any leading path, assume len > 1 since we've opened file.
for (i = lstrlen (szFileName)-1; (i > 0) &&
(szFileName[i] != TEXT ('\\')) ; i--);
if (szFileName[i] == TEXT ('\\')) i++;
// Send name size.
nCnt = (lstrlen (&szFileName[i]) + 1);
rc = send (c_sock, (LPSTR)&nCnt, sizeof (nCnt), 0);
// Send filename.
if (rc != SOCKET_ERROR) {
#ifdef _UNICODE
wcstombs (szAnsiName, &szFileName[i], nCnt);
#else
lstrcpy (szAnsiName, &szFileName[i]);
#endif
rc = send (c_sock, (LPSTR)szAnsiName, nCnt, 0);
}
// Send file size. Size will always be < 2 gig.
rc = send (c_sock, (LPSTR)&nFileSize, sizeof (nFileSize), 0);
if (rc == SOCKET_ERROR)
rc = BAD_SOCKET;
else
// Recv ack of file size.
recv (c_sock, (LPSTR)&rc, sizeof (rc), 0);
// Send the file.
nTotal = nFileSize;
pPtr = (LPSTR)pBuff;
while ((!rc) && nFileSize) {
MySetWindowText (hWnd, TEXT ("%02d%% sent"),
(nTotal-nFileSize)*100/nTotal);
// Send up to the block size.
nCnt = min (BLKSIZE, nFileSize);
rc = send (c_sock, pPtr, nCnt, 0);
if (rc == SOCKET_ERROR) {
Add2List (hWnd, TEXT("send error %d "), GetLastError());
rc = BAD_SOCKET;
} else
Add2List (hWnd, TEXT("sent %d bytes"), rc);
pPtr += rc;
nFileSize -= rc;
// Receive ack.
recv (c_sock, (LPSTR)&rc, sizeof (rc), 0);
}
SetWindowText (hWnd, szTitle);
// Send close code.
if (rc != BAD_SOCKET)
send (c_sock, (LPSTR)&rc, sizeof (rc), 0);

closesocket (c_sock);
// Clean up.
CloseHandle (hFile);
LocalFree (pBuff);
if (rc)
Add2List (hWnd, TEXT("SendFile Exit rc = %d"), rc);
else
Add2List (hWnd, TEXT("File sent successfully."));
return 0;
}












From a Windows standpoint, MySquirt is a simple program. It uses a dialog box as its main window. When the program is first launched, it creates a thread to monitor for other devices that creates an infrared socket, binds it to a service name, puts the socket into listen mode, and blocks on a call to accept. When a remote device connects, the monitor thread creates another thread to handle the actual receiving of the file while it loops back and waits for another connection.


A transmission is initiated when another device running MySquirt sends a file. This process begins when the user on the sending device presses the Send button. If text exists in the edit box, the application reads it and calls the SendFile routine. In this routine, a socket is created and any remote devices are enumerated using repeated calls to getsockopt. If a device is found, a connection is attempted with a call to connect. Connect succeeds only if the remote device has bound an IR socket using the same service name, which happens to be defined as the string contained in chzAppName, an ASCII representation of the program name. This addressing scheme ensures that if a connection is made, the remote device is running MySquirt. Once a connection is made, the sending device sends over the filename, which it does in two steps: first it sends the byte length of the filename and then the name itself. This process allows the server to know how many characters to receive before continuing. The device then sends the file size. If the file sent by the server device fits in the object store, the routine creates the file on the client side, notifying the user if the file already exists. If all has gone well to this point, the data is received and written to the file. The application closes the socket and frees the buffer created to read the data into.


On the receiving side, a transmission is initiated when the monitor thread's call to accept returns. The monitor thread creates a receiving thread and loops back looking for other sending devices. The receiving thread receives the name and size of the file and determines whether the file is acceptable. If so, it sends an acknowledgment back to the sending device. From then on, the receiving thread reads the data from the socket and writes it to the newly created file. When the transmission is complete, the receiving thread closes the file, closes the receiving socket, and terminates.


The other interesting aspect of MySquirt is that I wrote the program to be compiled on both Windows CE and the desktop versions of Windows using Microsoft Visual Studio .NET. I made a few adjustments to the program to handle the different declarations for the lpCmdLine parameter of WinMain and a macro to hide the differences between calling CreateThread in Windows CE and beginthreadex on the desktop. The example on the companion CD has project files for both eMbedded C++ for Windows CE compilation and Visual Studio .NET for compiling for the desktop.


[1] To build MySquirt for Windows XP or Windows Me, use Microsoft Visual C++ 6.0 or Microsoft Visual Studio .NET.


/ 169