fltlib: Add a stub dll.
[wine] / dlls / oledb32 / convert.c
1 /* OLE DB Conversion library
2  *
3  * Copyright 2009 Huw Davies
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include <stdarg.h>
21
22 #define COBJMACROS
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "ole2.h"
30 #include "msdadc.h"
31 #include "oledberr.h"
32
33 #include "oledb_private.h"
34
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(oledb);
38
39 typedef struct
40 {
41     const struct IDataConvertVtbl *lpVtbl;
42     const struct IDCInfoVtbl *lpDCInfoVtbl;
43
44     LONG ref;
45
46     UINT version; /* Set by IDCInfo_SetInfo */
47 } convert;
48
49 static inline convert *impl_from_IDataConvert(IDataConvert *iface)
50 {
51     return (convert *)((char*)iface - FIELD_OFFSET(convert, lpVtbl));
52 }
53
54 static inline convert *impl_from_IDCInfo(IDCInfo *iface)
55 {
56     return (convert *)((char*)iface - FIELD_OFFSET(convert, lpDCInfoVtbl));
57 }
58
59 static HRESULT WINAPI convert_QueryInterface(IDataConvert* iface,
60                                              REFIID riid,
61                                              void **obj)
62 {
63     convert *This = impl_from_IDataConvert(iface);
64     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
65
66     *obj = NULL;
67
68     if(IsEqualIID(riid, &IID_IUnknown) ||
69        IsEqualIID(riid, &IID_IDataConvert))
70     {
71         *obj = iface;
72     }
73     else if(IsEqualIID(riid, &IID_IDCInfo))
74     {
75         *obj = &This->lpDCInfoVtbl;
76     }
77     else
78     {
79         FIXME("interface %s not implemented\n", debugstr_guid(riid));
80         return E_NOINTERFACE;
81     }
82
83     IDataConvert_AddRef(iface);
84     return S_OK;
85 }
86
87
88 static ULONG WINAPI convert_AddRef(IDataConvert* iface)
89 {
90     convert *This = impl_from_IDataConvert(iface);
91     TRACE("(%p)\n", This);
92
93     return InterlockedIncrement(&This->ref);
94 }
95
96
97 static ULONG WINAPI convert_Release(IDataConvert* iface)
98 {
99     convert *This = impl_from_IDataConvert(iface);
100     LONG ref;
101
102     TRACE("(%p)\n", This);
103
104     ref = InterlockedDecrement(&This->ref);
105     if(ref == 0)
106     {
107         HeapFree(GetProcessHeap(), 0, This);
108     }
109
110     return ref;
111 }
112
113 static int get_length(DBTYPE type)
114 {
115     switch(type)
116     {
117     case DBTYPE_I1:
118     case DBTYPE_UI1:
119         return 1;
120     case DBTYPE_I2:
121     case DBTYPE_UI2:
122         return 2;
123     case DBTYPE_I4:
124     case DBTYPE_UI4:
125         return 4;
126     case DBTYPE_I8:
127     case DBTYPE_UI8:
128         return 8;
129     case DBTYPE_BSTR:
130         return sizeof(BSTR);
131     case DBTYPE_WSTR:
132         return 0;
133     default:
134         FIXME("Unhandled type %04x\n", type);
135         return 0;
136     }
137 }
138
139 static HRESULT WINAPI convert_DataConvert(IDataConvert* iface,
140                                           DBTYPE src_type, DBTYPE dst_type,
141                                           DBLENGTH src_len, DBLENGTH *dst_len,
142                                           void *src, void *dst,
143                                           DBLENGTH dst_max_len,
144                                           DBSTATUS src_status, DBSTATUS *dst_status,
145                                           BYTE precision, BYTE scale,
146                                           DBDATACONVERT flags)
147 {
148     convert *This = impl_from_IDataConvert(iface);
149     HRESULT hr;
150
151     TRACE("(%p)->(%d, %d, %d, %p, %p, %p, %d, %d, %p, %d, %d, %x)\n", This,
152           src_type, dst_type, src_len, dst_len, src, dst, dst_max_len,
153           src_status, dst_status, precision, scale, flags);
154
155     *dst_len = get_length(dst_type);
156     *dst_status = DBSTATUS_E_BADACCESSOR;
157
158     if(IDataConvert_CanConvert(iface, src_type, dst_type) != S_OK)
159     {
160         return DB_E_UNSUPPORTEDCONVERSION;
161     }
162
163     if(src_type == DBTYPE_STR)
164     {
165         BSTR b;
166         DWORD len;
167
168         if(flags & DBDATACONVERT_LENGTHFROMNTS)
169             len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0) - 1;
170         else
171             len = MultiByteToWideChar(CP_ACP, 0, src, src_len, NULL, 0);
172         b = SysAllocStringLen(NULL, len);
173         if(!b) return E_OUTOFMEMORY;
174         if(flags & DBDATACONVERT_LENGTHFROMNTS)
175             MultiByteToWideChar(CP_ACP, 0, src, -1, b, len + 1);
176         else
177             MultiByteToWideChar(CP_ACP, 0, src, src_len, b, len);
178
179         hr = IDataConvert_DataConvert(iface, DBTYPE_BSTR, dst_type, 0, dst_len,
180                                       &b, dst, dst_max_len, src_status, dst_status,
181                                       precision, scale, flags);
182
183         SysFreeString(b);
184         return hr;
185     }
186
187     if(src_type == DBTYPE_WSTR)
188     {
189         BSTR b;
190
191         if(flags & DBDATACONVERT_LENGTHFROMNTS)
192             b = SysAllocString(src);
193         else
194             b = SysAllocStringLen(src, src_len / 2);
195         if(!b) return E_OUTOFMEMORY;
196         hr = IDataConvert_DataConvert(iface, DBTYPE_BSTR, dst_type, 0, dst_len,
197                                       &b, dst, dst_max_len, src_status, dst_status,
198                                       precision, scale, flags);
199         SysFreeString(b);
200         return hr;
201     }
202
203     switch(dst_type)
204     {
205     case DBTYPE_I2:
206     {
207         signed short *d = dst;
208         switch(src_type)
209         {
210         case DBTYPE_EMPTY:       *d = 0; hr = S_OK;                              break;
211         case DBTYPE_I2:          *d = *(signed short*)src; hr = S_OK;            break;
212         case DBTYPE_I4:          hr = VarI2FromI4(*(signed int*)src, d);         break;
213         case DBTYPE_R4:          hr = VarI2FromR4(*(FLOAT*)src, d);              break;
214         case DBTYPE_R8:          hr = VarI2FromR8(*(double*)src, d);             break;
215         case DBTYPE_CY:          hr = VarI2FromCy(*(CY*)src, d);                 break;
216         case DBTYPE_DATE:        hr = VarI2FromDate(*(DATE*)src, d);             break;
217         case DBTYPE_BSTR:        hr = VarI2FromStr(*(WCHAR**)src, LOCALE_USER_DEFAULT, 0, d); break;
218         case DBTYPE_BOOL:        hr = VarI2FromBool(*(VARIANT_BOOL*)src, d);     break;
219         case DBTYPE_DECIMAL:     hr = VarI2FromDec((DECIMAL*)src, d);            break;
220         case DBTYPE_I1:          hr = VarI2FromI1(*(signed char*)src, d);        break;
221         case DBTYPE_UI1:         hr = VarI2FromUI1(*(BYTE*)src, d);              break;
222         case DBTYPE_UI2:         hr = VarI2FromUI2(*(WORD*)src, d);              break;
223         case DBTYPE_UI4:         hr = VarI2FromUI4(*(DWORD*)src, d);             break;
224         case DBTYPE_I8:          hr = VarI2FromI8(*(LONGLONG*)src, d);           break;
225         case DBTYPE_UI8:         hr = VarI2FromUI8(*(ULONGLONG*)src, d);         break;
226         default: FIXME("Unimplemented conversion %04x -> I2\n", src_type); return E_NOTIMPL;
227         }
228         break;
229     }
230
231     case DBTYPE_I4:
232     {
233         signed int *d = dst;
234         switch(src_type)
235         {
236         case DBTYPE_EMPTY:       *d = 0; hr = S_OK;                              break;
237         case DBTYPE_I2:          hr = VarI4FromI2(*(signed short*)src, d);       break;
238         case DBTYPE_I4:          *d = *(signed int*)src; hr = S_OK;              break;
239         case DBTYPE_R4:          hr = VarI4FromR4(*(FLOAT*)src, d);              break;
240         case DBTYPE_R8:          hr = VarI4FromR8(*(double*)src, d);             break;
241         case DBTYPE_CY:          hr = VarI4FromCy(*(CY*)src, d);                 break;
242         case DBTYPE_DATE:        hr = VarI4FromDate(*(DATE*)src, d);             break;
243         case DBTYPE_BSTR:        hr = VarI4FromStr(*(WCHAR**)src, LOCALE_USER_DEFAULT, 0, d); break;
244         case DBTYPE_BOOL:        hr = VarI4FromBool(*(VARIANT_BOOL*)src, d);     break;
245         case DBTYPE_DECIMAL:     hr = VarI4FromDec((DECIMAL*)src, d);            break;
246         case DBTYPE_I1:          hr = VarI4FromI1(*(signed char*)src, d);        break;
247         case DBTYPE_UI1:         hr = VarI4FromUI1(*(BYTE*)src, d);              break;
248         case DBTYPE_UI2:         hr = VarI4FromUI2(*(WORD*)src, d);              break;
249         case DBTYPE_UI4:         hr = VarI4FromUI4(*(DWORD*)src, d);             break;
250         case DBTYPE_I8:          hr = VarI4FromI8(*(LONGLONG*)src, d);           break;
251         case DBTYPE_UI8:         hr = VarI4FromUI8(*(ULONGLONG*)src, d);         break;
252         default: FIXME("Unimplemented conversion %04x -> I4\n", src_type); return E_NOTIMPL;
253         }
254         break;
255     }
256
257     case DBTYPE_BSTR:
258     {
259         BSTR *d = dst;
260         switch(src_type)
261         {
262         case DBTYPE_EMPTY:       *d = SysAllocStringLen(NULL, 0); hr = *d ? S_OK : E_OUTOFMEMORY;      break;
263         case DBTYPE_I2:          hr = VarBstrFromI2(*(signed short*)src, LOCALE_USER_DEFAULT, 0, d);   break;
264         case DBTYPE_I4:          hr = VarBstrFromI4(*(signed int*)src, LOCALE_USER_DEFAULT, 0, d);     break;
265         case DBTYPE_R4:          hr = VarBstrFromR4(*(FLOAT*)src, LOCALE_USER_DEFAULT, 0, d);          break;
266         case DBTYPE_R8:          hr = VarBstrFromR8(*(double*)src, LOCALE_USER_DEFAULT, 0, d);         break;
267         case DBTYPE_CY:          hr = VarBstrFromCy(*(CY*)src, LOCALE_USER_DEFAULT, 0, d);             break;
268         case DBTYPE_DATE:        hr = VarBstrFromDate(*(DATE*)src, LOCALE_USER_DEFAULT, 0, d);         break;
269         case DBTYPE_BSTR:        *d = SysAllocStringLen(*(BSTR*)src, SysStringLen(*(BSTR*)src)); hr = *d ? S_OK : E_OUTOFMEMORY;     break;
270         case DBTYPE_BOOL:        hr = VarBstrFromBool(*(VARIANT_BOOL*)src, LOCALE_USER_DEFAULT, 0, d); break;
271         case DBTYPE_DECIMAL:     hr = VarBstrFromDec((DECIMAL*)src, LOCALE_USER_DEFAULT, 0, d);        break;
272         case DBTYPE_I1:          hr = VarBstrFromI1(*(signed char*)src, LOCALE_USER_DEFAULT, 0, d);    break;
273         case DBTYPE_UI1:         hr = VarBstrFromUI1(*(BYTE*)src, LOCALE_USER_DEFAULT, 0, d);          break;
274         case DBTYPE_UI2:         hr = VarBstrFromUI2(*(WORD*)src, LOCALE_USER_DEFAULT, 0, d);          break;
275         case DBTYPE_UI4:         hr = VarBstrFromUI4(*(DWORD*)src, LOCALE_USER_DEFAULT, 0, d);         break;
276         case DBTYPE_I8:          hr = VarBstrFromI8(*(LONGLONG*)src, LOCALE_USER_DEFAULT, 0, d);       break;
277         case DBTYPE_UI8:         hr = VarBstrFromUI8(*(ULONGLONG*)src, LOCALE_USER_DEFAULT, 0, d);     break;
278         default: FIXME("Unimplemented conversion %04x -> BSTR\n", src_type); return E_NOTIMPL;
279         }
280         break;
281     }
282
283     case DBTYPE_WSTR:
284     {
285         BSTR b;
286         DBLENGTH bstr_len;
287         INT bytes_to_copy;
288         hr = IDataConvert_DataConvert(iface, src_type, DBTYPE_BSTR, src_len, &bstr_len,
289                                       src, &b, sizeof(BSTR), src_status, dst_status,
290                                       precision, scale, flags);
291         if(hr != S_OK) return hr;
292         bstr_len = SysStringLen(b);
293         *dst_len = bstr_len * sizeof(WCHAR); /* Doesn't include size for '\0' */
294         *dst_status = DBSTATUS_S_OK;
295         bytes_to_copy = min(*dst_len + sizeof(WCHAR), dst_max_len);
296         if(dst)
297         {
298             if(bytes_to_copy >= sizeof(WCHAR))
299             {
300                 memcpy(dst, b, bytes_to_copy - sizeof(WCHAR));
301                 *((WCHAR*)dst + bytes_to_copy / sizeof(WCHAR) - 1) = 0;
302                 if(bytes_to_copy < *dst_len + sizeof(WCHAR))
303                     *dst_status = DBSTATUS_S_TRUNCATED;
304             }
305             else
306             {
307                 *dst_status = DBSTATUS_E_DATAOVERFLOW;
308                 hr = DB_E_ERRORSOCCURRED;
309             }
310         }
311         SysFreeString(b);
312         return hr;
313     }
314
315     default:
316         FIXME("Unimplemented conversion %04x -> %04x\n", src_type, dst_type);
317         return E_NOTIMPL;
318
319     }
320
321     if(hr == DISP_E_OVERFLOW)
322     {
323         *dst_status = DBSTATUS_E_DATAOVERFLOW;
324         hr = DB_E_ERRORSOCCURRED;
325     }
326     else if(hr == S_OK)
327         *dst_status = DBSTATUS_S_OK;
328
329     return hr;
330 }
331
332 static inline WORD get_dbtype_class(DBTYPE type)
333 {
334     switch(type)
335     {
336     case DBTYPE_I2:
337     case DBTYPE_R4:
338     case DBTYPE_R8:
339     case DBTYPE_I1:
340     case DBTYPE_UI1:
341     case DBTYPE_UI2:
342         return DBTYPE_I2;
343
344     case DBTYPE_I4:
345     case DBTYPE_UI4:
346         return DBTYPE_I4;
347
348     case DBTYPE_I8:
349     case DBTYPE_UI8:
350         return DBTYPE_I8;
351
352     case DBTYPE_BSTR:
353     case DBTYPE_STR:
354     case DBTYPE_WSTR:
355         return DBTYPE_BSTR;
356
357     case DBTYPE_DBDATE:
358     case DBTYPE_DBTIME:
359     case DBTYPE_DBTIMESTAMP:
360         return DBTYPE_DBDATE;
361     }
362     return type;
363 }
364
365 /* Many src types will convert to this group of dst types */
366 static inline BOOL common_class(WORD dst_class)
367 {
368     switch(dst_class)
369     {
370     case DBTYPE_EMPTY:
371     case DBTYPE_NULL:
372     case DBTYPE_I2:
373     case DBTYPE_I4:
374     case DBTYPE_BSTR:
375     case DBTYPE_BOOL:
376     case DBTYPE_VARIANT:
377     case DBTYPE_I8:
378     case DBTYPE_CY:
379     case DBTYPE_DECIMAL:
380     case DBTYPE_NUMERIC:
381         return TRUE;
382     }
383     return FALSE;
384 }
385
386 static inline BOOL array_type(DBTYPE type)
387 {
388     return (type >= DBTYPE_I2 && type <= DBTYPE_UI4);
389 }
390
391 static HRESULT WINAPI convert_CanConvert(IDataConvert* iface,
392                                          DBTYPE src_type, DBTYPE dst_type)
393 {
394     convert *This = impl_from_IDataConvert(iface);
395     DBTYPE src_base_type = src_type & 0x1ff;
396     DBTYPE dst_base_type = dst_type & 0x1ff;
397     WORD dst_class = get_dbtype_class(dst_base_type);
398
399     TRACE("(%p)->(%d, %d)\n", This, src_type, dst_type);
400
401     if(src_type & DBTYPE_VECTOR || dst_type & DBTYPE_VECTOR) return S_FALSE;
402
403     if(src_type & DBTYPE_ARRAY)
404     {
405         if(!array_type(src_base_type)) return S_FALSE;
406         if(dst_type & DBTYPE_ARRAY)
407         {
408             if(src_type == dst_type) return S_OK;
409             return S_FALSE;
410         }
411         if(dst_type == DBTYPE_VARIANT) return S_OK;
412         return S_FALSE;
413     }
414
415     if(dst_type & DBTYPE_ARRAY)
416     {
417         if(!array_type(dst_base_type)) return S_FALSE;
418         if(src_type == DBTYPE_IDISPATCH || src_type == DBTYPE_VARIANT) return S_OK;
419         return S_FALSE;
420     }
421
422     if(dst_type & DBTYPE_BYREF)
423         if(dst_base_type != DBTYPE_BYTES && dst_base_type != DBTYPE_STR && dst_base_type != DBTYPE_WSTR)
424             return S_FALSE;
425
426     switch(get_dbtype_class(src_base_type))
427     {
428     case DBTYPE_EMPTY:
429         if(common_class(dst_class)) return S_OK;
430         switch(dst_class)
431         {
432         case DBTYPE_DATE:
433         case DBTYPE_GUID:
434             return S_OK;
435         default:
436             if(dst_base_type == DBTYPE_DBTIMESTAMP) return S_OK;
437             return S_FALSE;
438         }
439
440     case DBTYPE_NULL:
441         switch(dst_base_type)
442         {
443         case DBTYPE_NULL:
444         case DBTYPE_VARIANT: return S_OK;
445         default: return S_FALSE;
446         }
447
448     case DBTYPE_I4:
449         if(dst_base_type == DBTYPE_BYTES) return S_OK;
450         /* fall through */
451     case DBTYPE_I2:
452         if(dst_base_type == DBTYPE_DATE) return S_OK;
453         /* fall through */
454     case DBTYPE_DECIMAL:
455         if(common_class(dst_class)) return S_OK;
456         if(dst_class == DBTYPE_DBDATE) return S_OK;
457         return S_FALSE;
458
459     case DBTYPE_BOOL:
460         if(dst_base_type == DBTYPE_DATE) return S_OK;
461     case DBTYPE_NUMERIC:
462     case DBTYPE_CY:
463         if(common_class(dst_class)) return S_OK;
464         return S_FALSE;
465
466     case DBTYPE_I8:
467         if(common_class(dst_class)) return S_OK;
468         if(dst_base_type == DBTYPE_BYTES) return S_OK;
469         return S_FALSE;
470
471     case DBTYPE_DATE:
472         switch(dst_class)
473         {
474         case DBTYPE_EMPTY:
475         case DBTYPE_NULL:
476         case DBTYPE_I2:
477         case DBTYPE_I4:
478         case DBTYPE_BSTR:
479         case DBTYPE_BOOL:
480         case DBTYPE_VARIANT:
481         case DBTYPE_I8:
482         case DBTYPE_DATE:
483         case DBTYPE_DBDATE:
484             return S_OK;
485         default: return S_FALSE;
486         }
487
488     case DBTYPE_IDISPATCH:
489     case DBTYPE_VARIANT:
490         switch(dst_base_type)
491         {
492         case DBTYPE_IDISPATCH:
493         case DBTYPE_ERROR:
494         case DBTYPE_IUNKNOWN:
495             return S_OK;
496         }
497         /* fall through */
498     case DBTYPE_BSTR:
499         if(common_class(dst_class)) return S_OK;
500         switch(dst_class)
501         {
502         case DBTYPE_DATE:
503         case DBTYPE_GUID:
504         case DBTYPE_BYTES:
505         case DBTYPE_DBDATE:
506             return S_OK;
507         default: return S_FALSE;
508         }
509
510     case DBTYPE_ERROR:
511         switch(dst_base_type)
512         {
513         case DBTYPE_BSTR:
514         case DBTYPE_ERROR:
515         case DBTYPE_VARIANT:
516         case DBTYPE_WSTR:
517             return S_OK;
518         default: return S_FALSE;
519         }
520
521     case DBTYPE_IUNKNOWN:
522         switch(dst_base_type)
523         {
524         case DBTYPE_EMPTY:
525         case DBTYPE_NULL:
526         case DBTYPE_IDISPATCH:
527         case DBTYPE_VARIANT:
528         case DBTYPE_IUNKNOWN:
529             return S_OK;
530         default: return S_FALSE;
531         }
532
533     case DBTYPE_BYTES:
534         if(dst_class == DBTYPE_I4 || dst_class == DBTYPE_I8) return S_OK;
535         /* fall through */
536     case DBTYPE_GUID:
537         switch(dst_class)
538         {
539         case DBTYPE_EMPTY:
540         case DBTYPE_NULL:
541         case DBTYPE_BSTR:
542         case DBTYPE_VARIANT:
543         case DBTYPE_GUID:
544         case DBTYPE_BYTES:
545             return S_OK;
546         default: return S_FALSE;
547         }
548
549     case DBTYPE_DBDATE:
550         switch(dst_class)
551         {
552         case DBTYPE_EMPTY:
553         case DBTYPE_NULL:
554         case DBTYPE_DATE:
555         case DBTYPE_BSTR:
556         case DBTYPE_VARIANT:
557         case DBTYPE_DBDATE:
558             return S_OK;
559         default: return S_FALSE;
560         }
561
562     }
563     return S_FALSE;
564 }
565
566 static HRESULT WINAPI convert_GetConversionSize(IDataConvert* iface,
567                                                 DBTYPE wSrcType, DBTYPE wDstType,
568                                                 DBLENGTH *pcbSrcLength, DBLENGTH *pcbDstLength,
569                                                 void *pSrc)
570 {
571     convert *This = impl_from_IDataConvert(iface);
572     FIXME("(%p)->(%d, %d, %p, %p, %p): stub\n", This, wSrcType, wDstType, pcbSrcLength, pcbDstLength, pSrc);
573
574     return E_NOTIMPL;
575 }
576
577 static const struct IDataConvertVtbl convert_vtbl =
578 {
579     convert_QueryInterface,
580     convert_AddRef,
581     convert_Release,
582     convert_DataConvert,
583     convert_CanConvert,
584     convert_GetConversionSize
585 };
586
587 static HRESULT WINAPI dcinfo_QueryInterface(IDCInfo* iface, REFIID riid, void **obj)
588 {
589     convert *This = impl_from_IDCInfo(iface);
590
591     return IDataConvert_QueryInterface((IDataConvert *)This, riid, obj);
592 }
593
594 static ULONG WINAPI dcinfo_AddRef(IDCInfo* iface)
595 {
596     convert *This = impl_from_IDCInfo(iface);
597
598     return IDataConvert_AddRef((IDataConvert *)This);
599 }
600
601 static ULONG WINAPI dcinfo_Release(IDCInfo* iface)
602 {
603     convert *This = impl_from_IDCInfo(iface);
604
605     return IDataConvert_Release((IDataConvert *)This);
606 }
607
608 static HRESULT WINAPI dcinfo_GetInfo(IDCInfo *iface, ULONG num, DCINFOTYPE types[], DCINFO **info_ptr)
609 {
610     convert *This = impl_from_IDCInfo(iface);
611     ULONG i;
612     DCINFO *infos;
613
614     TRACE("(%p)->(%d, %p, %p)\n", This, num, types, info_ptr);
615
616     *info_ptr = infos = CoTaskMemAlloc(num * sizeof(*infos));
617     if(!infos) return E_OUTOFMEMORY;
618
619     for(i = 0; i < num; i++)
620     {
621         infos[i].eInfoType = types[i];
622         VariantInit(&infos[i].vData);
623
624         switch(types[i])
625         {
626         case DCINFOTYPE_VERSION:
627             V_VT(&infos[i].vData) = VT_UI4;
628             V_UI4(&infos[i].vData) = This->version;
629             break;
630         }
631     }
632
633     return S_OK;
634 }
635
636 static HRESULT WINAPI dcinfo_SetInfo(IDCInfo* iface, ULONG num, DCINFO info[])
637 {
638     convert *This = impl_from_IDCInfo(iface);
639     ULONG i;
640     HRESULT hr = S_OK;
641
642     TRACE("(%p)->(%d, %p)\n", This, num, info);
643
644     for(i = 0; i < num; i++)
645     {
646         switch(info[i].eInfoType)
647         {
648         case DCINFOTYPE_VERSION:
649             if(V_VT(&info[i].vData) != VT_UI4)
650             {
651                 FIXME("VERSION with vt %x\n", V_VT(&info[i].vData));
652                 hr = DB_S_ERRORSOCCURRED;
653                 break;
654             }
655             This->version = V_UI4(&info[i].vData);
656             break;
657
658         default:
659             FIXME("Unhandled info type %d (vt %x)\n", info[i].eInfoType, V_VT(&info[i].vData));
660         }
661     }
662     return hr;
663 }
664
665 static const struct IDCInfoVtbl dcinfo_vtbl =
666 {
667     dcinfo_QueryInterface,
668     dcinfo_AddRef,
669     dcinfo_Release,
670     dcinfo_GetInfo,
671     dcinfo_SetInfo
672 };
673
674 HRESULT create_oledb_convert(IUnknown *outer, void **obj)
675 {
676     convert *This;
677
678     TRACE("(%p, %p)\n", outer, obj);
679
680     *obj = NULL;
681
682     if(outer) return CLASS_E_NOAGGREGATION;
683
684     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
685     if(!This) return E_OUTOFMEMORY;
686
687     This->lpVtbl = &convert_vtbl;
688     This->lpDCInfoVtbl = &dcinfo_vtbl;
689     This->ref = 1;
690     This->version = 0x110;
691
692     *obj = &This->lpVtbl;
693
694     return S_OK;
695 }