520 lines
11 KiB
C++
520 lines
11 KiB
C++
|
/** (c) Nullsoft, Inc. C O N F I D E N T I A L
|
||
|
** Filename:
|
||
|
** Project:
|
||
|
** Description:
|
||
|
** Author: Ben Allison benski@nullsoft.com
|
||
|
** Created:
|
||
|
**/
|
||
|
#include "Main.h"
|
||
|
#include "ExternalCOM.h"
|
||
|
#include "BrowserCOM.h"
|
||
|
#include "CurrentSongCOM.h"
|
||
|
#include "SkinCOM.h"
|
||
|
#include "ApplicationCOM.h"
|
||
|
#include "BookmarksCOM.h"
|
||
|
#include "MediaCoreCOM.h"
|
||
|
#include "DataStoreCOM.h"
|
||
|
#include "JNetCOM.h"
|
||
|
#include "SecurityCOM.h"
|
||
|
#include "../nu/ConfigCOM.h"
|
||
|
#include "../nu/AutoChar.h"
|
||
|
#include "../nu/AutoCharFn.h"
|
||
|
#include "./Winamp/JSAPI.h"
|
||
|
#include "JSAPI2_ExternalObject.h"
|
||
|
#include "JSAPI2_Security.h"
|
||
|
|
||
|
static volatile LONG unique_dispid;
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
DISPID_CONFIG = 0,
|
||
|
DISPID_JNET = 1,
|
||
|
INITIAL_DISPID = 776,
|
||
|
};
|
||
|
|
||
|
static ExternalCOM *externalCOM = NULL;
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_Initialize()
|
||
|
{
|
||
|
if (NULL != externalCOM)
|
||
|
return S_FALSE;
|
||
|
|
||
|
ExternalCOM *temp = new ExternalCOM();
|
||
|
if (NULL == temp)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
externalCOM = temp;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_Uninitialize()
|
||
|
{
|
||
|
if (NULL == externalCOM)
|
||
|
return S_FALSE;
|
||
|
|
||
|
ExternalCOM *temp = externalCOM;
|
||
|
externalCOM = NULL;
|
||
|
temp->Release();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_GetExternal(ExternalCOM **instance)
|
||
|
{
|
||
|
if (NULL == instance)
|
||
|
return E_POINTER;
|
||
|
|
||
|
if (NULL == externalCOM)
|
||
|
{
|
||
|
*instance = NULL;
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
|
||
|
externalCOM->AddRef();
|
||
|
*instance = externalCOM;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_GetSkinCOM(SkinCOM **instance)
|
||
|
{
|
||
|
ExternalCOM *external = 0;
|
||
|
HRESULT hr = JSAPI1_GetExternal(&external);
|
||
|
if (SUCCEEDED(hr) && external)
|
||
|
{
|
||
|
hr = external->GetSkinCOM(instance);
|
||
|
external->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (NULL == instance) hr = E_POINTER;
|
||
|
else *instance = NULL;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_GetMediaCoreCOM(MediaCoreCOM **instance)
|
||
|
{
|
||
|
ExternalCOM *external = 0;
|
||
|
HRESULT hr = JSAPI1_GetExternal(&external);
|
||
|
if (SUCCEEDED(hr) && external)
|
||
|
{
|
||
|
hr = external->GetMediaCoreCOM(instance);
|
||
|
external->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (NULL == instance) hr = E_POINTER;
|
||
|
else *instance = NULL;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_GetCurrentSongCOM(CurrentSongCOM **instance)
|
||
|
{
|
||
|
ExternalCOM *external = 0;
|
||
|
HRESULT hr = JSAPI1_GetExternal(&external);
|
||
|
if (SUCCEEDED(hr) && external)
|
||
|
{
|
||
|
hr = external->GetCurrentSongCOM(instance);
|
||
|
external->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (NULL == instance) hr = E_POINTER;
|
||
|
else *instance = NULL;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_SkinChanged()
|
||
|
{
|
||
|
SkinCOM *skinCOM = 0;
|
||
|
HRESULT hr = JSAPI1_GetSkinCOM(&skinCOM);
|
||
|
if (SUCCEEDED(hr) && skinCOM)
|
||
|
{
|
||
|
skinCOM->SkinChanged();
|
||
|
skinCOM->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT __cdecl JSAPI1_CurrentTitleChanged()
|
||
|
{
|
||
|
CurrentSongCOM *songCOM = 0;
|
||
|
HRESULT hr = JSAPI1_GetCurrentSongCOM(&songCOM);
|
||
|
if (SUCCEEDED(hr) && songCOM)
|
||
|
{
|
||
|
songCOM->TitleChanged();
|
||
|
songCOM->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DISPID __cdecl JSAPI1_GenerateUniqueDispatchId()
|
||
|
{
|
||
|
return (DISPID)InterlockedIncrement(&unique_dispid);
|
||
|
}
|
||
|
|
||
|
#define REGISTER_DISPATCH_EX(__name, __creator, __dispId, __pDisp)\
|
||
|
{ __pDisp = new __creator;\
|
||
|
if (NULL != __pDisp) {\
|
||
|
__dispId = AddDispatch(__name, __pDisp);\
|
||
|
if (0 == __dispId) { (__pDisp)->Release(); (__pDisp) = NULL;}\
|
||
|
}\
|
||
|
}
|
||
|
|
||
|
#define REGISTER_DISPATCH(__name, __creator)\
|
||
|
{ DISPID __dispId; IDispatch *pDisp; \
|
||
|
REGISTER_DISPATCH_EX(__name, __creator, __dispId, pDisp);\
|
||
|
if (NULL != pDisp) pDisp->Release(); }
|
||
|
|
||
|
static const wchar_t *api1_api2_key = L"1";
|
||
|
|
||
|
ExternalCOM::ExternalCOM() : ref(1), mediaCoreCOM(NULL), skinCOM(NULL), songCOM(NULL), api2(0)
|
||
|
{
|
||
|
InitializeCriticalSection(&tableLock);
|
||
|
unique_dispid = INITIAL_DISPID; // we can't count on the CRT to have initialized this yet.
|
||
|
configFilename[0]=0;
|
||
|
|
||
|
DISPID dispId = 0;
|
||
|
REGISTER_DISPATCH(L"Browser", BrowserCOM());
|
||
|
REGISTER_DISPATCH_EX(L"CurrentSong", CurrentSongCOM(), dispId, songCOM);
|
||
|
REGISTER_DISPATCH_EX(L"CurrentSkin", SkinCOM(), dispId, skinCOM);
|
||
|
REGISTER_DISPATCH(L"Application", ApplicationCOM());
|
||
|
REGISTER_DISPATCH(L"Bookmarks", BookmarksCOM());
|
||
|
REGISTER_DISPATCH_EX(L"MediaCore", MediaCoreCOM(), dispId, mediaCoreCOM);
|
||
|
REGISTER_DISPATCH(L"DataStore", DataStoreCOM());
|
||
|
REGISTER_DISPATCH(L"Security", SecurityCOM());
|
||
|
JSAPI2::security.SetBypass(api1_api2_key, true);
|
||
|
REGISTER_DISPATCH_EX(L"API2", JSAPI2::ExternalObject(api1_api2_key), dispId, api2);
|
||
|
}
|
||
|
|
||
|
ExternalCOM::~ExternalCOM()
|
||
|
{
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
|
||
|
for ( JSAPI::Dispatcher *l_dispatch_table : dispatchTable )
|
||
|
delete l_dispatch_table;
|
||
|
|
||
|
for ( ConfigCOM *l_config : configs )
|
||
|
l_config->Release();
|
||
|
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
|
||
|
DeleteCriticalSection(&tableLock);
|
||
|
|
||
|
if (NULL != mediaCoreCOM) mediaCoreCOM->Release();
|
||
|
if (NULL != songCOM) songCOM->Release();
|
||
|
if (NULL != skinCOM) skinCOM->Release();
|
||
|
if (NULL != api2) api2->Release();
|
||
|
}
|
||
|
|
||
|
#define CHECK_ID(str, id)\
|
||
|
if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, rgszNames[i], -1, L##str, -1))\
|
||
|
{ rgdispid[i] = id; continue; }
|
||
|
|
||
|
STDMETHODIMP ExternalCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
|
||
|
{
|
||
|
UINT unknowns = 0;
|
||
|
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
size_t tableCount = dispatchTable.size();
|
||
|
|
||
|
for (unsigned int i = 0;i != cNames; i++)
|
||
|
{
|
||
|
rgdispid[i]=DISPID_UNKNOWN;
|
||
|
|
||
|
for (size_t entry = 0; entry < tableCount; entry++)
|
||
|
{
|
||
|
if (!wcscmp(rgszNames[i], dispatchTable[entry]->name))
|
||
|
{
|
||
|
rgdispid[i] = dispatchTable[entry]->id;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rgdispid[i] == DISPID_UNKNOWN && !wcscmp(rgszNames[i], L"Config"))
|
||
|
rgdispid[i] = DISPID_CONFIG;
|
||
|
else if (rgdispid[i] == DISPID_UNKNOWN && !wcscmp(rgszNames[i], L"JNetLib"))
|
||
|
rgdispid[i] = DISPID_JNET;
|
||
|
else if (rgdispid[i] == DISPID_UNKNOWN)
|
||
|
unknowns++;
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
|
||
|
if (0 != unknowns)
|
||
|
return DISP_E_UNKNOWNNAME;
|
||
|
else
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP ExternalCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP ExternalCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP ExternalCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
|
||
|
{
|
||
|
switch(dispid)
|
||
|
{
|
||
|
case DISPID_CONFIG:
|
||
|
{
|
||
|
JSAPI_VERIFY_METHOD(wFlags);
|
||
|
JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
|
||
|
|
||
|
LPCWSTR configName;
|
||
|
JSAPI_GETSTRING(configName, pdispparams, 1, puArgErr);
|
||
|
|
||
|
if (NULL != pvarResult)
|
||
|
{
|
||
|
VariantInit(pvarResult);
|
||
|
|
||
|
ConfigCOM *config;
|
||
|
if (SUCCEEDED(GetConfig(configName, &config)))
|
||
|
{
|
||
|
V_VT(pvarResult) = VT_DISPATCH;
|
||
|
V_DISPATCH(pvarResult) = config;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_VT(pvarResult) = VT_NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
|
||
|
case DISPID_JNET:
|
||
|
{
|
||
|
VariantInit(pvarResult);
|
||
|
V_VT(pvarResult) = VT_DISPATCH;
|
||
|
V_DISPATCH(pvarResult) = new JNetCOM(pdispparams->rgvarg[0].pdispVal);
|
||
|
return S_OK;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
size_t index = dispatchTable.size();
|
||
|
while(index--)
|
||
|
{
|
||
|
if (dispatchTable[index]->id == dispid)
|
||
|
{
|
||
|
VariantInit(pvarResult);
|
||
|
V_VT(pvarResult) = VT_DISPATCH;
|
||
|
V_DISPATCH(pvarResult) = dispatchTable[index]->object;
|
||
|
dispatchTable[index]->object->AddRef();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
if (((size_t)-1) != index) return S_OK;
|
||
|
|
||
|
return DISP_E_MEMBERNOTFOUND;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP ExternalCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
|
||
|
{
|
||
|
if (!ppvObject)
|
||
|
return E_POINTER;
|
||
|
else if (IsEqualIID(riid, IID_IDispatch))
|
||
|
*ppvObject = (IDispatch *)this;
|
||
|
else if (IsEqualIID(riid, IID_IUnknown))
|
||
|
*ppvObject = this;
|
||
|
else
|
||
|
{
|
||
|
*ppvObject = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) ExternalCOM::AddRef(void)
|
||
|
{
|
||
|
return InterlockedIncrement((LONG*)&ref);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) ExternalCOM::Release(void)
|
||
|
{
|
||
|
if (0 == ref)
|
||
|
return ref;
|
||
|
|
||
|
LONG r = InterlockedDecrement((LONG*)&ref);
|
||
|
if (0 == r)
|
||
|
delete(this);
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP ExternalCOM::QueryDispatchable(REFIID riid, Dispatchable **ppDispatchable)
|
||
|
{
|
||
|
if (IsEqualIID(riid, JSAPI::IID_JSAPI_ifc_info))
|
||
|
{
|
||
|
*ppDispatchable = (JSAPI::ifc_info *)this;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppDispatchable = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
(*ppDispatchable)->AddRef();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
const wchar_t *ExternalCOM::GetUserAgent()
|
||
|
{
|
||
|
return L"JSAPI1";
|
||
|
}
|
||
|
|
||
|
HRESULT ExternalCOM::GetConfig(LPCWSTR configName, ConfigCOM **config)
|
||
|
{
|
||
|
if (NULL == config) return E_POINTER;
|
||
|
|
||
|
AutoChar nameAnsi(configName);
|
||
|
if (NULL == (const char*)nameAnsi)
|
||
|
{
|
||
|
*config = NULL;
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
|
||
|
// check if there's already an open config object
|
||
|
size_t index = configs.size();
|
||
|
while (index-- && FALSE != configs[index]->IsEqual(nameAnsi));
|
||
|
|
||
|
if ((size_t)-1 == index)
|
||
|
{
|
||
|
if (L'\0' != configFilename[0] ||
|
||
|
NULL != PathCombineW(configFilename, CONFIGDIR, L"jscfg.ini"))
|
||
|
{
|
||
|
if (SUCCEEDED(ConfigCOM::CreateInstanceA(nameAnsi, AutoCharFn(configFilename), config)))
|
||
|
configs.push_back(*config);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*config = configs[index];
|
||
|
}
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
if (NULL != *config)
|
||
|
{
|
||
|
(*config)->AddRef();
|
||
|
}
|
||
|
else
|
||
|
hr = E_FAIL;
|
||
|
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT ExternalCOM::FindDispatch(DISPID dispId, IDispatch **instance)
|
||
|
{
|
||
|
if (NULL == instance)
|
||
|
return E_POINTER;
|
||
|
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
size_t index = dispatchTable.size();
|
||
|
while(index--)
|
||
|
{
|
||
|
if (dispatchTable[index]->id == dispId)
|
||
|
{
|
||
|
*instance = dispatchTable[index]->object;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
|
||
|
if (((size_t)-1) != index)
|
||
|
return S_OK;
|
||
|
|
||
|
*instance = NULL;
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
DISPID ExternalCOM::AddDispatch(const wchar_t *name, IDispatch *object)
|
||
|
{
|
||
|
if (NULL == object)
|
||
|
return 0;
|
||
|
|
||
|
DISPID id = JSAPI1_GenerateUniqueDispatchId();
|
||
|
|
||
|
JSAPI::Dispatcher *dispatcher = new JSAPI::Dispatcher(name, id, object);
|
||
|
if (NULL == dispatcher) return 0;
|
||
|
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
dispatchTable.push_back(dispatcher);
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
BOOL ExternalCOM::RemoveDispatch(DISPID dispatchId)
|
||
|
{
|
||
|
EnterCriticalSection(&tableLock);
|
||
|
size_t index = dispatchTable.size();
|
||
|
while(index--)
|
||
|
{
|
||
|
if (dispatchTable[index]->id == dispatchId)
|
||
|
{
|
||
|
JSAPI::Dispatcher *dispatcher = dispatchTable[index];
|
||
|
dispatchTable.erase(dispatchTable.begin() + index);
|
||
|
delete dispatcher;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
LeaveCriticalSection(&tableLock);
|
||
|
return (((size_t)-1) != index);
|
||
|
}
|
||
|
|
||
|
HRESULT ExternalCOM::GetSkinCOM(SkinCOM **instance)
|
||
|
{
|
||
|
if (NULL == instance)
|
||
|
return E_POINTER;
|
||
|
|
||
|
*instance = skinCOM;
|
||
|
if (NULL != *instance)
|
||
|
(*instance)->AddRef();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT ExternalCOM::GetMediaCoreCOM(MediaCoreCOM **instance)
|
||
|
{
|
||
|
if (NULL == instance)
|
||
|
return E_POINTER;
|
||
|
|
||
|
*instance = mediaCoreCOM;
|
||
|
if (NULL != *instance)
|
||
|
(*instance)->AddRef();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT ExternalCOM::GetCurrentSongCOM(CurrentSongCOM **instance)
|
||
|
{
|
||
|
if (NULL == instance)
|
||
|
return E_POINTER;
|
||
|
|
||
|
*instance = songCOM;
|
||
|
if (NULL != *instance)
|
||
|
(*instance)->AddRef();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
#define CBCLASS ExternalCOM
|
||
|
START_DISPATCH;
|
||
|
CB(JSAPI_IFC_INFO_GETUSERAGENT, GetUserAgent)
|
||
|
END_DISPATCH;
|
||
|
#undef CBCLASS
|