2012-12-09 59 views
6

設備:Windows7的上如何訪問UPnP設備的服務?

MS VC++ 2010我試圖枚舉使用C++從Windows UPnP設備的服務:貝爾金微蒙開關
開發環境。

我有IUPnPDevice指針,可以訪問多個屬性。
我有IUPnPServices指針,可以計算正確的服務數(7)。
我使用QueryInterface()來得到IEnumVARIANT指針(它似乎成功了)。
但是,Next()方法始終會失敗,並且HRESULT的值爲0x80040500 - 這會翻譯爲Windows error 1280 (0x500) - ERROR_ALREADY_FIBER
這個錯誤對我沒有任何意義。

(我同時使用IEnumVARIANTIEnumUnknown嘗試 - 因爲文檔表明它可以是,但都產生相同的結果)

我已經包含了完整的源文件下,再加上其輸出產生。
[注意:這是硬編碼使用我自己的設備的udn]

我會很感激,如果任何人都可以幫助,因爲我目前卡住了。

此致
戴夫

代碼:

// UpnpTest1.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

#include <windows.h> 
#include <upnp.h> 

static void DumpComError(const TCHAR *api, HRESULT hr); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int retcode=-1; // assume failure 

    HRESULT hr = CoInitialize(0); 
    if (hr==S_OK) 
    { 
    IUPnPDeviceFinder *deviceFinder=0; 
    hr = CoCreateInstance(CLSID_UPnPDeviceFinder, 0, CLSCTX_INPROC_SERVER, IID_IUPnPDeviceFinder, (void**)&deviceFinder); 
    if (hr==S_OK) 
    { 
     IUPnPDevice *device=0; 
     hr = deviceFinder->FindByUDN(L"uuid:Socket-1_0-221239K11002F6", &device); 
     if (hr==S_OK) 
     { 
     if (device) 
     { 
      TCHAR *manufacturer=0, *manufacturerUrl=0; 
      TCHAR *description=0, *name=0, *modelUrl=0; 
      TCHAR *serialNumber=0, *udn=0, *upc=0, *deviceType=0; 
      TCHAR *presentationUrl=0; 

      device->get_ManufacturerName(&manufacturer); 
      device->get_ManufacturerURL(&manufacturerUrl); 
      device->get_Description(&description); 
      device->get_FriendlyName(&name); 
      device->get_ModelURL(&modelUrl); 
      device->get_SerialNumber(&serialNumber); 
      device->get_UniqueDeviceName(&udn); 
      device->get_UPC(&upc); 
      device->get_Type(&deviceType); 
      device->get_PresentationURL(&presentationUrl); 

      _tprintf(_T("MANUFACTURER: %s [%s]\n"), manufacturer, manufacturerUrl); 
      _tprintf(_T("MODEL:  %s [%s]\n    [%s]\n"), description, name, modelUrl); 
      _tprintf(_T("DEVICE:  serial=%s\n    udn=%s\n    upc=%s\n    type=%s\n"), serialNumber, udn, upc, deviceType); 
      _tprintf(_T("URL:   %s\n"), presentationUrl); 

      IUPnPServices *services=0; 
      hr = device->get_Services(&services); 
      if (hr==S_OK) 
      { 
      if (services) 
      { 
       long numberOfServices=0; 
       services->get_Count(&numberOfServices); 

       if (numberOfServices>0) 
       { 
       IUnknown *unknown=0; 
       hr = services->get__NewEnum(&unknown); 
       if (hr==S_OK) 
       { 
        if (unknown) 
        { 
        IEnumVARIANT *enumInterface=0; 
        hr = unknown->QueryInterface(IID_IEnumVARIANT,(void**)&enumInterface); 
        if (enumInterface) 
        { 
         VARIANT var; 
         unsigned long fetched=0; 
         hr = enumInterface->Next(1, &var, &fetched); 

         if (hr==S_OK) 
         { 

         } 
         else 
         DumpComError(_T("IEnumVARIANT::Next"), hr); 
        } 
        else 
         DumpComError(_T("IUnknown::QueryInterface"), hr); 
        } 
        else 
        fprintf(stderr, "Failed to get enumeration interface.\n"); 
       } 
       else 
        DumpComError(_T("IUPnPServices::get__NewEnum"), hr); 
       } 
       else 
       fprintf(stderr, "No services available.\n"); 
      } 
      else 
       fprintf(stderr, "Failed to get services collection.\n"); 
      } 
      else 
      DumpComError(_T("IUPnPDevice::get_Services"), hr); 
     } 
     else 
      fprintf(stderr, "Device not found.\n"); 
     } 
     else 
     DumpComError(_T("IUPnPDeviceFinder::FindByUDN"), hr); 
    } 
    else 
     DumpComError(_T("CoCreateIndex"), hr); 
    } 
    else 
    DumpComError(_T("CoInitialize"), hr); 

    return retcode; 
} 

static void AddBoolToString(const TCHAR *name, bool value, TCHAR *buf, int &i, int max) 
{ 
    if (name && *name && value && buf && i>=0) 
    i += _snwprintf_s(&buf[i], max-i, (max-i-1)*sizeof(TCHAR), _T("%s%s=YES"), (i>0? _T("; "): _T("")), name); 
} 

