AgileReference::AgileReference(REFIID aIid, IUnknown* aObject)
  : mIid(aIid)
  , mGitCookie(0)
{
  /*
   * There are two possible techniques for creating agile references. Starting
   * with Windows 8.1, we may use the RoGetAgileReference API, which is faster.
   * If that API is not available, we fall back to using the Global Interface
   * Table.
   */
  static const DynamicallyLinkedFunctionPtr<decltype(&::RoGetAgileReference)>
    pRoGetAgileReference(L"ole32.dll", "RoGetAgileReference");

  MOZ_ASSERT(aObject);

  if (pRoGetAgileReference &&
      SUCCEEDED(pRoGetAgileReference(AGILEREFERENCE_DEFAULT, aIid, aObject,
                                     getter_AddRefs(mAgileRef)))) {
    return;
  }

  IGlobalInterfaceTable* git = ObtainGit();
  MOZ_ASSERT(git);
  if (!git) {
    return;
  }

  DebugOnly<HRESULT> hr = git->RegisterInterfaceInGlobal(aObject, aIid,
                                                         &mGitCookie);
  MOZ_ASSERT(SUCCEEDED(hr));
}
PyObject *PyIGlobalInterfaceTable::GetInterfaceFromGlobal(PyObject *self, PyObject *args)
{
	PyObject *obriid = NULL;
	DWORD dwCookie;
	IID riid;
	void* ppv;
	HRESULT hr;

	IGlobalInterfaceTable *pIGIT = GetI(self);
	if ( pIGIT == NULL )
	{
		return NULL;
	}
	if (!PyArg_ParseTuple(args, "lO:GetInterfaceFromGlobal", &dwCookie, &obriid) )
	{
		return NULL;
	}
	BOOL bPythonIsHappy = TRUE;
	if (!PyWinObject_AsIID(obriid, &riid)) bPythonIsHappy = FALSE;
	
	if(!bPythonIsHappy)
	{
		return NULL;
	}
	PY_INTERFACE_PRECALL;
	hr = pIGIT->GetInterfaceFromGlobal( dwCookie, riid, &ppv );
	PY_INTERFACE_POSTCALL;
	if ( FAILED(hr) )
	{
		return PyCom_BuildPyException(hr, pIGIT, IID_IGlobalInterfaceTable );
	}
	return PyCom_PyObjectFromIUnknown((IUnknown*)ppv, riid, FALSE);
}
AgileReference::~AgileReference()
{
  if (!mGitCookie) {
    return;
  }

  IGlobalInterfaceTable* git = ObtainGit();
  MOZ_ASSERT(git);
  if (!git) {
    return;
  }

  DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mGitCookie);
  MOZ_ASSERT(SUCCEEDED(hr));
}
HRESULT
AgileReference::Resolve(REFIID aIid, void** aOutInterface)
{
  MOZ_ASSERT(aOutInterface);
  MOZ_ASSERT(mAgileRef || mGitCookie);

  if (!aOutInterface) {
    return E_INVALIDARG;
  }

  *aOutInterface = nullptr;

  if (mAgileRef) {
    // IAgileReference lets you directly resolve the interface you want...
    return mAgileRef->Resolve(aIid, aOutInterface);
  }

  if (!mGitCookie) {
    return E_UNEXPECTED;
  }

  IGlobalInterfaceTable* git = ObtainGit();
  MOZ_ASSERT(git);
  if (!git) {
    return E_UNEXPECTED;
  }

  RefPtr<IUnknown> originalInterface;
  HRESULT hr = git->GetInterfaceFromGlobal(mGitCookie, mIid,
                                           getter_AddRefs(originalInterface));
  if (FAILED(hr)) {
    return hr;
  }

  if (aIid == mIid) {
    originalInterface.forget(aOutInterface);
    return S_OK;
  }

  // ...Whereas the GIT requires us to obtain the same interface that we
  // requested and then QI for the desired interface afterward.
  return originalInterface->QueryInterface(aIid, aOutInterface);
}
PyObject *PyIGlobalInterfaceTable::RegisterInterfaceInGlobal(PyObject *self, PyObject *args)
{
	PyObject *obpUnk = NULL;
	PyObject *obriid = NULL;
	IUnknown * pUnk;
	IID riid;
	DWORD pdwCookie;
	PyObject *ppyobRetval = NULL;
	HRESULT hr;

	IGlobalInterfaceTable *pIGIT = GetI(self);
	if ( pIGIT == NULL )
	{
		return NULL;
	}
	if (!PyArg_ParseTuple(args, "OO:RegisterInterfaceInGlobal", &obpUnk, &obriid) )
	{
		return NULL;
	}
	BOOL bPythonIsHappy = TRUE;
	if (!PyCom_InterfaceFromPyInstanceOrObject(obpUnk, IID_IUnknown, (void **)&pUnk, TRUE /* bNoneOK */))
		 bPythonIsHappy = FALSE;
	if (!PyWinObject_AsIID(obriid, &riid)) 
		bPythonIsHappy = FALSE;
	if(!bPythonIsHappy)
	{
		return NULL;
	}
	PY_INTERFACE_PRECALL;
	hr = pIGIT->RegisterInterfaceInGlobal( pUnk, riid, &pdwCookie );
	if (pUnk) pUnk->Release();
	PY_INTERFACE_POSTCALL;
	if ( FAILED(hr) )
	{
		return PyCom_BuildPyException(hr, pIGIT, IID_IGlobalInterfaceTable );
	}
	ppyobRetval = Py_BuildValue("l", pdwCookie);
	return ppyobRetval;
}
PyObject *PyIGlobalInterfaceTable::RevokeInterfaceFromGlobal(PyObject *self, PyObject *args)
{
	DWORD dwCookie;
	HRESULT hr;

	IGlobalInterfaceTable *pIGIT = GetI(self);
	if ( pIGIT == NULL )
	{
		return NULL;
	}
	if (!PyArg_ParseTuple(args, "l:RevokeInterfaceFromGlobal", &dwCookie) )
	{
		return NULL;
	}
	PY_INTERFACE_PRECALL;
	hr = pIGIT->RevokeInterfaceFromGlobal( dwCookie );
	PY_INTERFACE_POSTCALL;
	if ( FAILED(hr) )
	{
		return PyCom_BuildPyException(hr, pIGIT, IID_IGlobalInterfaceTable );
	}
	Py_INCREF(Py_None);
	return Py_None;
}