File: comintf.c

package info (click to toggle)
camlidl 1.12-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,592 kB
  • sloc: ml: 5,238; ansic: 945; cpp: 908; makefile: 358; xml: 213; sh: 74
file content (271 lines) | stat: -rw-r--r-- 8,211 bytes parent folder | download | duplicates (2)
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
36
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
168
169
170
171
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
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
/***********************************************************************/
/*                                                                     */
/*                              CamlIDL                                */
/*                                                                     */
/*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         */
/*                                                                     */
/*  Copyright 1999 Institut National de Recherche en Informatique et   */
/*  en Automatique.  All rights reserved.  This file is distributed    */
/*  under the terms of the GNU Library General Public License.         */
/*                                                                     */
/***********************************************************************/

/* $Id: comintf.c,v 1.11 2004-07-08 09:49:37 xleroy Exp $ */

/* Helper functions for handling COM interfaces */

#define CAML_NAME_SPACE

#include <string.h>
#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>
#include <caml/fail.h>
#include <caml/callback.h>
#include "camlidlruntime.h"
#include "comstuff.h"

int camlidl_num_components = 0;

static void camlidl_finalize_interface(value intf)
{
  interface IUnknown * i = (interface IUnknown *) Field(intf, 1);
  i->lpVtbl->Release(i);
}

value camlidl_pack_interface(void * intf, camlidl_ctx ctx)
{
  value res = caml_alloc_final(2, camlidl_finalize_interface, 0, 1);
  Field(res, 1) = (value) intf;
  if (ctx != NULL && (ctx->flags & CAMLIDL_ADDREF)) {
    struct IUnknown * i = (struct IUnknown *) intf;
    i->lpVtbl->AddRef(i);
  }
  return res;
}

void * camlidl_unpack_interface(value vintf, camlidl_ctx ctx)
{
  struct IUnknown * intf = (struct IUnknown *) Field(vintf, 1);
  if (ctx != NULL && (ctx->flags & CAMLIDL_ADDREF)) {
    intf->lpVtbl->AddRef(intf);
  }
  return (void *) intf;
}

value camlidl_make_interface(void * vtbl, value caml_object, IID * iid,
                             int has_dispatch)
{
  struct camlidl_component * comp =
    (struct camlidl_component *) caml_stat_alloc(sizeof(struct camlidl_component));
  comp->numintfs = 1;
  comp->refcount = 1;
  comp->intf[0].vtbl = vtbl;
  comp->intf[0].caml_object = caml_object;
  comp->intf[0].iid = iid;
  comp->intf[0].comp = comp;
#ifdef _WIN32
  comp->intf[0].typeinfo = has_dispatch ? camlidl_find_typeinfo(iid) : NULL;
#else
  if (has_dispatch)
    camlidl_error(0, "Com.make_xxx", "Dispatch interfaces not supported");
  comp->intf[0].typeinfo = NULL;
#endif
  caml_register_global_root(&(comp->intf[0].caml_object));
  InterlockedIncrement(&camlidl_num_components);
  return camlidl_pack_interface(&(comp->intf[0]), NULL);
}

/* Basic methods (QueryInterface, AddRef, Release) for COM objects
   encapsulating a Caml object */

HRESULT STDMETHODCALLTYPE
camlidl_QueryInterface(struct camlidl_intf * this, REFIID iid,
                       void ** object)
{
  struct camlidl_component * comp = this->comp;
  int i;
  for (i = 0; i < comp->numintfs; i++) {
    if (comp->intf[i].iid != NULL && IsEqualIID(iid, comp->intf[i].iid)) {
      *object = (void *) &(comp->intf[i]);
      InterlockedIncrement(&(comp->refcount));
      return S_OK;
    }
  }
#ifdef _WIN32
  if (IsEqualIID(iid, &IID_IUnknown)) {
    *object = (void *) this;
    InterlockedIncrement(&(comp->refcount));
    return S_OK;
  }
  if (this->typeinfo != NULL && IsEqualIID(iid, &IID_IDispatch)) {
    *object = (void *) this;
    InterlockedIncrement(&(comp->refcount));
    return S_OK;
  }
  if (IsEqualIID(iid, &IID_ISupportErrorInfo)) {
    *object = (void *) camlidl_support_error_info(this);
    return S_OK;
  }
#endif
  *object = NULL;
  return E_NOINTERFACE;
}
  
ULONG STDMETHODCALLTYPE camlidl_AddRef(struct camlidl_intf * this)
{
  return InterlockedIncrement(&(this->comp->refcount));
}

ULONG STDMETHODCALLTYPE camlidl_Release(struct camlidl_intf * this)
{
  struct camlidl_component * comp = this->comp;
  ULONG newrefcount = InterlockedDecrement(&(comp->refcount));
  int i;

  if (newrefcount == 0) {
    for (i = 0; i < comp->numintfs; i++) {
      struct camlidl_intf * intf = &(comp->intf[i]);
      caml_remove_global_root(&(intf->caml_object));
      if (intf->typeinfo != NULL) {
        struct IUnknown * i = (struct IUnknown *) intf->typeinfo;
        i->lpVtbl->Release(i);
      }
    }
    caml_stat_free(comp);
    InterlockedDecrement(&camlidl_num_components);
  }
  return newrefcount;
}

