compartmentmgr.c 17.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 *  ITfCompartmentMgr implementation
 *
 *  Copyright 2009 Aric Stewart, CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdarg.h>

#define COBJMACROS

#include "wine/debug.h"
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winuser.h"
#include "shlwapi.h"
#include "winerror.h"
#include "objbase.h"
#include "oleauto.h"
36
#include "olectl.h"
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

#include "wine/unicode.h"
#include "wine/list.h"

#include "msctf.h"
#include "msctf_internal.h"

WINE_DEFAULT_DEBUG_CHANNEL(msctf);

typedef struct tagCompartmentValue {
    struct list entry;
    GUID guid;
    TfClientId owner;
    ITfCompartment *compartment;
} CompartmentValue;

typedef struct tagCompartmentMgr {
    const ITfCompartmentMgrVtbl *CompartmentMgrVtbl;
    LONG refCount;

    IUnknown *pUnkOuter;

    struct list values;
} CompartmentMgr;

62 63 64 65 66 67 68 69
typedef struct tagCompartmentEnumGuid {
    const IEnumGUIDVtbl *Vtbl;
    LONG refCount;

    struct list *values;
    struct list *cursor;
} CompartmentEnumGuid;

70 71 72 73 74

typedef struct tagCompartmentSink {
    struct list         entry;
    union {
        IUnknown            *pIUnknown;
75
        ITfCompartmentEventSink *pITfCompartmentEventSink;
76 77 78
    } interfaces;
} CompartmentSink;

79 80
typedef struct tagCompartment {
    const ITfCompartmentVtbl *Vtbl;
81
    const ITfSourceVtbl *SourceVtbl;
82 83 84 85 86
    LONG refCount;

    /* Only VT_I4, VT_UNKNOWN and VT_BSTR data types are allowed */
    VARIANT variant;
    CompartmentValue *valueData;
87
    struct list CompartmentEventSink;
88 89
} Compartment;

90
static HRESULT CompartmentEnumGuid_Constructor(struct list* values, IEnumGUID **ppOut);
91
static HRESULT Compartment_Constructor(CompartmentValue *value, ITfCompartment **ppOut);
92

93 94 95 96 97
static inline Compartment *impl_from_ITfSourceVtbl(ITfSource *iface)
{
    return (Compartment *)((char *)iface - FIELD_OFFSET(Compartment,SourceVtbl));
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
HRESULT CompartmentMgr_Destructor(ITfCompartmentMgr *iface)
{
    CompartmentMgr *This = (CompartmentMgr *)iface;
    struct list *cursor, *cursor2;

    LIST_FOR_EACH_SAFE(cursor, cursor2, &This->values)
    {
        CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
        list_remove(cursor);
        ITfCompartment_Release(value->compartment);
        HeapFree(GetProcessHeap(),0,value);
    }

    HeapFree(GetProcessHeap(),0,This);
    return S_OK;
}

/*****************************************************
 * ITfCompartmentMgr functions
 *****************************************************/
static HRESULT WINAPI CompartmentMgr_QueryInterface(ITfCompartmentMgr *iface, REFIID iid, LPVOID *ppvOut)
{
    CompartmentMgr *This = (CompartmentMgr *)iface;
    if (This->pUnkOuter)
        return IUnknown_QueryInterface(This->pUnkOuter, iid, *ppvOut);
    else
    {
        *ppvOut = NULL;

        if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartmentMgr))
        {
            *ppvOut = This;
        }

        if (*ppvOut)
        {
            IUnknown_AddRef(iface);
            return S_OK;
        }

        WARN("unsupported interface: %s\n", debugstr_guid(iid));
        return E_NOINTERFACE;
    }
}

static ULONG WINAPI CompartmentMgr_AddRef(ITfCompartmentMgr *iface)
{
    CompartmentMgr *This = (CompartmentMgr *)iface;
    if (This->pUnkOuter)
        return IUnknown_AddRef(This->pUnkOuter);
    else
        return InterlockedIncrement(&This->refCount);
}

