none
Persistence of handle to top-level collection and of preparsed data RRS feed

  • General discussion

  • Short description:

    My code constantly queries a set of top-level collections for their capabilities. This is a slow process. The bottleneck is SetupDiGetClassDevs() which is very slow. I wonder if I can save the calculated preprocessed data (per  top-level collection) and when to invalidate it.

    Discussion:

    I provide an API for users to access my device driver. Theoretically, as long as the device is connected, the data acquired from it cannot change, so there's no point in re-reading it.

    However, I cannot limit the number of times the API users can call the interface functions that query the device. If they call it repeatedly their application becomes slow. Very slow. The solution is probably to cache the data in the API layer. But what data should I cache, and when should the API invalidate the data?

    Handle to top-level collection:

    I could save a handle for every top-level collection. I will have to close it when the device is removed. What happens if some other application tries to open a handle to the same collection? What if the user rearranges the order of devices (e.g. changing the "Preferred Device")?

    Preparsed data:

    Preparsed data is obtained by calling HidD_GetPreparsedData() and should be freed by calling HidD_FreePreparsedData(). It seems to be a good candidate to be stored. However, I cannot find information about its dependence on external data structures and about it's consistency. In addition, It is stated that you must free it once you don't need it. What happens if I keep it till the next time the device is removed?

    Tuesday, June 2, 2015 6:19 AM

All replies

  • As you mentioned, you should be able to keep the handle cached and open until the TLC is removed. I didn't quite understand the user re-arranging the order of devices... can you elaborate on what you mean?

    The data returned by HidD_GetPreparsedData is pretty static. It should remain valid as long as the handle is valid unless you call HidD_FreePreparsedData.

    Thanks,

    Vivek

     
    Wednesday, June 3, 2015 5:53 PM
  • Hi Vivek,

    Let me first explain the device-rearrangement point.

    Many games that employ joystick do not let the user select a joystick in case there are more than one. Rather, they take one of the installed joystick devices. Usually the "preferred" (=default) device. To overcome this problem, you can manipulate the joystick's identification number. You do it with a few DirectInput methods (such as IDirectInputJoyConfig8::SetConfig) as in the following function:

    // -----------------------------------------------------------
    
    bool CDirectInputConfig::MoveFromTo(int nFrom, int nTo)
    {
      HRESULT hRes = E_UNEXPECTED;
    
      if (!m_bIsWin2K)
      {                                                // Win9X version...
        const DWORD dwGetConfigFlags = DIJC_GUIDINSTANCE | DIJC_REGHWCONFIGTYPE | DIJC_GAIN | DIJC_CALLOUT;
        const DWORD dwSetConfigFlags = DIJC_REGHWCONFIGTYPE | DIJC_GAIN | DIJC_CALLOUT;
    
        DIJOYCONFIG JoyConfig;                         // declare then initalize fields etc.
        ZeroMemory(&JoyConfig, sizeof(JoyConfig));
        JoyConfig.dwSize = sizeof(JoyConfig);
    
        hRes = m_pdiJoyConfig->Acquire();              // get info about "to" id
        hRes = m_pdiJoyConfig->GetConfig(nTo, &JoyConfig, dwGetConfigFlags);
    
        if (S_FALSE != hRes)                           // clear it if "problem?"
        {
          hRes = m_pdiJoyConfig->DeleteConfig(nTo);
          hRes = m_pdiJoyConfig->SendNotify();
        }
                                                       // get info about "from" id
        hRes = m_pdiJoyConfig->GetConfig(nFrom, &JoyConfig, dwGetConfigFlags);
    
        if (JoyConfig.hwc.hws.dwFlags & JOY_HWS_AUTOLOAD)
        {                                               // this is a HID device
          hRes = m_pdiJoyConfig->SetConfig(nTo, &JoyConfig, dwSetConfigFlags);
          hRes = m_pdiJoyConfig->SendNotify();
          hRes = m_pdiJoyConfig->DeleteConfig(nFrom);
          hRes = m_pdiJoyConfig->SendNotify();
        }
        else
        {                                               // regular (or no) device
          hRes = m_pdiJoyConfig->DeleteConfig(nFrom);
          hRes = m_pdiJoyConfig->SendNotify();
          if ((0 == JoyConfig.hwc.hws.dwFlags) &&
    	  (0 == JoyConfig.hwc.hws.dwNumButtons))
          {                                             // we have an empty device.
            hRes = m_pdiJoyConfig->DeleteConfig(nTo);   // Rather than SetConfig(), do DeleteConfig()
          }
          else
          {
            hRes = m_pdiJoyConfig->SetConfig(nTo, &JoyConfig, dwSetConfigFlags);
          }
        }
        hRes = m_pdiJoyConfig->Unacquire();
      }
      else                                              // Windows 2000 switch functions...
      {
        DIJOYCONFIG jcSrc, jcDst;                       // declare then initalize fields etc.
    //    ZeroMemory(&jcSrc, sizeof(jcDst));            // doesn't seem right
                                                        // changed to two following lines
        ZeroMemory(&jcSrc, sizeof(jcSrc));
        ZeroMemory(&jcDst, sizeof(jcDst));
    
        jcSrc.dwSize = sizeof(jcSrc);                   // set the sizeof structure fields
        jcDst.dwSize = sizeof(jcDst);
    
        DWORD dwSetFlags = DIJC_GUIDINSTANCE | DIJC_REGHWCONFIGTYPE | DIJC_GAIN | DIJC_CALLOUT | DIJC_WDMGAMEPORT;
        DWORD dwGetFlags = dwSetFlags;
    
        hRes = m_pdiJoyConfig->Acquire();                // get info about "from" id
        hRes = m_pdiJoyConfig->GetConfig(nFrom, &jcSrc, dwGetFlags);
        if (S_OK != hRes)
        {
          hRes = m_pdiJoyConfig->Unacquire();
        }
                                                         // get info about "to" id
        hRes = m_pdiJoyConfig->GetConfig(nTo, &jcDst, dwGetFlags);
        if (SUCCEEDED(hRes))
        {
          hRes = m_pdiJoyConfig->GetConfig(nFrom, &jcSrc, dwGetFlags);
          hRes = m_pdiJoyConfig->SetConfig(nFrom, &jcDst, dwSetFlags);
          hRes = m_pdiJoyConfig->SetConfig(nTo, &jcSrc, dwSetFlags);
        }
        else
        {                                                 // destination empty
          hRes = m_pdiJoyConfig->GetConfig(nFrom, &jcSrc, dwGetFlags);
          hRes = m_pdiJoyConfig->DeleteConfig(nTo);
          hRes = m_pdiJoyConfig->SetConfig(nTo, &jcSrc, dwSetFlags);
        }
        hRes = m_pdiJoyConfig->SendNotify();
        hRes = m_pdiJoyConfig->Unacquire();
      }
      return SUCCEEDED(hRes);
    }
    
    // -----------------------------------------------------------
    

    I was wondering if applying this function by an external application will render the cached Preparsed Data invalid.

    I have already started testing with cached Preparsed Data. On the one hand, the code is functional. On the other hand I get some rare crashes when trying to free  (HidD_FreePreparsedData()) the data. The allocated heap seems to be corrupt. I am not sure it has to do with the issue at hand. I suspect I'm trying to free stale data.

    There's one point in your answer I'd like to you to clarify:

    "It should remain valid as long as the handle is valid  ..."

    Do you mean that I should not close the handle? I currently close it once I save the Preparsed Data and it seems to work.

    Regards

      Shaul Eizikovich

    Wednesday, June 3, 2015 6:59 PM