870 lines
24 KiB
C++
870 lines
24 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This module implements a device for the FD provider Sample
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
|
|
struct TRecvTreadInfo
|
|
{
|
|
SOCKET hDiscoverySocket;
|
|
HANDLE hStopEvent;
|
|
GUID* pDeviceId;
|
|
TDeviceInfo* pDeviceInfo;
|
|
}; // SRecvTreadInfo
|
|
|
|
VOID PrintUsage()
|
|
{
|
|
printf("FDProviderSampleDevice.exe /Switch Value </Switch Value>\n\n");
|
|
printf("The following switches are supported:\n");
|
|
printf(" DeviceId (Required) a UUID that represents the unique identifier for the device.\n");
|
|
printf(" DeviceCategory (Required) Identies the type of device\n");
|
|
printf(" FriendlyName (Required) Name that users will see\n");
|
|
printf(" HardwareId (Required) PnP HardwareId for the device.\n");
|
|
printf(" Manufacturer (Optional) Manufacturer name.\n");
|
|
printf(" ManufacturerUrl (Optional) Manufacturer;s web page\n");
|
|
printf(" ModelName (Optional) Model name\n");
|
|
printf(" ModelNumber (Optional) Model number\n");
|
|
printf(" ModelUrl (Optional) Model Url\n");
|
|
printf(" Upc (Optional) UPC code for the device\n");
|
|
printf(" FirmwareVersion (Optional) Device's firware version\n");
|
|
printf(" SerialNumber (Optional) Device's serial number\n");
|
|
printf(" PresentationUrl (Optional) Device's internal web page for administration\n\n");
|
|
printf("Example:\n");
|
|
printf(" FDProviderSampleDevice /DeviceId e3fa675d-3b7f-4894-99c5-9b1348100037 /DeviceCategory \"MFP,Printers,Scanners\" /FriendlyName \"FD sample device 1\" /HardwareId \"SampleHardwareId\" /FirmwareVersion \"2.3.400.57-1 E3\"\n");
|
|
} // PrintUsage
|
|
|
|
VOID PrintStartInfo(
|
|
PCWSTR pszDeviceId,
|
|
const TDeviceInfo& DeviceInfo)
|
|
{
|
|
// Print the device configuration
|
|
printf("Starting FD Sample device with properties:\n");
|
|
printf(" DeviceId: %S\n", pszDeviceId);
|
|
printf(" DeviceCategory: %S\n", DeviceInfo.szDeviceCategory);
|
|
printf(" FriendlyName: %S\n", DeviceInfo.szFriendlyName);
|
|
printf(" HardwareId: %S\n", DeviceInfo.szPnPHardwareId);
|
|
printf(" Manufacturer: %S\n", DeviceInfo.szManufacturer);
|
|
printf(" ManufacturerUrl: %S\n", DeviceInfo.szManufacturerUrl);
|
|
printf(" ModelName: %S\n", DeviceInfo.szModelName);
|
|
printf(" ModelNumber: %S\n", DeviceInfo.szModelNumber);
|
|
printf(" ModelUrl: %S\n", DeviceInfo.szModelUrl);
|
|
printf(" UPC: %S\n", DeviceInfo.szUpc);
|
|
printf(" FirmwareVersion: %S\n", DeviceInfo.szFirmwareVersion);
|
|
printf(" SerialNumber: %S\n\n", DeviceInfo.szSerialNumber);
|
|
printf(" PresentationUrl: %S\n\n", DeviceInfo.szPresentationUrl);
|
|
|
|
// Print key map
|
|
printf("Press\n");
|
|
printf(" q : Quit\n");
|
|
printf(" h : Send Hello\n");
|
|
printf(" b : Send Bye\n");
|
|
} // PrintStartInfo
|
|
|
|
int ParseCMDLine(
|
|
int argc,
|
|
__in_ecount(argc) PWSTR argv[],
|
|
__out PWSTR& pszDeviceId,
|
|
__out GUID& DeviceId,
|
|
__out TDeviceInfo& DeviceInfo)
|
|
{
|
|
RPC_STATUS retVal = RPC_S_OK;
|
|
|
|
for (int i = 1; i < argc - 1; i += 2)
|
|
{
|
|
if (_wcsicmp(L"/?", argv[i]) == 0)
|
|
{
|
|
PrintUsage();
|
|
return 1;
|
|
}
|
|
else if (_wcsicmp(L"/DeviceId", argv[i]) == 0)
|
|
{
|
|
pszDeviceId = argv[i+1];
|
|
retVal = UuidFromString((RPC_WSTR) pszDeviceId, &DeviceId);
|
|
if (RPC_S_OK != retVal)
|
|
{
|
|
printf("DeviceId was not a valid UUID\n");
|
|
return 1;
|
|
}
|
|
}
|
|
else if (_wcsicmp(L"/DeviceCategory", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szDeviceCategory, sizeof(DeviceInfo.szDeviceCategory), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/FriendlyName", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szFriendlyName, sizeof(DeviceInfo.szFriendlyName), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/HardwareId", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szPnPHardwareId, sizeof(DeviceInfo.szPnPHardwareId), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/Manufacturer", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szManufacturer, sizeof(DeviceInfo.szManufacturer), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/ManufacturerUrl", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szManufacturerUrl, sizeof(DeviceInfo.szManufacturerUrl), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/ModelName", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szModelName, sizeof(DeviceInfo.szModelName), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/ModelNumber", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szModelNumber, sizeof(DeviceInfo.szModelNumber), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/ModelUrl", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szModelUrl, sizeof(DeviceInfo.szModelUrl), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/Upc", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szUpc, sizeof(DeviceInfo.szUpc), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/FirmwareVersion", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szFirmwareVersion, sizeof(DeviceInfo.szFirmwareVersion), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/SerialNumber", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szSerialNumber, sizeof(DeviceInfo.szSerialNumber), argv[i+1]);
|
|
}
|
|
else if (_wcsicmp(L"/PresentationUrl", argv[i]) == 0)
|
|
{
|
|
StringCbCopy(DeviceInfo.szPresentationUrl, sizeof(DeviceInfo.szPresentationUrl), argv[i+1]);
|
|
}
|
|
else
|
|
{
|
|
printf("Unrecognized command line parameter %S\n", argv[i]);
|
|
|
|
PrintUsage();
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Check that the required parameters were supplied
|
|
if ( !pszDeviceId
|
|
|| !*DeviceInfo.szDeviceCategory
|
|
|| !*DeviceInfo.szFriendlyName
|
|
|| !*DeviceInfo.szPnPHardwareId)
|
|
{
|
|
printf("All required switches were not supplied\n\n");
|
|
|
|
PrintUsage();
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
} // ParseCMDLine
|
|
|
|
ULONG GetDefaultMulticastInterfaceIndex(
|
|
ULONG AddressFamily,
|
|
__out ULONG& InterfaceIndex)
|
|
{
|
|
ULONG RetVal = 0;
|
|
PIP_ADAPTER_ADDRESSES pAdapterAddresses = NULL;
|
|
PIP_ADAPTER_ADDRESSES pCurrentAdapter = NULL;
|
|
ULONG BufSize = 0;
|
|
ULONG i = 0;
|
|
|
|
assert(( (AF_INET == AddressFamily)
|
|
|| (AF_INET6 == AddressFamily)));
|
|
|
|
for (i = 0; i < 5; ++i)
|
|
{
|
|
RetVal = GetAdaptersAddresses(
|
|
AddressFamily,
|
|
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_UNICAST,
|
|
NULL,
|
|
pAdapterAddresses,
|
|
&BufSize);
|
|
|
|
if (RetVal != ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pAdapterAddresses)
|
|
{
|
|
free(pAdapterAddresses);
|
|
}
|
|
|
|
pAdapterAddresses = (PIP_ADAPTER_ADDRESSES) malloc(BufSize);
|
|
if (!pAdapterAddresses)
|
|
{
|
|
RetVal = ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (NO_ERROR == RetVal)
|
|
{
|
|
// Got the list of adapters.
|
|
// Now find the first interface that is up and supports multicast
|
|
pCurrentAdapter = pAdapterAddresses;
|
|
while (pCurrentAdapter)
|
|
{
|
|
if ( (IfOperStatusUp == pCurrentAdapter->OperStatus) // Adapter is operational
|
|
&& ((IP_ADAPTER_NO_MULTICAST & pCurrentAdapter->Flags) == 0)) // Adapter is multicast capable
|
|
{
|
|
break;
|
|
}
|
|
|
|
pCurrentAdapter = pCurrentAdapter->Next;
|
|
}
|
|
|
|
if (pCurrentAdapter)
|
|
{
|
|
if (AF_INET == AddressFamily)
|
|
{
|
|
InterfaceIndex = pCurrentAdapter->IfIndex;
|
|
}
|
|
else
|
|
{
|
|
InterfaceIndex = pCurrentAdapter->Ipv6IfIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RetVal = ERROR_INVALID_FUNCTION;
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
if (pAdapterAddresses)
|
|
{
|
|
free(pAdapterAddresses);
|
|
}
|
|
|
|
return RetVal;
|
|
} // GetDefaultMulticastInterfaceIndex
|
|
|
|
|
|
int JoinMulticastGroupAndSetSocketOptions(
|
|
SOCKET hDiscoverySocket,
|
|
__in PADDRINFOW pMulticastAddrInfo,
|
|
__in PADDRINFOW pLocalAddrInfo)
|
|
{
|
|
int err = 0;
|
|
BOOL fEnabled = TRUE;
|
|
BOOL fDisabled = FALSE;
|
|
int sendRecvBufSize = 64 * 1024;
|
|
ULONG cByteCount = 0;
|
|
ULONG InterfaceIndex = 0;
|
|
|
|
// Set SO_EXCLUSIVEADDRUSE and SO_REUSEADDR for the socket
|
|
// so that there can be multiple listeners on the same machine
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
SOL_SOCKET,
|
|
SO_EXCLUSIVEADDRUSE,
|
|
(char*) &fDisabled,
|
|
sizeof(fDisabled));
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt SO_EXCLUSIVEADDRUSE failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
SOL_SOCKET,
|
|
SO_REUSEADDR,
|
|
(char*) &fEnabled,
|
|
sizeof(fEnabled));
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt SO_REUSEADDR failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Disable connection reset for UDP
|
|
err = WSAIoctl(
|
|
hDiscoverySocket,
|
|
SIO_UDP_CONNRESET,
|
|
&fDisabled,
|
|
sizeof(fDisabled),
|
|
NULL,
|
|
0,
|
|
&cByteCount,
|
|
NULL,
|
|
NULL);
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("WSAIoctl SIO_UDP_CONNRESET failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Adjust the send and receive buffer size
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
SOL_SOCKET,
|
|
SO_RCVBUF,
|
|
(char*) &sendRecvBufSize,
|
|
sizeof(sendRecvBufSize));
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt SO_RCVBUF failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
SOL_SOCKET,
|
|
SO_SNDBUF,
|
|
(char*) &sendRecvBufSize,
|
|
sizeof(sendRecvBufSize));
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt SO_SNDBUF failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
err = GetDefaultMulticastInterfaceIndex(
|
|
pMulticastAddrInfo->ai_family,
|
|
InterfaceIndex);
|
|
if (0 != err)
|
|
{
|
|
printf("GetDefaultMulticastInterfaceIndex failed. Error %u\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (AF_INET == pMulticastAddrInfo->ai_family) // IPv4
|
|
{
|
|
IP_MREQ mreq = {0};
|
|
|
|
// Join the multicast group
|
|
mreq.imr_multiaddr = ((PSOCKADDR_IN)pMulticastAddrInfo->ai_addr)->sin_addr; // Set the multicast address
|
|
memcpy(&mreq.imr_interface, &InterfaceIndex, sizeof(mreq.imr_interface)); // Set the interface index
|
|
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
IPPROTO_IP,
|
|
IP_ADD_MEMBERSHIP,
|
|
(char*) &mreq,
|
|
sizeof(mreq));
|
|
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt IP_ADD_MEMBERSHIP failed. Error %u\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the outgoing multicast interface
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_IF,
|
|
(char*) &InterfaceIndex,
|
|
sizeof(InterfaceIndex));
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt IP_MULTICAST_IF failed. Error %u\n", err);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else // IPv6
|
|
{
|
|
IPV6_MREQ mreq6 = {0};
|
|
|
|
// Join the multicast group
|
|
mreq6.ipv6mr_multiaddr = ((PSOCKADDR_IN6) pMulticastAddrInfo->ai_addr)->sin6_addr; // Set the multicast address
|
|
mreq6.ipv6mr_interface = InterfaceIndex;
|
|
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
IPPROTO_IPV6,
|
|
IPV6_ADD_MEMBERSHIP,
|
|
(char*) &mreq6,
|
|
sizeof(mreq6));
|
|
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt IPV6_ADD_MEMBERSHIP failed. Error %u\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the outgoing multicast interface
|
|
err = setsockopt(
|
|
hDiscoverySocket,
|
|
IPPROTO_IPV6,
|
|
IPV6_MULTICAST_IF,
|
|
(char*) &InterfaceIndex,
|
|
sizeof(InterfaceIndex));
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("setsockopt IPV6_MULTICAST_IF failed. Error %u\n", err);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return err;
|
|
} // JoinMulticastGroup
|
|
|
|
DWORD __stdcall RecvThread(
|
|
__in PVOID pParameter)
|
|
{
|
|
DWORD RetVal = 0;
|
|
TRecvTreadInfo* pRecvThreadInfo = (TRecvTreadInfo*) pParameter;
|
|
HANDLE hSelectEvent = NULL;
|
|
HANDLE hWaitHandles[2] = {0};
|
|
DWORD WaitSignal = 0;
|
|
ULONG cByteCount = 0;
|
|
SOCKADDR_STORAGE FromAddr = {0};
|
|
int FromAddrLen = 0;
|
|
TQueryMessage* pQueryMessage = NULL;
|
|
TReplyMessage ReplyMessage;
|
|
|
|
// Initialize random number generation on this thread.
|
|
srand((unsigned) time(NULL));
|
|
|
|
// Initilize the Reply message
|
|
ReplyMessage.MessageType = Reply;
|
|
ReplyMessage.DeviceId = *(pRecvThreadInfo->pDeviceId);
|
|
ReplyMessage.DeviceInfo = *(pRecvThreadInfo->pDeviceInfo);
|
|
|
|
// Allocate a message recv buffer
|
|
pQueryMessage = (TQueryMessage*) malloc(MaxMessageSize);
|
|
if (!pQueryMessage)
|
|
{
|
|
RetVal = ERROR_OUTOFMEMORY;
|
|
printf("Out of memory. Messages will not be received.\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Create select event
|
|
hSelectEvent = WSACreateEvent();
|
|
if (!hSelectEvent)
|
|
{
|
|
RetVal = WSAGetLastError();
|
|
printf("Failed to create hSelectEvent. Messages will not be received. Error %u\n", RetVal);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Register socket for select events
|
|
RetVal = WSAEventSelect(
|
|
pRecvThreadInfo->hDiscoverySocket,
|
|
hSelectEvent,
|
|
FD_READ);
|
|
|
|
if (0 != RetVal)
|
|
{
|
|
RetVal = WSAGetLastError();
|
|
printf("WSAEventSelect. Messages will not be received. Error %u\n", RetVal);
|
|
goto Cleanup;
|
|
}
|
|
|
|
hWaitHandles[0] = pRecvThreadInfo->hStopEvent;
|
|
hWaitHandles[1] = hSelectEvent;
|
|
|
|
while (true)
|
|
{
|
|
RetVal = 0;
|
|
|
|
// Wait for a message to arrive or shutdown to be signaled
|
|
WaitSignal = WaitForMultipleObjects(
|
|
ARRAYSIZE(hWaitHandles),
|
|
hWaitHandles,
|
|
FALSE,
|
|
INFINITE);
|
|
|
|
// These values should never occur
|
|
if ( (WaitSignal == WAIT_TIMEOUT )
|
|
|| (WaitSignal == WAIT_ABANDONED_0)
|
|
|| (WaitSignal == WAIT_ABANDONED_0 + 1))
|
|
{
|
|
printf("Unexpected wait result. Continue.\n");
|
|
|
|
// Reset the event
|
|
WSAResetEvent(hSelectEvent);
|
|
continue;
|
|
}
|
|
|
|
if (WAIT_OBJECT_0 == WaitSignal)
|
|
{
|
|
// Stop event has been signaled
|
|
break;
|
|
}
|
|
|
|
//
|
|
// A message has been received, process it
|
|
//
|
|
ZeroMemory(&FromAddr, sizeof(FromAddr));
|
|
FromAddrLen = sizeof(FromAddr);
|
|
cByteCount = recvfrom(
|
|
pRecvThreadInfo->hDiscoverySocket,
|
|
(char *) pQueryMessage,
|
|
MaxMessageSize,
|
|
0,
|
|
(sockaddr*) &FromAddr,
|
|
&FromAddrLen);
|
|
|
|
if (SOCKET_ERROR != cByteCount)
|
|
{
|
|
// Process message if it is a Query message
|
|
if ( (sizeof(TQueryMessage) == cByteCount)
|
|
&& (pQueryMessage->MessageType == Query))
|
|
{
|
|
// Respond if
|
|
// the device category is not specified
|
|
// or the same as our device criteria
|
|
if ( (L'\0' == pQueryMessage->szDeviceCategory[0])
|
|
|| (wcscmp(pQueryMessage->szDeviceCategory, pRecvThreadInfo->pDeviceInfo->szDeviceCategory) == 0))
|
|
{
|
|
// back off a bit so we don't overwelm the client if there are many devices.
|
|
Sleep(rand() % 100);
|
|
|
|
cByteCount = sendto(
|
|
pRecvThreadInfo->hDiscoverySocket,
|
|
(char*) &ReplyMessage,
|
|
sizeof(ReplyMessage),
|
|
0,
|
|
(sockaddr*) &FromAddr,
|
|
FromAddrLen);
|
|
|
|
if (0 != cByteCount)
|
|
{
|
|
printf("Reply sent.\n");
|
|
}
|
|
else
|
|
{
|
|
RetVal = WSAGetLastError();
|
|
printf("send failed. Error %u\n", RetVal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RetVal = WSAGetLastError();
|
|
printf("recv failed. Error %u\n", RetVal);
|
|
}
|
|
|
|
// Reset the event
|
|
WSAResetEvent(hSelectEvent);
|
|
}
|
|
|
|
Cleanup:
|
|
if (pQueryMessage)
|
|
{
|
|
free(pQueryMessage);
|
|
}
|
|
if (hSelectEvent)
|
|
{
|
|
WSACloseEvent(hSelectEvent);
|
|
}
|
|
|
|
return RetVal;
|
|
} // RecvThread
|
|
|
|
VOID StopRecv(
|
|
HANDLE hStopEvent,
|
|
HANDLE hRecvThread)
|
|
{
|
|
// Signal the Recv thread to stop.
|
|
if (!SetEvent(hStopEvent))
|
|
{
|
|
printf("Failed to set hStopEvent. Error: %u\n", GetLastError());
|
|
}
|
|
|
|
// Wait for the thread to end.
|
|
if (WaitForSingleObject(hRecvThread, INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
printf("WaitForSingleObject failed\n");
|
|
}
|
|
} // StopRecv
|
|
|
|
VOID SendHello(
|
|
SOCKET hDiscoverySocket,
|
|
__in GUID& DeviceId,
|
|
__in TDeviceInfo& DeviceInfo,
|
|
__in PADDRINFOW pMulticastAddrInfo)
|
|
{
|
|
int cByteCount = 0;
|
|
THelloMessage HelloMessage;
|
|
|
|
HelloMessage.MessageType = Hello;
|
|
HelloMessage.DeviceId = DeviceId;
|
|
HelloMessage.DeviceInfo = DeviceInfo;
|
|
|
|
cByteCount = sendto(
|
|
hDiscoverySocket,
|
|
(char*) &HelloMessage,
|
|
sizeof(HelloMessage),
|
|
0,
|
|
pMulticastAddrInfo->ai_addr,
|
|
(int) pMulticastAddrInfo->ai_addrlen);
|
|
|
|
if (0 != cByteCount)
|
|
{
|
|
printf("Hello Sent\n\n");
|
|
}
|
|
else
|
|
{
|
|
printf("send failed. Error %u\n", WSAGetLastError());
|
|
}
|
|
} // SendHello
|
|
|
|
VOID SendBye(
|
|
SOCKET hDiscoverySocket,
|
|
__in GUID& DeviceId,
|
|
__in PADDRINFOW pMulticastAddrInfo)
|
|
{
|
|
int cByteCount = 0;
|
|
TByeMessage ByeMessage;
|
|
|
|
ByeMessage.MessageType = Bye;
|
|
ByeMessage.DeviceId = DeviceId;
|
|
|
|
cByteCount = sendto(
|
|
hDiscoverySocket,
|
|
(char*) &ByeMessage,
|
|
sizeof(ByeMessage),
|
|
0,
|
|
pMulticastAddrInfo->ai_addr,
|
|
(int) pMulticastAddrInfo->ai_addrlen);
|
|
|
|
if (0 != cByteCount)
|
|
{
|
|
printf("Bye Sent\n\n");
|
|
}
|
|
else
|
|
{
|
|
printf("send failed. Error %u\n", WSAGetLastError());
|
|
}
|
|
} // SendBye
|
|
|
|
int __cdecl wmain(
|
|
int argc,
|
|
__in_ecount(argc) PWSTR argv[])
|
|
{
|
|
int err = 0;
|
|
WSADATA wsaData = {0};
|
|
PWSTR pszDeviceId = NULL;
|
|
GUID sDeviceId = {0};
|
|
TDeviceInfo DeviceInfo = {0};
|
|
WCHAR ch = 0;
|
|
SOCKET hDiscoverySocket = INVALID_SOCKET;
|
|
HANDLE hStopEvent = NULL;
|
|
HANDLE hRecvThread = NULL;
|
|
TRecvTreadInfo RecvThreadInfo = {0};
|
|
ADDRINFOW addrHints = {0};
|
|
PADDRINFOW pMulticastAddrInfo = NULL;
|
|
PADDRINFOW pLocalAddrInfo = NULL;
|
|
|
|
// Parse command line parameters;
|
|
err = ParseCMDLine(
|
|
argc,
|
|
argv,
|
|
pszDeviceId,
|
|
sDeviceId,
|
|
DeviceInfo);
|
|
if (0 != err)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
PrintStartInfo(pszDeviceId, DeviceInfo);
|
|
|
|
// Initialize Winsock
|
|
err = WSAStartup(MAKEWORD(2,2), &wsaData);
|
|
if (0 == err)
|
|
{
|
|
if ( (2 != LOBYTE(wsaData.wVersion))
|
|
|| (2 != HIBYTE(wsaData.wVersion)))
|
|
{
|
|
printf("Winsock 2.2 is not supported\n");
|
|
err = 1;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("Error initializing Winsock: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Create musticast socket address structure
|
|
addrHints.ai_flags = AI_NUMERICHOST;
|
|
addrHints.ai_family = AF_UNSPEC;
|
|
addrHints.ai_socktype = SOCK_DGRAM;
|
|
addrHints.ai_protocol = IPPROTO_UDP;
|
|
|
|
err = GetAddrInfo(
|
|
szMulticastAddress,
|
|
szMulticastPort,
|
|
&addrHints,
|
|
&pMulticastAddrInfo);
|
|
|
|
if (0 != err)
|
|
{
|
|
printf("GetAddrInfo failed for the multicast address: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Create local address to bind to
|
|
addrHints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
|
|
addrHints.ai_family = pMulticastAddrInfo->ai_family;
|
|
addrHints.ai_socktype = pMulticastAddrInfo->ai_socktype;
|
|
addrHints.ai_protocol = pMulticastAddrInfo->ai_protocol;
|
|
|
|
err = GetAddrInfo(
|
|
NULL, // local socket
|
|
szMulticastPort,
|
|
&addrHints,
|
|
&pLocalAddrInfo);
|
|
|
|
if (0 != err)
|
|
{
|
|
printf("GetAddrInfo failed for the local address: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Create a socket to listen on
|
|
hDiscoverySocket = socket(
|
|
pLocalAddrInfo->ai_family,
|
|
pLocalAddrInfo->ai_socktype,
|
|
pLocalAddrInfo->ai_protocol);
|
|
if (hDiscoverySocket == INVALID_SOCKET)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("Failed to create socket: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
err = JoinMulticastGroupAndSetSocketOptions(
|
|
hDiscoverySocket,
|
|
pMulticastAddrInfo,
|
|
pLocalAddrInfo);
|
|
|
|
if (0 != err)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Bind the socket to the local address
|
|
err = bind(
|
|
hDiscoverySocket,
|
|
pLocalAddrInfo->ai_addr,
|
|
(int) pLocalAddrInfo->ai_addrlen);
|
|
|
|
if (0 != err)
|
|
{
|
|
err = WSAGetLastError();
|
|
printf("bind failed: %d\n",err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Create hStopEvent
|
|
hStopEvent = CreateEvent(
|
|
NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (!hStopEvent)
|
|
{
|
|
err = GetLastError();
|
|
printf("CreateEvent failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Start RecvThread
|
|
RecvThreadInfo.hDiscoverySocket = hDiscoverySocket;
|
|
RecvThreadInfo.hStopEvent = hStopEvent;
|
|
RecvThreadInfo.pDeviceId = &sDeviceId;
|
|
RecvThreadInfo.pDeviceInfo = &DeviceInfo;
|
|
|
|
hRecvThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
RecvThread,
|
|
&RecvThreadInfo,
|
|
0,
|
|
NULL);
|
|
|
|
if (!hRecvThread)
|
|
{
|
|
err = GetLastError();
|
|
printf("CreateThread failed: %d\n", err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Process input
|
|
while (true)
|
|
{
|
|
ch = _getwch();
|
|
switch (ch)
|
|
{
|
|
case L'q':
|
|
case L'Q':
|
|
StopRecv(
|
|
hStopEvent,
|
|
hRecvThread);
|
|
goto Cleanup;
|
|
case L'h':
|
|
case L'H':
|
|
SendHello(
|
|
hDiscoverySocket,
|
|
sDeviceId,
|
|
DeviceInfo,
|
|
pMulticastAddrInfo);
|
|
break;
|
|
case L'b':
|
|
case L'B':
|
|
SendBye(
|
|
hDiscoverySocket,
|
|
sDeviceId,
|
|
pMulticastAddrInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if (pMulticastAddrInfo)
|
|
{
|
|
FreeAddrInfo(pMulticastAddrInfo);
|
|
}
|
|
if (pLocalAddrInfo)
|
|
{
|
|
FreeAddrInfo(pLocalAddrInfo);
|
|
}
|
|
if (hDiscoverySocket != INVALID_SOCKET)
|
|
{
|
|
closesocket(hDiscoverySocket);
|
|
}
|
|
if (hStopEvent)
|
|
{
|
|
CloseHandle(hStopEvent);
|
|
}
|
|
if (hRecvThread)
|
|
{
|
|
CloseHandle(hRecvThread);
|
|
}
|
|
WSACleanup();
|
|
|
|
|
|
return err;
|
|
} // wmain
|
|
|