static ULONG WINAPI CompartmentMgr_Release(ITfCompartmentMgr *iface)
{
    CompartmentMgr *This = (CompartmentMgr *)iface;
    if (This->pUnkOuter)
        return IUnknown_Release(This->pUnkOuter);
    else
    {
        ULONG ret;

        ret = InterlockedDecrement(&This->refCount);
        if (ret == 0)
            CompartmentMgr_Destructor(iface);
        return ret;
    }
}

168
static HRESULT WINAPI CompartmentMgr_GetCompartment(ITfCompartmentMgr *iface,
169 170 171
        REFGUID rguid, ITfCompartment **ppcomp)
{
    CompartmentMgr *This = (CompartmentMgr *)iface;
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
    CompartmentValue* value;
    struct list *cursor;
    HRESULT hr;

    TRACE("(%p) %s  %p\n",This,debugstr_guid(rguid),ppcomp);

    LIST_FOR_EACH(cursor, &This->values)
    {
        value = LIST_ENTRY(cursor,CompartmentValue,entry);
        if (IsEqualGUID(rguid,&value->guid))
        {
            ITfCompartment_AddRef(value->compartment);
            *ppcomp = value->compartment;
            return S_OK;
        }
    }

    value = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentValue));
    value->guid = *rguid;
    value->owner = 0;
    hr = Compartment_Constructor(value,&value->compartment);
    if (SUCCEEDED(hr))
    {
        list_add_head(&This->values,&value->entry);
        ITfCompartment_AddRef(value->compartment);
        *ppcomp = value->compartment;
    }
    else
    {
        HeapFree(GetProcessHeap(),0,value);
        *ppcomp = NULL;
    }
    return hr;
205 206
}

207
static HRESULT WINAPI CompartmentMgr_ClearCompartment(ITfCompartmentMgr *iface,
208 209
    TfClientId tid, REFGUID rguid)
{
210
    struct list *cursor;
211
    CompartmentMgr *This = (CompartmentMgr *)iface;
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    TRACE("(%p) %i %s\n",This,tid,debugstr_guid(rguid));

    LIST_FOR_EACH(cursor, &This->values)
    {
        CompartmentValue* value = LIST_ENTRY(cursor,CompartmentValue,entry);
        if (IsEqualGUID(rguid,&value->guid))
        {
            if (value->owner && tid != value->owner)
                return E_UNEXPECTED;
            list_remove(cursor);
            ITfCompartment_Release(value->compartment);
            HeapFree(GetProcessHeap(),0,value);
            return S_OK;
        }
    }

    return CONNECT_E_NOCONNECTION;
229 230
}

231
static HRESULT WINAPI CompartmentMgr_EnumCompartments(ITfCompartmentMgr *iface,
232 233 234
 IEnumGUID **ppEnum)
{
    CompartmentMgr *This = (CompartmentMgr *)iface;
235 236 237 238
    TRACE("(%p) %p\n",This,ppEnum);
    if (!ppEnum)
        return E_INVALIDARG;
    return CompartmentEnumGuid_Constructor(&This->values, ppEnum);
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
}

static const ITfCompartmentMgrVtbl CompartmentMgr_CompartmentMgrVtbl =
{
    CompartmentMgr_QueryInterface,
    CompartmentMgr_AddRef,
    CompartmentMgr_Release,

    CompartmentMgr_GetCompartment,
    CompartmentMgr_ClearCompartment,
    CompartmentMgr_EnumCompartments
};

HRESULT CompartmentMgr_Constructor(IUnknown *pUnkOuter, REFIID riid, IUnknown **ppOut)
{
    CompartmentMgr *This;

    if (!ppOut)
        return E_POINTER;

    if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
        return CLASS_E_NOAGGREGATION;

    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentMgr));
    if (This == NULL)
        return E_OUTOFMEMORY;

    This->CompartmentMgrVtbl = &CompartmentMgr_CompartmentMgrVtbl;
    This->pUnkOuter = pUnkOuter;
    list_init(&This->values);

    if (pUnkOuter)
    {
        TRACE("returning %p\n", This);
        *ppOut = (IUnknown*)This;
        return S_OK;
    }
    else
    {
        HRESULT hr;
        hr = IUnknown_QueryInterface((IUnknown*)This, riid, (LPVOID*)ppOut);
        if (FAILED(hr))
            HeapFree(GetProcessHeap(),0,This);
        return hr;
    }
}
285 286