/* Query a COM interface */

value camlidl_com_queryInterface(value vintf, value viid)
{
  void * res;
  HRESULT hr;
  interface IUnknown * intf =
    (interface IUnknown *) camlidl_unpack_interface(vintf, NULL);
  hr = intf->lpVtbl->QueryInterface(intf, &GUID_val(viid), &res);
  if (FAILED(hr))
    camlidl_error(hr, "Com.queryInterface", "Interface not available");
  return camlidl_pack_interface(res, NULL);
}

/* Combine the interfaces of two Caml components */

#define is_a_caml_interface(i) \
  ((void *) (((interface IUnknown *) i)->lpVtbl->QueryInterface) == \
   (void *) camlidl_QueryInterface)

value camlidl_com_combine(value vintf1, value vintf2)
{
  struct camlidl_intf * i1, * i2;
  struct camlidl_component * c1, * c2, * c;
  int n, i;

  i1 = camlidl_unpack_interface(vintf1, NULL);
  i2 = camlidl_unpack_interface(vintf2, NULL);
  if (! is_a_caml_interface(i1) || ! is_a_caml_interface(i2))
    camlidl_error(CLASS_E_NOAGGREGATION, "Com.combine",
                  "Not a Caml interface");
  c1 = i1->comp;
  c2 = i2->comp;
  n = c1->numintfs + c2->numintfs;
  c = (struct camlidl_component *)
        caml_stat_alloc(sizeof(struct camlidl_component) +
                        sizeof(struct camlidl_intf) * (n - 1));
  InterlockedIncrement(&camlidl_num_components);
  c->numintfs = n;
  c->refcount = 1;
  for (i = 0; i < c1->numintfs; i++)
    c->intf[i] = c1->intf[i];
  for (i = 0; i < c2->numintfs; i++)
    c->intf[c1->numintfs + i] = c2->intf[i];
  for (i = 0; i < n; i++) {
    caml_register_global_root(&(c->intf[i].caml_object));
    c->intf[i].comp = c;
  }
  return camlidl_pack_interface(c->intf + (i1 - c1->intf), NULL);
}

/* Create an instance of a component */

value camlidl_com_create_instance(value clsid, value iid)
{
#ifdef _WIN32
  void * instance;
  HRESULT res;

  res = CoCreateInstance(&GUID_val(clsid),
                         NULL,
                         CLSCTX_ALL,
                         &GUID_val(iid),
                         &instance);
  if (FAILED(res)) camlidl_error(res, "Com.create_instance", NULL);
  return camlidl_pack_interface(instance, NULL);
#else
  caml_invalid_argument("Com.create_instance not implemented");
#endif
}

/* Initialization, termination */

value camlidl_com_initialize(value unit)
{
#ifdef _WIN32
  OleInitialize(NULL);
#endif
  return Val_unit;
}

value camlidl_com_uninitialize(value unit)
{
#ifdef _WIN32
  OleUninitialize();
#endif
  return Val_unit;
}

/* Register a Caml component factory */

struct camlidl_comp * camlidl_registered_components = NULL;

value camlidl_com_register_factory(value compdata)
{
  struct camlidl_comp * c = caml_stat_alloc(sizeof(struct camlidl_comp));
  c->compdata = compdata;
  caml_register_global_root(&(c->compdata));
  c->next = camlidl_registered_components;
  camlidl_registered_components = c;
  return Val_unit;
}

/* Parse and allocate an UID */

value camlidl_com_parse_uid(value str)
{
  value res;
  int u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11;

  if (caml_string_length(str) != 36 ||
      sscanf(String_val(str),
             "%8x-%4x-%4x-%2x%2x-%2x%2x%2x%2x%2x%2x",
             &u1, &u2, &u3, &u4, &u5, &u6, &u7, &u8, &u9, &u10, &u11) != 11)
    camlidl_error(CO_E_IIDSTRING, "Com.clsid", "Badly formed GUID");
  res = caml_alloc_small((sizeof(GUID) + sizeof(value) - 1) / sizeof(value),
                         Abstract_tag);
  GUID_val(res).Data1 = u1;
  GUID_val(res).Data2 = u2;
  GUID_val(res).Data3 = u3;
  GUID_val(res).Data4[0] = u4;
  GUID_val(res).Data4[1] = u5;
  GUID_val(res).Data4[2] = u6;
  GUID_val(res).Data4[3] = u7;
  GUID_val(res).Data4[4] = u8;
  GUID_val(res).Data4[5] = u9;
  GUID_val(res).Data4[6] = u10;
  GUID_val(res).Data4[7] = u11;
  return res;
}