493 lines
13 KiB
C++
493 lines
13 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: C O N T R O L . C P P
|
|
//
|
|
// Contents: Functions dealing with UPnP service control
|
|
//
|
|
// Notes:
|
|
//
|
|
// Author: danielwe 28 Oct 1999
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "ncbase.h"
|
|
#include "oleauto.h"
|
|
#include "updiagp.h"
|
|
#include "media.h"
|
|
#include "ncinet.h"
|
|
#include "util.h"
|
|
|
|
extern const STANDARD_OPERATION_LIST c_Ops;
|
|
|
|
//
|
|
// Global service control structure for demo services
|
|
//
|
|
extern const DEMO_SERVICE_CTL c_rgSvc[] =
|
|
{
|
|
{
|
|
TEXT("app"),
|
|
6,
|
|
{
|
|
{ TEXT("VolumeUp"), Val_VolumeUp, Do_VolumeUp },
|
|
{ TEXT("VolumeDown"), Val_VolumeDown, Do_VolumeDown },
|
|
{ TEXT("SetVolume"), Val_SetVolume, Do_SetVolume },
|
|
{ TEXT("Mute"), Val_Mute, Do_Mute },
|
|
{ TEXT("Power"), Val_Power, Do_Power },
|
|
{ TEXT("LoadFile"), Val_LoadFile, Do_LoadFile },
|
|
},
|
|
},
|
|
{
|
|
TEXT("xport"),
|
|
4,
|
|
{
|
|
{ TEXT("Play"), Val_Play, Do_Play },
|
|
{ TEXT("Stop"), Val_Stop, Do_Stop },
|
|
{ TEXT("Pause"), Val_Pause, Do_Pause },
|
|
{ TEXT("SetPos"), Val_SetPos, Do_SetPos },
|
|
}
|
|
},
|
|
{
|
|
TEXT("clock"),
|
|
1,
|
|
{
|
|
{ TEXT("SetDateTime"), Val_SetTime, Do_SetTime },
|
|
}
|
|
},
|
|
};
|
|
|
|
extern const DWORD c_cDemoSvc = celems(c_rgSvc);
|
|
|
|
ACTION *PActFromSz(ACTION_SET * pActionSet, LPCTSTR szAction)
|
|
{
|
|
for (DWORD iAction = 0; iAction < pActionSet->cActions; iAction++)
|
|
{
|
|
if (!_tcsicmp(pActionSet->rgActions[iAction].szActionName, szAction))
|
|
return &pActionSet->rgActions[iAction];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
const DEMO_ACTION *PDemoActFromSz(const DEMO_SERVICE_CTL *psvc, LPCTSTR szAction)
|
|
{
|
|
DWORD iAction;
|
|
|
|
Assert(psvc);
|
|
|
|
for (iAction = 0; iAction < psvc->cActions; iAction++)
|
|
{
|
|
if (!_tcsicmp(psvc->rgActions[iAction].szAction, szAction))
|
|
return &psvc->rgActions[iAction];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
UPNPSVC *PSvcFromIdDev(UPNPDEV *pdev, LPCTSTR szId)
|
|
{
|
|
DWORD isvc;
|
|
DWORD idev;
|
|
UPNPSVC * psvc;
|
|
|
|
// First look thru all local services
|
|
//
|
|
for (isvc = 0; isvc < pdev->cSvcs; isvc++)
|
|
{
|
|
psvc = pdev->rgSvcs[isvc];
|
|
|
|
if (!_tcsicmp(psvc->szControlId, szId))
|
|
{
|
|
return psvc;
|
|
}
|
|
}
|
|
|
|
// If not found there, recurse for each sub-device
|
|
//
|
|
for (idev = 0; idev < pdev->cDevs; idev++)
|
|
{
|
|
psvc = PSvcFromIdDev(pdev->rgDevs[idev], szId);
|
|
if (psvc)
|
|
{
|
|
return psvc;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: PSvcFromId
|
|
//
|
|
// Purpose: Given a service identifier, return the service for which a
|
|
// valid control handler exists
|
|
//
|
|
// Arguments:
|
|
// szId [in] Service identifier
|
|
//
|
|
// Returns: Matching service
|
|
//
|
|
// Author: danielwe 6 Nov 1999
|
|
//
|
|
// Notes:
|
|
//
|
|
UPNPSVC *PSvcFromId(LPCTSTR szId)
|
|
{
|
|
DWORD idev;
|
|
|
|
// Loop thru all devices and all services within those devices looking
|
|
// for a service that implements the control handler identified by szId
|
|
//
|
|
for (idev = 0; idev < g_params.cCd; idev++)
|
|
{
|
|
UPNPSVC * psvc;
|
|
|
|
psvc = PSvcFromIdDev(g_params.rgCd[idev], szId);
|
|
if (psvc)
|
|
{
|
|
return psvc;
|
|
}
|
|
}
|
|
|
|
TraceTag(ttidUpdiag, "Can't find a service matching id: '%s'.", szId);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// BUGBUG
|
|
/*
|
|
DWORD ValidateActionArguments(ACTION * pAction,
|
|
g_pdata->cArgs,
|
|
(ARG *) &g_pdata->rgArgs)
|
|
{
|
|
return 1;
|
|
}
|
|
*/
|
|
|
|
const STANDARD_OPERATION * PStdOpFromSz(LPTSTR szOpName)
|
|
{
|
|
const STANDARD_OPERATION * pStdOperation = NULL;
|
|
for (DWORD iOps = 0; iOps < c_Ops.cOperations; iOps++)
|
|
{
|
|
if (!_tcsicmp(c_Ops.rgOperations[iOps].szOperation, szOpName))
|
|
{
|
|
pStdOperation = &c_Ops.rgOperations[iOps];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pStdOperation;
|
|
}
|
|
|
|
DWORD dwArgsFromOpName(LPTSTR szOpName)
|
|
{
|
|
DWORD dwArgs=0;
|
|
const STANDARD_OPERATION * pStdOperation = PStdOpFromSz(szOpName);
|
|
if (pStdOperation)
|
|
{
|
|
dwArgs = pStdOperation->nArguments;
|
|
}
|
|
|
|
return dwArgs;
|
|
}
|
|
|
|
DWORD DwPerformOperation(UPNPSVC * psvc, OPERATION_DATA * pOpData,
|
|
DWORD cArgs, ARG *rgArgs)
|
|
{
|
|
const STANDARD_OPERATION * pStdOperation = PStdOpFromSz(pOpData->szOpName);
|
|
if(pStdOperation)
|
|
{
|
|
return pStdOperation->pfnOperation(psvc, pOpData, cArgs, rgArgs);
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "Operation %s is not supported by the emulated device.",
|
|
pOpData->szOpName);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// BUGBUG: separate the demo stuff from the generic stuff
|
|
VOID PerformActionForDemoService(UPNPSVC * psvc);
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ProcessControlRequest
|
|
//
|
|
// Purpose: Main control request handler
|
|
//
|
|
// Arguments:
|
|
// (none)
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// Author: danielwe 6 Nov 1999
|
|
//
|
|
// tongl 11/20/99
|
|
// added the state table change and moved the functionality
|
|
// to play midi files to PerformActionForDemoService
|
|
//
|
|
// Notes: This ISAPICTL extension will signal an event telling us
|
|
// to process a request that it has received. This function
|
|
// determines which UPnP service the request was meant for,
|
|
// and if a control handler for that service exists,
|
|
// it calls the appropriate code to process the request
|
|
// to make service state table changes and notify user
|
|
// control points
|
|
//
|
|
//
|
|
VOID ProcessControlRequest()
|
|
{
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(g_hMutex, INFINITE))
|
|
{
|
|
DWORD dwReturn = 0;
|
|
UPNPSVC * psvc;
|
|
ACTION * pAction;
|
|
LPTSTR pszEventSource;
|
|
|
|
pszEventSource = TszFromSz(g_pdata->szEventSource);
|
|
if (pszEventSource)
|
|
{
|
|
TraceTag(ttidUpdiag, "Processing a control request for %s!",
|
|
g_pdata->szEventSource);
|
|
|
|
psvc = PSvcFromId(pszEventSource);
|
|
if (psvc)
|
|
{
|
|
LPTSTR pszAction;
|
|
|
|
// Validate arguments and set action return value
|
|
pszAction = TszFromSz(g_pdata->szAction);
|
|
if (pszAction)
|
|
{
|
|
pAction = PActFromSz(&(psvc->action_set), pszAction);
|
|
if (pAction)
|
|
{
|
|
/*
|
|
// NYI
|
|
dwReturn = ValidateActionArguments(pAction,
|
|
g_pdata->cArgs,
|
|
(ARG *) &g_pdata->rgArgs);
|
|
*/
|
|
dwReturn = 1;
|
|
if (!dwReturn)
|
|
{
|
|
TraceTag(ttidUpdiag, "Failed to validate action '%s'.",
|
|
pAction->szActionName);
|
|
}
|
|
}
|
|
|
|
delete [] pszAction;
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "ProcessControlRequest: TszFromSz failed");
|
|
}
|
|
}
|
|
|
|
g_pdata->dwReturn = dwReturn;
|
|
|
|
TraceTag(ttidUpdiag, "Signalling event for ISAPICTL to continue...");
|
|
// Release the ISAPI DLL so it can return to the UCP
|
|
SetEvent(g_hEventRet);
|
|
|
|
if (dwReturn)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwActionResult;
|
|
|
|
AssertSz(pAction, "Good return but no action?");
|
|
|
|
// Now perform the action
|
|
TraceTag(ttidUpdiag, "Performing action %s.", pAction->szActionName);
|
|
|
|
OPERATION_DATA * pOperation;
|
|
DWORD iArgs =0;
|
|
DWORD cArgs;
|
|
ARG rgArgs[MAX_PROP_CHANGES];
|
|
|
|
for (DWORD iOp=0; iOp<pAction->cOperations; iOp++)
|
|
{
|
|
pOperation = &pAction->rgOperations[iOp];
|
|
|
|
// get the arguments for this operation
|
|
cArgs = dwArgsFromOpName(pOperation->szOpName);
|
|
|
|
Assert(iArgs+cArgs <= g_pdata->cArgs);
|
|
for(DWORD i=0; i<cArgs; i++)
|
|
{
|
|
lstrcpy(rgArgs[i].szValue, g_pdata->rgArgs[iArgs].szValue);
|
|
iArgs++;
|
|
}
|
|
|
|
// perform the operation
|
|
dwActionResult = DwPerformOperation(psvc, pOperation, cArgs, rgArgs);
|
|
if (dwActionResult)
|
|
break;
|
|
}
|
|
|
|
if (!dwActionResult)
|
|
{
|
|
TraceTag(ttidUpdiag, "Successfully completed action %s.",
|
|
pAction->szActionName);
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "Failed to complete action %s.",
|
|
pAction->szActionName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "Did not perform action for %s.",
|
|
g_pdata->szEventSource);
|
|
}
|
|
|
|
if (psvc && psvc->psvcDemoCtl)
|
|
{
|
|
PerformActionForDemoService(psvc);
|
|
}
|
|
|
|
ReleaseMutex(g_hMutex);
|
|
}
|
|
|
|
delete [] pszEventSource;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: PerformActionForDemoService
|
|
//
|
|
// Purpose: Perform the action on the demo services (midi-player)
|
|
//
|
|
// Arguments:
|
|
// (none)
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// Author: danielwe 6 Nov 1999
|
|
//
|
|
// Notes: For the two demo services, this function calls the
|
|
// appropriate code to process the request.
|
|
//
|
|
VOID PerformActionForDemoService(UPNPSVC * psvc)
|
|
{
|
|
const DEMO_ACTION * pAct;
|
|
DWORD dwReturn = 0;
|
|
|
|
// Validate arguments and set action return value
|
|
LPTSTR pszAction = TszFromSz(g_pdata->szAction);
|
|
if (pszAction)
|
|
{
|
|
pAct = PDemoActFromSz(psvc->psvcDemoCtl, pszAction);
|
|
if (pAct)
|
|
{
|
|
dwReturn = pAct->pfnValidate(g_pdata->cArgs,
|
|
(ARG *) &g_pdata->rgArgs);
|
|
if (!dwReturn)
|
|
{
|
|
TraceTag(ttidUpdiag, "Failed to validate demo action '%s'.",
|
|
pAct->szAction);
|
|
}
|
|
}
|
|
|
|
delete [] pszAction;
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "PerformActionForDemoService: TszFromSz");
|
|
}
|
|
|
|
g_pdata->dwReturn = dwReturn;
|
|
|
|
TraceTag(ttidUpdiag, "Signalling event for ISAPICTL to continue...");
|
|
// Release the ISAPI DLL so it can return to the UCP
|
|
SetEvent(g_hEventRet);
|
|
|
|
if (dwReturn)
|
|
{
|
|
DWORD dwActionResult;
|
|
|
|
AssertSz(pAct, "Good return but no action?");
|
|
|
|
// Now perform the action
|
|
|
|
TraceTag(ttidUpdiag, "Performing action %s.", pAct->szAction);
|
|
|
|
dwActionResult = pAct->pfnAction(g_pdata->cArgs,
|
|
(ARG *) &g_pdata->rgArgs);
|
|
if (dwActionResult)
|
|
{
|
|
TraceTag(ttidUpdiag, "Successfully completed action %s.",
|
|
pAct->szAction);
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "Failed to complete action %s. Result = %d.",
|
|
pAct->szAction, dwActionResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceTag(ttidUpdiag, "Did not perform action for %s.",
|
|
g_pdata->szEventSource);
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: RequestHandlerThreadStart
|
|
//
|
|
// Purpose: Start routine for the request handler thread
|
|
//
|
|
// Arguments:
|
|
// pvParam []
|
|
//
|
|
// Returns: Always 0
|
|
//
|
|
// Author: danielwe 6 Nov 1999
|
|
//
|
|
// Notes:
|
|
//
|
|
DWORD WINAPI RequestHandlerThreadStart(LPVOID pvParam)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TraceTag(ttidUpdiag, "COM initialized.");
|
|
while (TRUE)
|
|
{
|
|
TraceTag(ttidUpdiag, "Awaiting control request...");
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(g_hEvent, INFINITE))
|
|
{
|
|
TraceTag(ttidUpdiag, "Event was signalled...");
|
|
// When the event is signalled, a control request is ready
|
|
ProcessControlRequest();
|
|
}
|
|
}
|
|
|
|
// Right now this will never be called because this thread never
|
|
// exits, but eventually we may want it to.
|
|
//
|
|
CoUninitialize();
|
|
}
|
|
else
|
|
{
|
|
TraceError("RequestHandlerThreadStart - CoInitializeEx", hr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|