/**************************************************
287
 * IEnumGUID implementation for ITfCompartmentMgr::EnumCompartments
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
 **************************************************/
static void CompartmentEnumGuid_Destructor(CompartmentEnumGuid *This)
{
    TRACE("destroying %p\n", This);
    HeapFree(GetProcessHeap(),0,This);
}

static HRESULT WINAPI CompartmentEnumGuid_QueryInterface(IEnumGUID *iface, REFIID iid, LPVOID *ppvOut)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
    *ppvOut = NULL;

    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IEnumGUID))
    {
        *ppvOut = This;
    }

    if (*ppvOut)
    {
        IUnknown_AddRef(iface);
        return S_OK;
    }

    WARN("unsupported interface: %s\n", debugstr_guid(iid));
    return E_NOINTERFACE;
}

static ULONG WINAPI CompartmentEnumGuid_AddRef(IEnumGUID *iface)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid*)iface;
    return InterlockedIncrement(&This->refCount);
}

static ULONG WINAPI CompartmentEnumGuid_Release(IEnumGUID *iface)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
        CompartmentEnumGuid_Destructor(This);
    return ret;
}

/*****************************************************
 * IEnumGuid functions
 *****************************************************/
static HRESULT WINAPI CompartmentEnumGuid_Next( LPENUMGUID iface,
    ULONG celt, GUID *rgelt, ULONG *pceltFetched)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
    ULONG fetched = 0;

    TRACE("(%p)\n",This);

    if (rgelt == NULL) return E_POINTER;

    while (fetched < celt && This->cursor)
    {
        CompartmentValue* value = LIST_ENTRY(This->cursor,CompartmentValue,entry);
        if (!value)
            break;

        This->cursor = list_next(This->values,This->cursor);
        *rgelt = value->guid;

        ++fetched;
        ++rgelt;
    }

    if (pceltFetched) *pceltFetched = fetched;
    return fetched == celt ? S_OK : S_FALSE;
}

static HRESULT WINAPI CompartmentEnumGuid_Skip( LPENUMGUID iface, ULONG celt)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
    TRACE("(%p)\n",This);

    This->cursor = list_next(This->values,This->cursor);
    return S_OK;
}

static HRESULT WINAPI CompartmentEnumGuid_Reset( LPENUMGUID iface)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
    TRACE("(%p)\n",This);
    This->cursor = list_head(This->values);
    return S_OK;
}

static HRESULT WINAPI CompartmentEnumGuid_Clone( LPENUMGUID iface,
    IEnumGUID **ppenum)
{
    CompartmentEnumGuid *This = (CompartmentEnumGuid *)iface;
    HRESULT res;

    TRACE("(%p)\n",This);

    if (ppenum == NULL) return E_POINTER;

    res = CompartmentEnumGuid_Constructor(This->values, ppenum);
    if (SUCCEEDED(res))
    {
        CompartmentEnumGuid *new_This = (CompartmentEnumGuid *)*ppenum;
        new_This->cursor = This->cursor;
    }
    return res;
}

static const IEnumGUIDVtbl IEnumGUID_Vtbl ={
    CompartmentEnumGuid_QueryInterface,
    CompartmentEnumGuid_AddRef,
    CompartmentEnumGuid_Release,

    CompartmentEnumGuid_Next,
    CompartmentEnumGuid_Skip,
    CompartmentEnumGuid_Reset,
    CompartmentEnumGuid_Clone
};

static HRESULT CompartmentEnumGuid_Constructor(struct list *values, IEnumGUID **ppOut)
{
    CompartmentEnumGuid *This;

    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CompartmentEnumGuid));
    if (This == NULL)
        return E_OUTOFMEMORY;

    This->Vtbl= &IEnumGUID_Vtbl;
    This->refCount = 1;

    This->values = values;
    This->cursor = list_head(values);

    TRACE("returning %p\n", This);
    *ppOut = (IEnumGUID*)This;
    return S_OK;
}
427 428 429 430

