📄 upnploader.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include <windows.h>
#include <winsock2.h>
#include <upnphost.h>
#include <linklist.h>
#include <regentry.h>
#include <ncdefine.h>
#include <ncdebug.h>
#include <service.h>
#include <upnphostkeys.h>
#include <upnpmem.h>
#include <auto_xxx.hxx>
#include <string.hxx>
#define ttidLoader 1
#define LOADER_SIGNAL_STOP 1 // registry value that tells the loader to exit
IUPnPRegistrar *g_upnpRegistrar = NULL;
PCWSTR g_pszHTMLDeviceListPath = L"\\windows\\upnp\\loader.htm";
HANDLE g_hUPnPHostEvent = NULL;
BOOL fShutdown;
LONG g_fState;
static void UpdateRunningDeviceList();
class HostedDeviceInfo
{
public:
HostedDeviceInfo(
PCWSTR pszDeviceId
) :
newState(UPNPREG_DISABLED)
{
link.Flink = link.Blink = 0;
bstrDeviceId = SysAllocString(pszDeviceId);
}
HRESULT Run(RegEntry *pDeviceKey);
HRESULT Stop();
~HostedDeviceInfo();
static void InitializeRunningDeviceList();
static void UpdateRunningDeviceList();
static void CleanupRunningDeviceList();
static BOOL CreateHTMLDeviceList();
static HostedDeviceInfo *FindRunningDevice(PCWSTR pszDeviceId);
private:
static LIST_ENTRY runningDeviceList;
LIST_ENTRY link;
BSTR bstrDeviceId;
DWORD newState;
};
LIST_ENTRY HostedDeviceInfo::runningDeviceList;
HRESULT
HostedDeviceInfo::Run(
RegEntry *pupnpDeviceKey
)
{
HRESULT hr;
PWSTR pszProgId;
ce::auto_bstr bstrInitString;
ce::auto_bstr bstrXMLDesc;
ce::auto_bstr bstrResourcePath;
DWORD dwLifetime = 0;
IUPnPDeviceControl* pdev = NULL;
IUPnPReregistrar* pupnpReregistrar = NULL;
CLSID clsid;
ce::wistring wstrXMLDesc;
ce::wistring::size_type n;
char pszHostName[256];
ce::wstring wstrHostName;
//need to make copies of the registry values because RegEntry only holds onto the last string value
pszProgId = _wcsdup(pupnpDeviceKey->GetString(UPNPPROGIDVALUE));
bstrInitString = SysAllocString(pupnpDeviceKey->GetString(UPNPINITSTRINGVALUE));
wstrXMLDesc = pupnpDeviceKey->GetString(UPNPXMLDESCVALUE);
bstrResourcePath = SysAllocString(pupnpDeviceKey->GetString(UPNPRESOURCEPATHVALUE));
dwLifetime = pupnpDeviceKey->GetNumber(UPNPLIFETIME);
if(wstrXMLDesc.empty())
{
ce::auto_hfile hFile;
hFile = CreateFile(pupnpDeviceKey->GetString(UPNPXMLDESCFILE), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile.valid())
{
DWORD cbFile = GetFileSize(hFile, NULL);
ce::string strXMLDesc;
strXMLDesc.reserve(cbFile);
if(ReadFile(hFile, strXMLDesc.get_buffer(), strXMLDesc.capacity(), &cbFile, NULL))
{
ce::MultiByteToWideChar(CP_UTF8, strXMLDesc, cbFile, &wstrXMLDesc);
}
}
}
// Get device host name
gethostname(pszHostName, sizeof(pszHostName));
ce::MultiByteToWideChar(CP_ACP, pszHostName, sizeof(pszHostName), &wstrHostName);
//
// Replace every instance of %!HOST_NAME! in the device description with the host name of the device
//
for(n = 0; ce::wstring::npos != (n = wstrXMLDesc.find(L"%!HOST_NAME!", n)); )
wstrXMLDesc.replace(n, sizeof("%!HOST_NAME!") - 1, wstrHostName);
bstrXMLDesc = SysAllocString(wstrXMLDesc);
if (pszProgId == NULL || *pszProgId == NULL || bstrXMLDesc == NULL || *bstrXMLDesc == NULL)
{
TraceTag(ttidError, "Run: Invalid parameters\n");
hr = E_INVALIDARG;
}
else if (!g_upnpRegistrar
|| FAILED(g_upnpRegistrar->QueryInterface(__uuidof(IUPnPReregistrar), (void **)&pupnpReregistrar)))
{
hr = E_FAIL;
}
else
{
hr = CLSIDFromProgID(pszProgId, &clsid);
if (FAILED(hr))
{
TraceTag(ttidError, "Failed to get clsid for %ls\n", pszProgId);
}
else
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUPnPDeviceControl), (LPVOID *)&pdev);
if (FAILED(hr))
{
TraceTag(ttidError, "Failed to create %ls\n", pszProgId);
}
else
{
if(SysStringLen(bstrDeviceId))
{
TraceTag(ttidLoader,"Loading device %ls with ProgId %ls\n",bstrDeviceId,pszProgId);
hr = pupnpReregistrar->ReregisterRunningDevice(
bstrDeviceId,
bstrXMLDesc,
pdev,
bstrInitString,
bstrResourcePath,
dwLifetime);
}
else
{
TraceTag(ttidLoader,"Loading device with ProgId %ls for the first time\n", pszProgId);
hr = g_upnpRegistrar->RegisterRunningDevice(
bstrXMLDesc,
pdev,
bstrInitString,
bstrResourcePath,
dwLifetime,
&bstrDeviceId);
if (SUCCEEDED(hr))
{
pupnpDeviceKey->SetValue(UPNPUDN, bstrDeviceId);
}
}
if (SUCCEEDED(hr))
{ // add to running device list
Assert(link.Flink == 0);
InsertHeadList(&runningDeviceList,&link);
}
}
}
if (pdev)
pdev->Release();
if (pupnpReregistrar)
pupnpReregistrar->Release();
if (pszProgId)
free (pszProgId);
return hr;
}
HRESULT HostedDeviceInfo::Stop()
{
HRESULT hr;
// we should be on the running device list
if (!link.Flink || !link.Blink)
return E_FAIL;
// need to use IUPnPRegistrar
if (!g_upnpRegistrar)
return E_FAIL;
TraceTag(ttidLoader, "Stopping device %ls\n", bstrDeviceId);
hr = g_upnpRegistrar->UnregisterDevice(bstrDeviceId, FALSE);
RemoveEntryList(&link);
link.Flink = link.Blink = 0;
return hr;
}
HostedDeviceInfo::~HostedDeviceInfo()
{
Assert(link.Flink == 0 && link.Blink == 0);
if (bstrDeviceId)
SysFreeString(bstrDeviceId);
}
void HostedDeviceInfo::InitializeRunningDeviceList()
{
InitializeListHead(&runningDeviceList);
}
void HostedDeviceInfo::UpdateRunningDeviceList()
{
HRESULT hr;
LIST_ENTRY *pListEntry = NULL;
HostedDeviceInfo *pDev = NULL;
RegEntry upnpDevicesKey(UPNPDEVICESKEY,HKEY_LOCAL_MACHINE,FALSE);
if(!upnpDevicesKey.Success())
{
TraceTag(ttidError, "%S: Unable to open registry entry [HKLM\\%S]\n", __FUNCTION__, UPNPDEVICESKEY);
return;
}
TraceTag(ttidLoader, "%S: Marking devices disabled until we read registry settings\n", __FUNCTION__);
for (pListEntry = runningDeviceList.Flink; pListEntry != &runningDeviceList; pListEntry = pListEntry->Flink)
{
pDev = CONTAINING_RECORD(pListEntry,HostedDeviceInfo,link);
pDev->newState = UPNPREG_DISABLED; // until proven otherwise
}
// enumerate subkeys
TraceTag(ttidLoader, "%S: Enumerating [HKLM\\%S] subkeys\n", __FUNCTION__, UPNPDEVICESKEY);
RegEnumSubKeys enumDevices(&upnpDevicesKey);
if(!enumDevices.Success())
{
TraceTag(ttidError, "%S: Error enumerating UpnpLoader subkeys\n", __FUNCTION__);
return;
}
while (enumDevices.Next() == 0)
{
RegEntry upnpDeviceKey(enumDevices.GetName(), upnpDevicesKey.GetKey());
if(!upnpDeviceKey.Success())
{
TraceTag(ttidError, "%S: Error reading %S from registry. Continuing.\n", __FUNCTION__, enumDevices.GetName());
continue;
}
UPNPREG_STATE state = (UPNPREG_STATE) upnpDeviceKey.GetNumber(L"State", UPNPREG_DISABLED);
pDev = FindRunningDevice(upnpDeviceKey.GetString(UPNPUDN));
// Now compare the registry state with our list and resolve the differences
// Registry: RunningDeviceList:
// DISABLED absent => OK
// STARTING absent => new device, write STARTED
// STARTED absent => new device, write STARTED
// DISABLED present => remove device
// STARTING present => restart device, write STARTED
// STARTED present => OK
if (!pDev)
{
switch (state)
{
case UPNPREG_DISABLED:
// nothing to do
break;
case UPNPREG_STARTING:
case UPNPREG_STARTED:
// new device
pDev = new HostedDeviceInfo(upnpDeviceKey.GetString(UPNPUDN));
if (pDev)
{
hr = pDev->Run(&upnpDeviceKey);
if (FAILED(hr))
{
TraceTag(ttidError, "%S: Could not start device %ls hr=%x\n",__FUNCTION__, pDev->bstrDeviceId, hr);
delete pDev;
pDev = NULL;
}
}
if (!pDev)
{
// failed to start the device
upnpDeviceKey.SetValue(L"State",UPNPREG_DISABLED);
}
else
{
pDev->newState = UPNPREG_STARTED;
upnpDeviceKey.SetValue(L"State",pDev->newState);
}
break;
}
}
else
{
// device is already in the list
pDev->newState = state;
}
}
// now go through the running devices and see if we need to stop any
TraceTag(ttidLoader, "%S: Determine whether any devices need to be stopped.\n", __FUNCTION__);
for (pListEntry = runningDeviceList.Flink; pListEntry != &runningDeviceList; )
{
pDev = CONTAINING_RECORD(pListEntry,HostedDeviceInfo,link);
pListEntry = pListEntry->Flink;
switch (pDev->newState)
{
case UPNPREG_STARTING:
{
TraceTag(ttidLoader,"%S: Stopping and restarting [%ls]\n", __FUNCTION__, pDev->bstrDeviceId);
// anomalous. Handle by stopping and then restarting
RegEntry upnpDeviceKey(pDev->bstrDeviceId, upnpDevicesKey.GetKey());
pDev->Stop(); // removes the device from the running list
// run will re-add the device to the list we are traversing but this should not be a problem
hr = pDev->Run(&upnpDeviceKey);
if(FAILED(hr))
{
TraceTag(ttidError, "%S: Error restarting device [%ls]\n", __FUNCTION__, pDev->bstrDeviceId);
delete pDev;
}
break;
}
case UPNPREG_STARTED:
// nothing to do
break;
case UPNPREG_DISABLED:
default:
// stop the device. The unregister is gratuitous since somebody has probably already
// called Unregister to cause the registry key to go away.
TraceTag(ttidLoader, "%S: Unregistering device [%ls]\n", __FUNCTION__, pDev->bstrDeviceId);
hr = pDev->Stop();
if(FAILED(hr))
{
TraceTag(ttidError, "%S: Failed to unregister device [%ls]. May already be unregistered.\n", __FUNCTION__, pDev->bstrDeviceId);
}
delete pDev;
break;
}
}
CreateHTMLDeviceList();
TraceTag(ttidLoader, "%S: Updated UpnpLoader running devices list\n", __FUNCTION__);
}
void HostedDeviceInfo::CleanupRunningDeviceList()
{
HRESULT hr;
LIST_ENTRY *pListEntry;
HostedDeviceInfo *pDev;
// now go through the running devices and see if we need to stop any
for (pListEntry = runningDeviceList.Flink; pListEntry != &runningDeviceList; )
{
pDev = CONTAINING_RECORD(pListEntry,HostedDeviceInfo,link);
hr = pDev->Stop();
TraceTag(ttidInit, "Stop() returned %x\n", hr);
delete pDev;
pListEntry = runningDeviceList.Flink;
}
}
BOOL
HostedDeviceInfo::CreateHTMLDeviceList()
{
HANDLE hFile = INVALID_HANDLE_VALUE;
LIST_ENTRY *pListEntry;
HostedDeviceInfo *pDev;
BOOL fRet = FALSE;
DWORD dwWritten;
PCSTR pszHTMLHeader = "<html>\r\n"
"<head><title>Registered UPnP Devices</title></head>\r\n"
"<h2>Hosted Devices</h2>\r\n";
PCSTR pszHTMLTrailer = "</html>\r\n";
CHAR szBuffer[1024];
PWSTR pszTmpPath = new WCHAR [wcslen(g_pszHTMLDeviceListPath)+5];
if (!pszTmpPath)
return FALSE;
wcscpy(pszTmpPath,g_pszHTMLDeviceListPath);
wcscat(pszTmpPath,L".bak"); // append .bak to create the temp file name
hFile = CreateFileW(pszTmpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
goto Cleanup;
fRet = WriteFile(hFile, pszHTMLHeader, strlen(pszHTMLHeader), &dwWritten,NULL);
if (!fRet)
goto Cleanup;
for (pListEntry = runningDeviceList.Flink; pListEntry != &runningDeviceList; pListEntry = pListEntry->Flink)
{
pDev = CONTAINING_RECORD(pListEntry,HostedDeviceInfo,link);
sprintf(szBuffer,"<p>%ls",pDev->bstrDeviceId);
fRet = WriteFile(hFile, szBuffer,strlen(szBuffer),&dwWritten, NULL);
if (!fRet)
break;
}
fRet = WriteFile(hFile, pszHTMLTrailer, strlen(pszHTMLTrailer), &dwWritten, NULL);
if (!fRet)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -