//+--------------------------------------------------------------------------- // // 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; iOpcOperations; 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; irgArgs[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; }