/**************************************************
 * ITfCompartment
 **************************************************/
431 432 433 434 435 436
static void free_sink(CompartmentSink *sink)
{
        IUnknown_Release(sink->interfaces.pIUnknown);
        HeapFree(GetProcessHeap(),0,sink);
}

437 438
static void Compartment_Destructor(Compartment *This)
{
439
    struct list *cursor, *cursor2;
440 441
    TRACE("destroying %p\n", This);
    VariantClear(&This->variant);
442 443 444 445 446 447
    LIST_FOR_EACH_SAFE(cursor, cursor2, &This->CompartmentEventSink)
    {
        CompartmentSink* sink = LIST_ENTRY(cursor,CompartmentSink,entry);
        list_remove(cursor);
        free_sink(sink);
    }
448 449 450 451 452 453 454 455 456 457 458 459
    HeapFree(GetProcessHeap(),0,This);
}

static HRESULT WINAPI Compartment_QueryInterface(ITfCompartment *iface, REFIID iid, LPVOID *ppvOut)
{
    Compartment *This = (Compartment *)iface;
    *ppvOut = NULL;

    if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfCompartment))
    {
        *ppvOut = This;
    }
460 461 462 463
    else if (IsEqualIID(iid, &IID_ITfSource))
    {
        *ppvOut = &This->SourceVtbl;
    }
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495

    if (*ppvOut)
    {
        IUnknown_AddRef(iface);
        return S_OK;
    }

    WARN("unsupported interface: %s\n", debugstr_guid(iid));
    return E_NOINTERFACE;
}

static ULONG WINAPI Compartment_AddRef(ITfCompartment *iface)
{
    Compartment *This = (Compartment*)iface;
    return InterlockedIncrement(&This->refCount);
}

static ULONG WINAPI Compartment_Release(ITfCompartment *iface)
{
    Compartment *This = (Compartment *)iface;
    ULONG ret;

    ret = InterlockedDecrement(&This->refCount);
    if (ret == 0)
        Compartment_Destructor(This);
    return ret;
}

static HRESULT WINAPI Compartment_SetValue(ITfCompartment *iface,
    TfClientId tid, const VARIANT *pvarValue)
{
    Compartment *This = (Compartment *)iface;
496
    struct list *cursor;
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520

    TRACE("(%p) %i %p\n",This,tid,pvarValue);

    if (!pvarValue)
        return E_INVALIDARG;

    if (!(V_VT(pvarValue) == VT_BSTR || V_VT(pvarValue) == VT_I4 ||
          V_VT(pvarValue) == VT_UNKNOWN))
        return E_INVALIDARG;

    if (!This->valueData->owner)
        This->valueData->owner = tid;

    VariantClear(&This->variant);

    /* Shallow copy of value and type */
    This->variant = *pvarValue;

    if (V_VT(pvarValue) == VT_BSTR)
        V_BSTR(&This->variant) = SysAllocStringByteLen((char*)V_BSTR(pvarValue),
                SysStringByteLen(V_BSTR(pvarValue)));
    else if (V_VT(pvarValue) == VT_UNKNOWN)
        IUnknown_AddRef(V_UNKNOWN(&This->variant));

521 522 523 524 525 526
    LIST_FOR_EACH(cursor, &This->CompartmentEventSink)
    {
        CompartmentSink* sink = LIST_ENTRY(cursor,CompartmentSink,entry);
        ITfCompartmentEventSink_OnChange(sink->interfaces.pITfCompartmentEventSink,&This->valueData->guid);
    }

527
    return S_OK;
528 529 530 531 532 533
}

static HRESULT WINAPI Compartment_GetValue(ITfCompartment *iface,
    VARIANT *pvarValue)
{
    Compartment *This = (Compartment *)iface;
534 535 536 537 538
    TRACE("(%p) %p\n",This, pvarValue);

    if (!pvarValue)
        return E_INVALIDARG;

539 540 541
    VariantInit(pvarValue);
    if (V_VT(&This->variant) == VT_EMPTY) return S_FALSE;
    return VariantCopy(pvarValue,&This->variant);
542 543 544 545 546 547 548 549 550 551 552
}

