#include "XmmrControl.h" #include #include #include "EventSink.h" std::string toUtf8(const wchar_t* text) { if (text == nullptr) { return ""; } int size = WideCharToMultiByte(CP_UTF8, 0, text, -1, nullptr, 0, nullptr, nullptr); std::string converted(size > 0 ? size - 1 : 0, '\0'); WideCharToMultiByte(CP_UTF8, 0, text, -1, converted.data(), size, nullptr, nullptr); return converted; } std::wstring toWide(const std::string& text) { int size = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, nullptr, 0); std::wstring converted(size > 0 ? size - 1 : 0, L'\0'); MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, converted.data(), size); return converted; } bool XmmrControl::create(HWND parent, std::string* error) { CLSID clsid; HRESULT result = CLSIDFromProgID(L"XMMR.XMMRCtrl.1", &clsid); if (FAILED(result)) { *error = "XMMR.XMMRCtrl.1 is not registered in this Wine prefix"; return false; } result = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_IOleObject, reinterpret_cast(&object_)); if (FAILED(result)) { *error = "XMMT.ocx would not load"; return false; } // MFC controls reject every dispatch call with E_UNEXPECTED until they are // initialised, and this one is never loaded from a stream. IPersistStreamInit* fresh = nullptr; if (SUCCEEDED(object_->QueryInterface(IID_IPersistStreamInit, reinterpret_cast(&fresh)))) { fresh->InitNew(); fresh->Release(); } OleSetContainedObject(object_, TRUE); site_ = new ControlSite(parent); object_->SetHostNames(L"Nonemm", L"Nonemm"); object_->SetClientSite(site_); RECT area = {0, 0, 0, 0}; GetClientRect(parent, &area); result = object_->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, site_, 0, parent, &area); if (FAILED(result)) { *error = "the control would not activate"; return false; } if (FAILED(object_->QueryInterface(IID_IDispatch, reinterpret_cast(&dispatch_)))) { *error = "the control has no IDispatch"; return false; } return listen(error); } bool XmmrControl::listen(std::string* error) { IID source = IID_NULL; ITypeInfo* events = sourceTypeInfo(dispatch_, &source); IConnectionPointContainer* container = nullptr; if (FAILED(dispatch_->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast(&container)))) { *error = "the control has no connection points"; return false; } HRESULT found = container->FindConnectionPoint(source, &point_); container->Release(); if (FAILED(found)) { *error = "the control's event interface was not found"; return false; } sink_ = createEventSink(events, source, handler_); if (events != nullptr) { events->Release(); } if (FAILED(point_->Advise(sink_, &cookie_))) { *error = "the control refused the event sink"; return false; } return true; } void XmmrControl::destroy() { if (point_ != nullptr && cookie_ != 0) { point_->Unadvise(cookie_); cookie_ = 0; } if (point_ != nullptr) { point_->Release(); point_ = nullptr; } if (sink_ != nullptr) { sink_->Release(); sink_ = nullptr; } if (dispatch_ != nullptr) { dispatch_->Release(); dispatch_ = nullptr; } if (object_ != nullptr) { object_->SetClientSite(nullptr); object_->Release(); object_ = nullptr; } if (site_ != nullptr) { site_->Release(); site_ = nullptr; } } HRESULT XmmrControl::invoke(const wchar_t* name, WORD flags, DISPPARAMS* arguments, VARIANT* result) { if (dispatch_ == nullptr) { return E_POINTER; } DISPID dispid = 0; LPOLESTR wanted = const_cast(name); HRESULT found = dispatch_->GetIDsOfNames(IID_NULL, &wanted, 1, LOCALE_USER_DEFAULT, &dispid); if (FAILED(found)) { return found; } return dispatch_->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, flags, arguments, result, nullptr, nullptr); } HRESULT XmmrControl::put(const wchar_t* name, VARIANT value) { DISPID assigned = DISPID_PROPERTYPUT; DISPPARAMS arguments = {&value, &assigned, 1, 1}; HRESULT result = invoke(name, DISPATCH_PROPERTYPUT, &arguments, nullptr); VariantClear(&value); return result; } HRESULT XmmrControl::putBool(const wchar_t* name, bool value) { VARIANT wanted; VariantInit(&wanted); wanted.vt = VT_BOOL; wanted.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE; return put(name, wanted); } HRESULT XmmrControl::putLong(const wchar_t* name, long value) { VARIANT wanted; VariantInit(&wanted); wanted.vt = VT_I4; wanted.lVal = value; return put(name, wanted); } HRESULT XmmrControl::putString(const wchar_t* name, const std::string& value) { VARIANT wanted; VariantInit(&wanted); wanted.vt = VT_BSTR; wanted.bstrVal = SysAllocString(toWide(value).c_str()); return put(name, wanted); } HRESULT XmmrControl::call(const wchar_t* name, VARIANT* arguments, unsigned count) { DISPPARAMS parameters = {arguments, nullptr, count, 0}; return invoke(name, DISPATCH_METHOD, ¶meters, nullptr); } // Arguments go to a dispatch call last one first. HRESULT XmmrControl::callLong(const wchar_t* name, long first, long second) { VARIANT arguments[2]; VariantInit(&arguments[0]); VariantInit(&arguments[1]); arguments[0].vt = VT_I4; arguments[0].lVal = second; arguments[1].vt = VT_I4; arguments[1].lVal = first; return call(name, arguments, 2); } long XmmrControl::getLong(const wchar_t* name) { VARIANT value; VariantInit(&value); DISPPARAMS none = {nullptr, nullptr, 0, 0}; if (FAILED(invoke(name, DISPATCH_PROPERTYGET, &none, &value))) { return 0; } long found = SUCCEEDED(VariantChangeType(&value, &value, 0, VT_I4)) ? value.lVal : 0; VariantClear(&value); return found; } std::string XmmrControl::getString(const wchar_t* name) { VARIANT value; VariantInit(&value); DISPPARAMS none = {nullptr, nullptr, 0, 0}; if (FAILED(invoke(name, DISPATCH_PROPERTYGET, &none, &value))) { return ""; } std::string found; if (SUCCEEDED(VariantChangeType(&value, &value, 0, VT_BSTR))) { found = toUtf8(value.bstrVal); } VariantClear(&value); return found; }