Sync with trunk r58033.
[reactos.git] / dll / win32 / oleaut32 / recinfo.c
1 /*
2 * Copyright 2005 Jacek Caban
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
28 #include "oaidl.h"
29 #include "oleauto.h"
30
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(ole);
35
36 typedef struct {
37 enum VARENUM vt;
38 VARKIND varkind;
39 ULONG offset;
40 BSTR name;
41 } fieldstr;
42
43 typedef struct {
44 IRecordInfo IRecordInfo_iface;
45 LONG ref;
46
47 GUID guid;
48 UINT lib_index;
49 WORD n_vars;
50 ULONG size;
51 BSTR name;
52 fieldstr *fields;
53 ITypeInfo *pTypeInfo;
54 } IRecordInfoImpl;
55
56 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
57 {
58 return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
59 }
60
61 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
62 {
63 TRACE("%p %p %d\n", src, pvar, vt);
64
65 #define CASE_COPY(x) \
66 case VT_ ## x: \
67 memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
68 break
69
70 switch(vt) {
71 CASE_COPY(I2);
72 CASE_COPY(I4);
73 CASE_COPY(R4);
74 CASE_COPY(R8);
75 CASE_COPY(CY);
76 CASE_COPY(DATE);
77 CASE_COPY(BSTR);
78 CASE_COPY(ERROR);
79 CASE_COPY(BOOL);
80 CASE_COPY(DECIMAL);
81 CASE_COPY(I1);
82 CASE_COPY(UI1);
83 CASE_COPY(UI2);
84 CASE_COPY(UI4);
85 CASE_COPY(I8);
86 CASE_COPY(UI8);
87 CASE_COPY(INT);
88 CASE_COPY(UINT);
89 CASE_COPY(INT_PTR);
90 CASE_COPY(UINT_PTR);
91 default:
92 FIXME("Not supported type: %d\n", vt);
93 return E_NOTIMPL;
94 };
95 #undef CASE_COPY
96
97 V_VT(pvar) = vt;
98 return S_OK;
99 }
100
101 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
102 {
103 VARIANT var;
104 HRESULT hres;
105
106 TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
107
108 hres = VariantChangeType(&var, src, 0, vt);
109 if(FAILED(hres))
110 return hres;
111
112 #define CASE_COPY(x) \
113 case VT_ ## x: \
114 memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
115 break
116
117 switch(vt) {
118 CASE_COPY(I2);
119 CASE_COPY(I4);
120 CASE_COPY(R4);
121 CASE_COPY(R8);
122 CASE_COPY(CY);
123 CASE_COPY(DATE);
124 CASE_COPY(BSTR);
125 CASE_COPY(ERROR);
126 CASE_COPY(BOOL);
127 CASE_COPY(DECIMAL);
128 CASE_COPY(I1);
129 CASE_COPY(UI1);
130 CASE_COPY(UI2);
131 CASE_COPY(UI4);
132 CASE_COPY(I8);
133 CASE_COPY(UI8);
134 CASE_COPY(INT);
135 CASE_COPY(UINT);
136 CASE_COPY(INT_PTR);
137 CASE_COPY(UINT_PTR);
138 default:
139 FIXME("Not supported type: %d\n", V_VT(&var));
140 return E_NOTIMPL;
141 };
142 #undef CASE_COPY
143 return S_OK;
144 }
145
146 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
147 void **ppvObject)
148 {
149 TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
150
151 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
152 *ppvObject = iface;
153 IRecordInfo_AddRef(iface);
154 return S_OK;
155 }
156
157 FIXME("Not supported interface: %s\n", debugstr_guid(riid));
158 return E_NOINTERFACE;
159 }
160
161 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
162 {
163 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
164 ULONG ref = InterlockedIncrement(&This->ref);
165 TRACE("(%p) -> %d\n", This, ref);
166 return ref;
167 }
168
169 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
170 {
171 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
172 ULONG ref = InterlockedDecrement(&This->ref);
173
174 TRACE("(%p) -> %d\n", This, ref);
175
176 if(!ref) {
177 int i;
178 for(i=0; i<This->n_vars; i++)
179 SysFreeString(This->fields[i].name);
180 HeapFree(GetProcessHeap(), 0, This->name);
181 HeapFree(GetProcessHeap(), 0, This->fields);
182 ITypeInfo_Release(This->pTypeInfo);
183 HeapFree(GetProcessHeap(), 0, This);
184 }
185 return ref;
186 }
187
188 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
189 {
190 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
191 TRACE("(%p)->(%p)\n", This, pvNew);
192
193 if(!pvNew)
194 return E_INVALIDARG;
195
196 memset(pvNew, 0, This->size);
197 return S_OK;
198 }
199
200 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
201 {
202 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
203 int i;
204 PVOID var;
205
206 TRACE("(%p)->(%p)\n", This, pvExisting);
207
208 if(!pvExisting)
209 return E_INVALIDARG;
210
211 for(i=0; i<This->n_vars; i++) {
212 if(This->fields[i].varkind != VAR_PERINSTANCE) {
213 ERR("varkind != VAR_PERINSTANCE\n");
214 continue;
215 }
216 var = ((PBYTE)pvExisting)+This->fields[i].offset;
217 switch(This->fields[i].vt) {
218 case VT_BSTR:
219 SysFreeString(*(BSTR*)var);
220 *(BSTR*)var = NULL;
221 break;
222 case VT_I2:
223 case VT_I4:
224 case VT_R4:
225 case VT_R8:
226 case VT_CY:
227 case VT_DATE:
228 case VT_ERROR:
229 case VT_BOOL:
230 case VT_DECIMAL:
231 case VT_I1:
232 case VT_UI1:
233 case VT_UI2:
234 case VT_UI4:
235 case VT_I8:
236 case VT_UI8:
237 case VT_INT:
238 case VT_UINT:
239 break;
240 case VT_INT_PTR:
241 case VT_UINT_PTR:
242 *(void**)var = NULL;
243 break;
244 case VT_SAFEARRAY:
245 SafeArrayDestroy(var);
246 break;
247 default:
248 FIXME("Not supported vt = %d\n", This->fields[i].vt);
249 break;
250 }
251 }
252
253 return S_OK;
254 }
255
256 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, PVOID pvExisting,
257 PVOID pvNew)
258 {
259 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
260
261 TRACE("(%p)->(%p %p)\n", This, pvExisting, pvNew);
262
263 if(!pvExisting || !pvNew)
264 return E_INVALIDARG;
265
266 memcpy(pvExisting, pvNew, This->size);
267 return S_OK;
268 }
269
270 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
271 {
272 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
273
274 TRACE("(%p)->(%p)\n", This, pguid);
275
276 if(!pguid)
277 return E_INVALIDARG;
278
279 *pguid = This->guid;
280 return S_OK;
281 }
282
283 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
284 {
285 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
286
287 TRACE("(%p)->(%p)\n", This, pbstrName);
288
289 if(!pbstrName)
290 return E_INVALIDARG;
291
292 *pbstrName = SysAllocString(This->name);
293 return S_OK;
294 }
295
296 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
297 {
298 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
299
300 TRACE("(%p)->(%p)\n", This, pcbSize);
301
302 if(!pcbSize)
303 return E_INVALIDARG;
304
305 *pcbSize = This->size;
306 return S_OK;
307 }
308
309 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
310 {
311 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
312
313 TRACE("(%p)->(%p)\n", This, ppTypeInfo);
314
315 if(!ppTypeInfo)
316 return E_INVALIDARG;
317
318 ITypeInfo_AddRef(This->pTypeInfo);
319 *ppTypeInfo = This->pTypeInfo;
320
321 return S_OK;
322 }
323
324 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
325 LPCOLESTR szFieldName, VARIANT *pvarField)
326 {
327 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
328 int i;
329
330 TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
331
332 if(!pvData || !szFieldName || !pvarField)
333 return E_INVALIDARG;
334
335 for(i=0; i<This->n_vars; i++)
336 if(!strcmpW(This->fields[i].name, szFieldName))
337 break;
338 if(i == This->n_vars)
339 return TYPE_E_FIELDNOTFOUND;
340
341 VariantClear(pvarField);
342 return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
343 This->fields[i].vt);
344 }
345
346 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
347 LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
348 {
349 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
350 int i;
351
352 TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
353
354 if(!pvData || !szFieldName || !pvarField)
355 return E_INVALIDARG;
356
357 for(i=0; i<This->n_vars; i++)
358 if(!strcmpW(This->fields[i].name, szFieldName))
359 break;
360 if(i == This->n_vars)
361 return TYPE_E_FIELDNOTFOUND;
362
363 VariantClear(pvarField);
364 V_VT(pvarField) = VT_BYREF|This->fields[i].vt;
365 V_BYREF(pvarField) = ((PBYTE)pvData)+This->fields[i].offset;
366 *ppvDataCArray = NULL;
367 return S_OK;
368 }
369
370 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
371 LPCOLESTR szFieldName, VARIANT *pvarField)
372 {
373 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
374 int i;
375
376 TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
377 pvarField);
378
379 if(!pvData || !szFieldName || !pvarField
380 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
381 return E_INVALIDARG;
382
383 if(wFlags == INVOKE_PROPERTYPUTREF) {
384 FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
385 return E_NOTIMPL;
386 }
387
388 for(i=0; i<This->n_vars; i++)
389 if(!strcmpW(This->fields[i].name, szFieldName))
390 break;
391 if(i == This->n_vars)
392 return TYPE_E_FIELDNOTFOUND;
393
394 return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
395 This->fields[i].vt);
396 }
397
398 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
399 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
400 {
401 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
402 int i;
403
404 FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
405
406 if(!pvData || !szFieldName || !pvarField
407 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
408 return E_INVALIDARG;
409
410 for(i=0; i<This->n_vars; i++)
411 if(!strcmpW(This->fields[i].name, szFieldName))
412 break;
413 if(i == This->n_vars)
414 return TYPE_E_FIELDNOTFOUND;
415
416 return E_NOTIMPL;
417 }
418
419 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
420 BSTR *rgBstrNames)
421 {
422 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
423 ULONG n = This->n_vars, i;
424
425 TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
426
427 if(!pcNames)
428 return E_INVALIDARG;
429
430 if(*pcNames < n)
431 n = *pcNames;
432
433 if(rgBstrNames) {
434 for(i=0; i<n; i++)
435 rgBstrNames[i] = SysAllocString(This->fields[i].name);
436 }
437
438 *pcNames = n;
439 return S_OK;
440 }
441
442 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
443 {
444 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
445 GUID guid2;
446
447 TRACE( "(%p)->(%p)\n", This, info2 );
448
449 IRecordInfo_GetGuid( info2, &guid2 );
450 if (IsEqualGUID( &This->guid, &guid2 )) return TRUE;
451
452 FIXME( "records have different guids (%s %s) but could still match\n",
453 debugstr_guid( &This->guid ), debugstr_guid( &guid2 ) );
454
455 return FALSE;
456 }
457
458 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
459 {
460 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
461
462 TRACE("(%p)\n", This);
463
464 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->size);
465 }
466
467 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
468 PVOID *ppvDest)
469 {
470 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
471
472 TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
473
474 if(!pvSource || !ppvDest)
475 return E_INVALIDARG;
476
477 *ppvDest = IRecordInfo_RecordCreate(iface);
478 return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
479 }
480
481 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
482 {
483 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
484 HRESULT hres;
485
486 TRACE("(%p)->(%p)\n", This, pvRecord);
487
488 hres = IRecordInfo_RecordClear(iface, pvRecord);
489 if(FAILED(hres))
490 return hres;
491
492 if(!HeapFree(GetProcessHeap(), 0, pvRecord))
493 return E_INVALIDARG;
494
495 return S_OK;
496 }
497
498 static const IRecordInfoVtbl IRecordInfoImplVtbl = {
499 IRecordInfoImpl_QueryInterface,
500 IRecordInfoImpl_AddRef,
501 IRecordInfoImpl_Release,
502 IRecordInfoImpl_RecordInit,
503 IRecordInfoImpl_RecordClear,
504 IRecordInfoImpl_RecordCopy,
505 IRecordInfoImpl_GetGuid,
506 IRecordInfoImpl_GetName,
507 IRecordInfoImpl_GetSize,
508 IRecordInfoImpl_GetTypeInfo,
509 IRecordInfoImpl_GetField,
510 IRecordInfoImpl_GetFieldNoCopy,
511 IRecordInfoImpl_PutField,
512 IRecordInfoImpl_PutFieldNoCopy,
513 IRecordInfoImpl_GetFieldNames,
514 IRecordInfoImpl_IsMatchingType,
515 IRecordInfoImpl_RecordCreate,
516 IRecordInfoImpl_RecordCreateCopy,
517 IRecordInfoImpl_RecordDestroy
518 };
519
520 /******************************************************************************
521 * GetRecordInfoFromGuids [OLEAUT32.322]
522 *
523 * RETURNS
524 * Success: S_OK
525 * Failure: E_INVALIDARG, if any argument is invalid.
526 */
527 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
528 ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
529 {
530 ITypeInfo *pTypeInfo;
531 ITypeLib *pTypeLib;
532 HRESULT hres;
533
534 TRACE("(%p,%d,%d,%d,%p,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
535 lcid, rGuidTypeInfo, ppRecInfo);
536
537 hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
538 if(FAILED(hres)) {
539 WARN("LoadRegTypeLib failed!\n");
540 return hres;
541 }
542
543 hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
544 ITypeLib_Release(pTypeLib);
545 if(FAILED(hres)) {
546 WARN("GetTypeInfoOfGuid failed!\n");
547 return hres;
548 }
549
550 hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
551 ITypeInfo_Release(pTypeInfo);
552 return hres;
553 }
554
555 /******************************************************************************
556 * GetRecordInfoFromTypeInfo [OLEAUT32.332]
557 */
558 HRESULT WINAPI GetRecordInfoFromTypeInfo(ITypeInfo* pTI, IRecordInfo** ppRecInfo) {
559 HRESULT hres;
560 TYPEATTR *typeattr;
561 IRecordInfoImpl *ret;
562 ITypeInfo *pTypeInfo;
563 int i;
564 GUID guid;
565
566 TRACE("(%p %p)\n", pTI, ppRecInfo);
567
568 if(!pTI || !ppRecInfo)
569 return E_INVALIDARG;
570
571 hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
572 if(FAILED(hres) || !typeattr) {
573 WARN("GetTypeAttr failed: %08x\n", hres);
574 return hres;
575 }
576
577 if(typeattr->typekind == TKIND_ALIAS) {
578 hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.u.hreftype, &pTypeInfo);
579 guid = typeattr->guid;
580 ITypeInfo_ReleaseTypeAttr(pTI, typeattr);
581 if(FAILED(hres)) {
582 WARN("GetRefTypeInfo failed: %08x\n", hres);
583 return hres;
584 }
585 ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr);
586 }else {
587 pTypeInfo = pTI;
588 ITypeInfo_AddRef(pTypeInfo);
589 guid = typeattr->guid;
590 }
591
592 if(typeattr->typekind != TKIND_RECORD) {
593 WARN("typekind != TKIND_RECORD\n");
594 ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
595 ITypeInfo_Release(pTypeInfo);
596 return E_INVALIDARG;
597 }
598
599 ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
600 ret->IRecordInfo_iface.lpVtbl = &IRecordInfoImplVtbl;
601 ret->ref = 1;
602 ret->pTypeInfo = pTypeInfo;
603 ret->n_vars = typeattr->cVars;
604 ret->size = typeattr->cbSizeInstance;
605 ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
606
607 ret->guid = guid;
608
609 /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
610 * ITypeLib::GetLibAttr, but we currently don't need this.
611 */
612
613 hres = ITypeInfo_GetDocumentation(pTypeInfo, MEMBERID_NIL, &ret->name, NULL, NULL, NULL);
614 if(FAILED(hres)) {
615 WARN("ITypeInfo::GetDocumentation failed\n");
616 ret->name = NULL;
617 }
618
619 ret->fields = HeapAlloc(GetProcessHeap(), 0, ret->n_vars*sizeof(VARDESC));
620 for(i = 0; i<ret->n_vars; i++) {
621 VARDESC *vardesc;
622 hres = ITypeInfo_GetVarDesc(pTypeInfo, i, &vardesc);
623 if(FAILED(hres)) {
624 WARN("GetVarDesc failed\n");
625 continue;
626 }
627 ret->fields[i].vt = vardesc->elemdescVar.tdesc.vt;
628 ret->fields[i].varkind = vardesc->varkind;
629 ret->fields[i].offset = vardesc->u.oInst;
630 hres = ITypeInfo_GetDocumentation(pTypeInfo, vardesc->memid, &ret->fields[i].name,
631 NULL, NULL, NULL);
632 if(FAILED(hres))
633 WARN("GetDocumentation failed: %08x\n", hres);
634 ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc);
635 }
636
637 *ppRecInfo = &ret->IRecordInfo_iface;
638
639 return S_OK;
640 }