static const ITfCompartmentVtbl ITfCompartment_Vtbl ={
    Compartment_QueryInterface,
    Compartment_AddRef,
    Compartment_Release,

    Compartment_SetValue,
    Compartment_GetValue
};

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
/*****************************************************
 * ITfSource functions
 *****************************************************/

static HRESULT WINAPI Source_QueryInterface(ITfSource *iface, REFIID iid, LPVOID *ppvOut)
{
    Compartment *This = impl_from_ITfSourceVtbl(iface);
    return Compartment_QueryInterface((ITfCompartment *)This, iid, *ppvOut);
}

static ULONG WINAPI Source_AddRef(ITfSource *iface)
{
    Compartment *This = impl_from_ITfSourceVtbl(iface);
    return Compartment_AddRef((ITfCompartment*)This);
}

static ULONG WINAPI Source_Release(ITfSource *iface)
{
    Compartment *This = impl_from_ITfSourceVtbl(iface);
    return Compartment_Release((ITfCompartment *)This);
}

575
static HRESULT WINAPI CompartmentSource_AdviseSink(ITfSource *iface,
576 577
        REFIID riid, IUnknown *punk, DWORD *pdwCookie)
{
578
    CompartmentSink *cs;
579 580 581 582 583 584 585
    Compartment *This = impl_from_ITfSourceVtbl(iface);

    TRACE("(%p) %s %p %p\n",This,debugstr_guid(riid),punk,pdwCookie);

    if (!riid || !punk || !pdwCookie)
        return E_INVALIDARG;

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    if (IsEqualIID(riid, &IID_ITfCompartmentEventSink))
    {
        cs = HeapAlloc(GetProcessHeap(),0,sizeof(CompartmentSink));
        if (!cs)
            return E_OUTOFMEMORY;
        if (FAILED(IUnknown_QueryInterface(punk, riid, (LPVOID *)&cs->interfaces.pITfCompartmentEventSink)))
        {
            HeapFree(GetProcessHeap(),0,cs);
            return CONNECT_E_CANNOTCONNECT;
        }
        list_add_head(&This->CompartmentEventSink,&cs->entry);
        *pdwCookie = generate_Cookie(COOKIE_MAGIC_COMPARTMENTSINK , cs);
    }
    else
    {
        FIXME("(%p) Unhandled Sink: %s\n",This,debugstr_guid(riid));
        return E_NOTIMPL;
    }

    TRACE("cookie %x\n",*pdwCookie);

    return S_OK;
608 609
}

610
static HRESULT WINAPI CompartmentSource_UnadviseSink(ITfSource *iface, DWORD pdwCookie)
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
{
    CompartmentSink *sink;
    Compartment *This = impl_from_ITfSourceVtbl(iface);

    TRACE("(%p) %x\n",This,pdwCookie);

    if (get_Cookie_magic(pdwCookie)!=COOKIE_MAGIC_COMPARTMENTSINK)
        return E_INVALIDARG;

    sink = (CompartmentSink*)remove_Cookie(pdwCookie);
    if (!sink)
        return CONNECT_E_NOCONNECTION;

    list_remove(&sink->entry);
    free_sink(sink);

    return S_OK;
}

static const ITfSourceVtbl Compartment_SourceVtbl =
{
    Source_QueryInterface,
    Source_AddRef,
    Source_Release,

    CompartmentSource_AdviseSink,
    CompartmentSource_UnadviseSink,
};

640 641 642 643 644 645 646 647 648
static HRESULT Compartment_Constructor(CompartmentValue *valueData, ITfCompartment **ppOut)
{
    Compartment *This;

    This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(Compartment));
    if (This == NULL)
        return E_OUTOFMEMORY;

    This->Vtbl= &ITfCompartment_Vtbl;
649
    This->SourceVtbl = &Compartment_SourceVtbl;
650 651 652 653 654
    This->refCount = 1;

    This->valueData = valueData;
    VariantInit(&This->variant);

655 656
    list_init(&This->CompartmentEventSink);

657 658 659 660
    TRACE("returning %p\n", This);
    *ppOut = (ITfCompartment*)This;
    return S_OK;
}