static void AddIntToString(const TCHAR *name, int value, TCHAR *buf, int &i, int max) 
{ 
    if (name && *name && value && buf && i>=0) 
    i += _snwprintf_s(&buf[i], max-i, (max-i-1)*sizeof(TCHAR), _T("%s%s=%d"), (i>0? _T("; "): _T("")), name, value); 
} 

static void DumpComError(const TCHAR *api, HRESULT hr) 
{ 
    bool failure = (hr&0x80000000? true: false); 
    bool severe = (hr&0x40000000? true: false); 
    bool microsoft = (hr&0x20000000? false: true); 
    bool ntStatus = (hr&0x10000000? true: false); 
    bool xBit  = (hr&0x08000000? true: false); 
    int facility = (hr&0x07FF0000)>>16; 
    int code  = (hr&0x0000FFFF); 

    TCHAR buf[1024]={0}; 
    int bufsize = sizeof(buf)/sizeof(TCHAR); 
    int i=0; 

    AddBoolToString(_T("failure"), failure, buf, i, bufsize); 
    AddBoolToString(_T("severe"), severe, buf, i, bufsize); 
    AddBoolToString(_T("microsoft"), microsoft, buf, i, bufsize); 
    AddBoolToString(_T("ntStatus"), ntStatus, buf, i, bufsize); 
    AddBoolToString(_T("xBit"), xBit, buf, i, bufsize); 
    AddIntToString(_T("facility"), facility, buf, i, bufsize); 
    AddIntToString(_T("code"), code, buf, i, bufsize); 

    _ftprintf(stderr, _T("\n%s() failed, hr=0x%08x\n[%s]\n"), api, hr, buf); 
} 

輸出: 它產生以下輸出:

MANUFACTURER: Belkin International Inc. [http://www.belkin.com/] 
MODEL:  Belkin Plugin Socket 1.0 [WeMo Switch] 
       [http://www.belkin.com/plugin/] 
DEVICE:  serial=221239K11002F6 
       udn=uuid:Socket-1_0-221239K11002F6 
       upc=123456789 
       type=urn:Belkin:device:controllee:1 
URL:   http://192.168.1.16:49153/pluginpres.html 

IEnumVARIANT::Next() failed, hr=0x80040500 
[failure=YES; microsoft=YES; facility=4; code=1280] 

編輯:

經過很多死鎖後,我設法通過手動構建SOAP請求並使用Windows套接字通過TCP發送請求來實現此目的。棘手的一點是得到了正確的語法,因爲我以前沒有使用SOAP的經驗。 [UPnP對識別IP地址&端口號很有用 - 因爲這些端口號可以更改]。一旦啓動並運行 - 實際上比UPnP界面簡單得多。讓我知道,如果你有興趣,我可以發佈代碼...它不直接回答我在這裏提出的問題,所以用這個細節回答我的問題是沒有意義的。

但是,如果您有興趣,請告訴我,我可以發佈代碼。

乾杯,
戴夫

+0

注意:從本頁[http://www.issackelly.com/blog/2012/07/30/wemo-hacking/]可以看到服務列表,因此理論上我應該能夠訪問服務直接使用IUPnPServices :: get_Item()然而,我無法弄清楚什麼是正確的格式serviceId(第一個參數)... – user390935

+0

注2:我試過使用IUPnPService :: get_Item(L「urn:Belkin:serviceId:basicevent1 「,&serviceId);但是返回的HRESULT與以前一樣(0x80040500) – user390935

+0

是@ user390935,發佈您的代碼。我正在尋找平臺中立的SSDP客戶端。 – Vink

回答

2

的0x80040500 HRESULT是不是你的想法,但UPNP_E_INVALID_DOCUMENT。有關這種模糊可能性的解釋,請參閱my answer in another SO question

我的猜測是您的貝爾金設備提供不符合設備描述或服務描述的XML。不符合不一定意味着格式不對,UPnP規範有大量的二級要求。嘗試使用英特爾開發人員工具中的Device Spy(鏈接在其他答案的底部),如果設備彈出,則從其上的相同套件運行Device Validator。

+0

非常感謝...我應該知道Facility是4.我想這意味着Windows不喜歡從設備迴應。 – user390935

+0

是的,這是我的猜測。 HRESULT是20年來與OS/2兼容的可怕遺產。對於所有「用戶自定義代碼」,只有一個FACILITY_ITF本身已經夠糟糕了,但微軟正在使用它自己的新API的相同工具,它可能使用了另一個未使用的代碼。 –

0

我的經驗是類似的,因爲UPnPDeviceFinder根本不起作用。它從不發送UPnP搜索數據包,所以設備不響應​​。要使其起作用的唯一方法是,如果您還使用Windows Media Player或「Cast To Device」菜單(即WMP)啓動搜索。因爲它是UPnPDeviceFinder將返回一些設備,只有當他們碰巧在那個時候廣播,但即使找到一個XBox(另一個微軟產品)在沒有其他活動的情況下在示例中不起作用。