comctl32/tests: Fix the property sheet tests on older Windows versions.
[wine] / dlls / rpcrt4 / rpc_epmap.c
1 /*
2  * RPC endpoint mapper
3  *
4  * Copyright 2002 Greg Turner
5  * Copyright 2001 Ove Kåven, TransGaming Technologies
6  * Copyright 2008 Robert Shearman (for CodeWeavers)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include <stdarg.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28
29 #include "rpc.h"
30
31 #include "wine/debug.h"
32 #include "wine/exception.h"
33
34 #include "rpc_binding.h"
35 #include "epm.h"
36 #include "epm_towers.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(ole);
39
40 /* The "real" RPC portmapper endpoints that I know of are:
41  *
42  *  ncadg_ip_udp: 135
43  *  ncacn_ip_tcp: 135
44  *  ncacn_np: \\pipe\epmapper
45  *  ncalrpc: epmapper
46  *  ncacn_http: 593
47  *
48  * If the user's machine ran a DCE RPC daemon, it would
49  * probably be possible to connect to it, but there are many
50  * reasons not to, like:
51  *  - the user probably does *not* run one, and probably
52  *    shouldn't be forced to run one just for local COM
53  *  - very few Unix systems use DCE RPC... if they run a RPC
54  *    daemon at all, it's usually Sun RPC
55  *  - DCE RPC registrations are persistent and saved on disk,
56  *    while MS-RPC registrations are documented as non-persistent
57  *    and stored only in RAM, and auto-destroyed when the process
58  *    dies (something DCE RPC can't do)
59  *
60  * Of course, if the user *did* want to run a DCE RPC daemon anyway,
61  * there would be interoperability advantages, like the possibility
62  * of running a fully functional DCOM server using Wine...
63  */
64
65 static const struct epm_endpoints
66 {
67     const char *protseq;
68     const char *endpoint;
69 } epm_endpoints[] =
70 {
71     { "ncacn_np", "\\pipe\\epmapper" },
72     { "ncacn_ip_tcp", "135" },
73     { "ncacn_ip_udp", "135" },
74     { "ncalrpc", "epmapper" },
75     { "ncacn_http", "593" },
76 };
77
78 static BOOL start_rpcss(void)
79 {
80     PROCESS_INFORMATION pi;
81     STARTUPINFOW si;
82     static WCHAR cmd[6];
83     static const WCHAR rpcss[] = {'r','p','c','s','s',0};
84     BOOL rslt;
85
86     TRACE("\n");
87
88     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
89     ZeroMemory(&si, sizeof(STARTUPINFOA));
90     si.cb = sizeof(STARTUPINFOA);
91
92     memcpy(cmd, rpcss, sizeof(rpcss));
93
94     rslt = CreateProcessW(
95                           NULL,           /* executable */
96                           cmd,            /* command line */
97                           NULL,           /* process security attributes */
98                           NULL,           /* primary thread security attributes */
99                           FALSE,          /* inherit handles */
100                           0,              /* creation flags */
101                           NULL,           /* use parent's environment */
102                           NULL,           /* use parent's current directory */
103                           &si,            /* STARTUPINFO pointer */
104                           &pi             /* PROCESS_INFORMATION */
105                           );
106
107     if (rslt)
108     {
109         CloseHandle(pi.hProcess);
110         CloseHandle(pi.hThread);
111         Sleep(100);
112     }
113
114     return rslt;
115 }
116
117 static inline BOOL is_epm_destination_local(RPC_BINDING_HANDLE handle)
118 {
119     RpcBinding *bind = handle;
120     const char *protseq = bind->Protseq;
121     const char *network_addr = bind->NetworkAddr;
122
123     return ((!strcmp(protseq, "ncalrpc") && !network_addr) ||
124             (!strcmp(protseq, "ncacn_np") &&
125                 (!network_addr || !strcmp(network_addr, "."))));
126 }
127
128 static RPC_STATUS get_epm_handle_client(RPC_BINDING_HANDLE handle, RPC_BINDING_HANDLE *epm_handle)
129 {
130     RpcBinding *bind = handle;
131     const char * pszEndpoint = NULL;
132     RPC_STATUS status;
133     RpcBinding* epm_bind;
134     unsigned int i;
135
136     if (bind->server)
137         return RPC_S_INVALID_BINDING;
138
139     for (i = 0; i < sizeof(epm_endpoints)/sizeof(epm_endpoints[0]); i++)
140         if (!strcmp(bind->Protseq, epm_endpoints[i].protseq))
141             pszEndpoint = epm_endpoints[i].endpoint;
142
143     if (!pszEndpoint)
144     {
145         FIXME("no endpoint for the endpoint-mapper found for protseq %s\n", debugstr_a(bind->Protseq));
146         return RPC_S_PROTSEQ_NOT_SUPPORTED;
147     }
148
149     status = RpcBindingCopy(handle, epm_handle);
150     if (status != RPC_S_OK) return status;
151
152     epm_bind = *epm_handle;
153     if (epm_bind->AuthInfo)
154     {
155         /* don't bother with authenticating against the EPM by default
156         * (see EnableAuthEpResolution registry value) */
157         RpcAuthInfo_Release(epm_bind->AuthInfo);
158         epm_bind->AuthInfo = NULL;
159     }
160     RPCRT4_ResolveBinding(epm_bind, pszEndpoint);
161     TRACE("RPC_S_OK\n");
162     return RPC_S_OK;
163 }
164
165 static RPC_STATUS get_epm_handle_server(RPC_BINDING_HANDLE *epm_handle)
166 {
167     unsigned char string_binding[] = "ncacn_np:.[\\\\pipe\\\\epmapper]";
168
169     return RpcBindingFromStringBindingA(string_binding, epm_handle);
170 }
171
172 static LONG WINAPI rpc_filter(EXCEPTION_POINTERS *__eptr)
173 {
174     switch (GetExceptionCode())
175     {
176         case EXCEPTION_ACCESS_VIOLATION:
177         case EXCEPTION_ILLEGAL_INSTRUCTION:
178             return EXCEPTION_CONTINUE_SEARCH;
179         default:
180             return EXCEPTION_EXECUTE_HANDLER;
181     }
182 }
183
184 /***********************************************************************
185  *             RpcEpRegisterA (RPCRT4.@)
186  */
187 RPC_STATUS WINAPI RpcEpRegisterA( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
188                                   UUID_VECTOR *UuidVector, RPC_CSTR Annotation )
189 {
190   PRPC_SERVER_INTERFACE If = IfSpec;
191   ULONG i;
192   RPC_STATUS status = RPC_S_OK;
193   error_status_t status2;
194   ept_entry_t *entries;
195   handle_t handle;
196
197   TRACE("(%p,%p,%p,%s)\n", IfSpec, BindingVector, UuidVector, debugstr_a((char*)Annotation));
198   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
199   for (i=0; i<BindingVector->Count; i++) {
200     RpcBinding* bind = BindingVector->BindingH[i];
201     TRACE(" protseq[%d]=%s\n", i, debugstr_a(bind->Protseq));
202     TRACE(" endpoint[%d]=%s\n", i, debugstr_a(bind->Endpoint));
203   }
204   if (UuidVector) {
205     for (i=0; i<UuidVector->Count; i++)
206       TRACE(" obj[%d]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
207   }
208
209   if (!BindingVector->Count) return RPC_S_OK;
210
211   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
212   if (!entries)
213       return RPC_S_OUT_OF_MEMORY;
214
215   status = get_epm_handle_server(&handle);
216   if (status != RPC_S_OK)
217   {
218     HeapFree(GetProcessHeap(), 0, entries);
219     return status;
220   }
221
222   for (i = 0; i < BindingVector->Count; i++)
223   {
224       unsigned j;
225       RpcBinding* bind = BindingVector->BindingH[i];
226       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
227       {
228           int len = strlen((char *)Annotation);
229           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
230                                   bind->Protseq, bind->Endpoint,
231                                   bind->NetworkAddr,
232                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
233           if (status != RPC_S_OK) break;
234
235           if (UuidVector)
236               memcpy(&entries[i * UuidVector->Count].object, &UuidVector->Uuid[j], sizeof(GUID));
237           else
238               memset(&entries[i].object, 0, sizeof(entries[i].object));
239           memcpy(entries[i].annotation, Annotation, min(len + 1, ept_max_annotation_size));
240       }
241   }
242
243   if (status == RPC_S_OK)
244   {
245       while (TRUE)
246       {
247           __TRY
248           {
249               ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
250                          entries, TRUE, &status2);
251           }
252           __EXCEPT(rpc_filter)
253           {
254               status2 = GetExceptionCode();
255           }
256           __ENDTRY
257           if (status2 == RPC_S_SERVER_UNAVAILABLE &&
258               is_epm_destination_local(handle))
259           {
260               if (start_rpcss())
261                   continue;
262           }
263           if (status2 != RPC_S_OK)
264               ERR("ept_insert failed with error %d\n", status2);
265           status = status2; /* FIXME: convert status? */
266           break;
267       }
268   }
269   RpcBindingFree(&handle);
270
271   for (i = 0; i < BindingVector->Count; i++)
272   {
273       unsigned j;
274       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
275           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
276   }
277
278   HeapFree(GetProcessHeap(), 0, entries);
279
280   return status;
281 }
282
283 /***********************************************************************
284  *             RpcEpRegisterW (RPCRT4.@)
285  */
286 RPC_STATUS WINAPI RpcEpRegisterW( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
287                                   UUID_VECTOR *UuidVector, RPC_WSTR Annotation )
288 {
289   LPSTR annA = RPCRT4_strdupWtoA(Annotation);
290   RPC_STATUS status;
291
292   status = RpcEpRegisterA(IfSpec, BindingVector, UuidVector, (RPC_CSTR)annA);
293
294   HeapFree(GetProcessHeap(), 0, annA);
295   return status;
296 }
297
298 /***********************************************************************
299  *             RpcEpUnregister (RPCRT4.@)
300  */
301 RPC_STATUS WINAPI RpcEpUnregister( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
302                                    UUID_VECTOR *UuidVector )
303 {
304   PRPC_SERVER_INTERFACE If = IfSpec;
305   ULONG i;
306   RPC_STATUS status = RPC_S_OK;
307   error_status_t status2;
308   ept_entry_t *entries;
309   handle_t handle;
310
311   TRACE("(%p,%p,%p)\n", IfSpec, BindingVector, UuidVector);
312   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
313   for (i=0; i<BindingVector->Count; i++) {
314     RpcBinding* bind = BindingVector->BindingH[i];
315     TRACE(" protseq[%d]=%s\n", i, debugstr_a(bind->Protseq));
316     TRACE(" endpoint[%d]=%s\n", i, debugstr_a(bind->Endpoint));
317   }
318   if (UuidVector) {
319     for (i=0; i<UuidVector->Count; i++)
320       TRACE(" obj[%d]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
321   }
322
323   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
324   if (!entries)
325       return RPC_S_OUT_OF_MEMORY;
326
327   status = get_epm_handle_server(&handle);
328   if (status != RPC_S_OK)
329   {
330     HeapFree(GetProcessHeap(), 0, entries);
331     return status;
332   }
333
334   for (i = 0; i < BindingVector->Count; i++)
335   {
336       unsigned j;
337       RpcBinding* bind = BindingVector->BindingH[i];
338       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
339       {
340           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
341                                   bind->Protseq, bind->Endpoint,
342                                   bind->NetworkAddr,
343                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
344           if (status != RPC_S_OK) break;
345
346           if (UuidVector)
347               memcpy(&entries[i * UuidVector->Count + j].object, &UuidVector->Uuid[j], sizeof(GUID));
348           else
349               memset(&entries[i].object, 0, sizeof(entries[i].object));
350       }
351   }
352
353   if (status == RPC_S_OK)
354   {
355       __TRY
356       {
357           ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
358                      entries, TRUE, &status2);
359       }
360       __EXCEPT(rpc_filter)
361       {
362           status2 = GetExceptionCode();
363       }
364       __ENDTRY
365       if (status2 == RPC_S_SERVER_UNAVAILABLE)
366           status2 = EPT_S_NOT_REGISTERED;
367       if (status2 != RPC_S_OK)
368           ERR("ept_insert failed with error %d\n", status2);
369       status = status2; /* FIXME: convert status? */
370   }
371   RpcBindingFree(&handle);
372
373   for (i = 0; i < BindingVector->Count; i++)
374   {
375       unsigned j;
376       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
377           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
378   }
379
380   HeapFree(GetProcessHeap(), 0, entries);
381
382   return status;
383 }
384
385 /***********************************************************************
386  *             RpcEpResolveBinding (RPCRT4.@)
387  */
388 RPC_STATUS WINAPI RpcEpResolveBinding( RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec )
389 {
390   PRPC_CLIENT_INTERFACE If = IfSpec;
391   RpcBinding* bind = Binding;
392   RPC_STATUS status;
393   error_status_t status2;
394   handle_t handle;
395   ept_lookup_handle_t entry_handle = NULL;
396   twr_t *tower;
397   twr_t *towers[4] = { NULL };
398   unsigned32 num_towers, i;
399   GUID uuid = GUID_NULL;
400   char *resolved_endpoint = NULL;
401
402   TRACE("(%p,%p)\n", Binding, IfSpec);
403   TRACE(" protseq=%s\n", debugstr_a(bind->Protseq));
404   TRACE(" obj=%s\n", debugstr_guid(&bind->ObjectUuid));
405   TRACE(" networkaddr=%s\n", debugstr_a(bind->NetworkAddr));
406   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
407
408   /* just return for fully bound handles */
409   if (bind->Endpoint && (bind->Endpoint[0] != '\0'))
410     return RPC_S_OK;
411
412   status = get_epm_handle_client(Binding, &handle);
413   if (status != RPC_S_OK) return status;
414   
415   status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax, bind->Protseq,
416                           ((RpcBinding *)handle)->Endpoint,
417                           bind->NetworkAddr, &tower);
418   if (status != RPC_S_OK)
419   {
420       WARN("couldn't get tower\n");
421       RpcBindingFree(&handle);
422       return status;
423   }
424
425   while (TRUE)
426   {
427     __TRY
428     {
429       ept_map(handle, &uuid, tower, &entry_handle, sizeof(towers)/sizeof(towers[0]), &num_towers, towers, &status2);
430       /* FIXME: translate status2? */
431     }
432     __EXCEPT(rpc_filter)
433     {
434       status2 = GetExceptionCode();
435     }
436     __ENDTRY
437     if (status2 == RPC_S_SERVER_UNAVAILABLE &&
438         is_epm_destination_local(handle))
439     {
440       if (start_rpcss())
441         continue;
442     }
443     break;
444   };
445
446   RpcBindingFree(&handle);
447   I_RpcFree(tower);
448
449   if (status2 != RPC_S_OK)
450   {
451     ERR("ept_map failed for ifid %s, protseq %s, networkaddr %s\n", debugstr_guid(&If->TransferSyntax.SyntaxGUID), bind->Protseq, bind->NetworkAddr);
452     return status2;
453   }
454
455   for (i = 0; i < num_towers; i++)
456   {
457     /* only parse the tower if we haven't already found a suitable
458     * endpoint, otherwise just free the tower */
459     if (!resolved_endpoint)
460     {
461       status = TowerExplode(towers[i], NULL, NULL, NULL, &resolved_endpoint, NULL);
462       TRACE("status = %d\n", status);
463     }
464     I_RpcFree(towers[i]);
465   }
466
467   if (resolved_endpoint)
468   {
469     RPCRT4_ResolveBinding(Binding, resolved_endpoint);
470     I_RpcFree(resolved_endpoint);
471     return RPC_S_OK;
472   }
473
474   WARN("couldn't find an endpoint\n");
475   return EPT_S_NOT_REGISTERED;
476 }
477
478 /*****************************************************************************
479  * TowerExplode (RPCRT4.@)
480  */
481 RPC_STATUS WINAPI TowerExplode(
482     const twr_t *tower, PRPC_SYNTAX_IDENTIFIER object, PRPC_SYNTAX_IDENTIFIER syntax,
483     char **protseq, char **endpoint, char **address)
484 {
485     size_t tower_size;
486     RPC_STATUS status;
487     const unsigned char *p;
488     u_int16 floor_count;
489     const twr_uuid_floor_t *object_floor;
490     const twr_uuid_floor_t *syntax_floor;
491
492     TRACE("(%p, %p, %p, %p, %p, %p)\n", tower, object, syntax, protseq,
493           endpoint, address);
494
495     if (protseq)
496         *protseq = NULL;
497     if (endpoint)
498         *endpoint = NULL;
499     if (address)
500         *address = NULL;
501
502     tower_size = tower->tower_length;
503
504     if (tower_size < sizeof(u_int16))
505         return EPT_S_NOT_REGISTERED;
506
507     p = &tower->tower_octet_string[0];
508
509     floor_count = *(const u_int16 *)p;
510     p += sizeof(u_int16);
511     tower_size -= sizeof(u_int16);
512     TRACE("floor_count: %d\n", floor_count);
513     /* FIXME: should we do something with the floor count? at the moment we don't */
514
515     if (tower_size < sizeof(*object_floor) + sizeof(*syntax_floor))
516         return EPT_S_NOT_REGISTERED;
517
518     object_floor = (const twr_uuid_floor_t *)p;
519     p += sizeof(*object_floor);
520     tower_size -= sizeof(*object_floor);
521     syntax_floor = (const twr_uuid_floor_t *)p;
522     p += sizeof(*syntax_floor);
523     tower_size -= sizeof(*syntax_floor);
524
525     if ((object_floor->count_lhs != sizeof(object_floor->protid) +
526         sizeof(object_floor->uuid) + sizeof(object_floor->major_version)) ||
527         (object_floor->protid != EPM_PROTOCOL_UUID) ||
528         (object_floor->count_rhs != sizeof(object_floor->minor_version)))
529         return EPT_S_NOT_REGISTERED;
530
531     if ((syntax_floor->count_lhs != sizeof(syntax_floor->protid) +
532         sizeof(syntax_floor->uuid) + sizeof(syntax_floor->major_version)) ||
533         (syntax_floor->protid != EPM_PROTOCOL_UUID) ||
534         (syntax_floor->count_rhs != sizeof(syntax_floor->minor_version)))
535         return EPT_S_NOT_REGISTERED;
536
537     status = RpcTransport_ParseTopOfTower(p, tower_size, protseq, address, endpoint);
538     if ((status == RPC_S_OK) && syntax && object)
539     {
540         syntax->SyntaxGUID = syntax_floor->uuid;
541         syntax->SyntaxVersion.MajorVersion = syntax_floor->major_version;
542         syntax->SyntaxVersion.MinorVersion = syntax_floor->minor_version;
543         object->SyntaxGUID = object_floor->uuid;
544         object->SyntaxVersion.MajorVersion = object_floor->major_version;
545         object->SyntaxVersion.MinorVersion = object_floor->minor_version;
546     }
547     return status;
548 }
549
550 /***********************************************************************
551  *             TowerConstruct (RPCRT4.@)
552  */
553 RPC_STATUS WINAPI TowerConstruct(
554     const RPC_SYNTAX_IDENTIFIER *object, const RPC_SYNTAX_IDENTIFIER *syntax,
555     const char *protseq, const char *endpoint, const char *address,
556     twr_t **tower)
557 {
558     size_t tower_size;
559     RPC_STATUS status;
560     unsigned char *p;
561     twr_uuid_floor_t *object_floor;
562     twr_uuid_floor_t *syntax_floor;
563
564     TRACE("(%p, %p, %s, %s, %s, %p)\n", object, syntax, debugstr_a(protseq),
565           debugstr_a(endpoint), debugstr_a(address), tower);
566
567     *tower = NULL;
568
569     status = RpcTransport_GetTopOfTower(NULL, &tower_size, protseq, address, endpoint);
570
571     if (status != RPC_S_OK)
572         return status;
573
574     tower_size += sizeof(u_int16) + sizeof(*object_floor) + sizeof(*syntax_floor);
575     *tower = I_RpcAllocate(FIELD_OFFSET(twr_t, tower_octet_string[tower_size]));
576     if (!*tower)
577         return RPC_S_OUT_OF_RESOURCES;
578
579     (*tower)->tower_length = tower_size;
580     p = &(*tower)->tower_octet_string[0];
581     *(u_int16 *)p = 5; /* number of floors */
582     p += sizeof(u_int16);
583     object_floor = (twr_uuid_floor_t *)p;
584     p += sizeof(*object_floor);
585     syntax_floor = (twr_uuid_floor_t *)p;
586     p += sizeof(*syntax_floor);
587
588     object_floor->count_lhs = sizeof(object_floor->protid) + sizeof(object_floor->uuid) +
589                               sizeof(object_floor->major_version);
590     object_floor->protid = EPM_PROTOCOL_UUID;
591     object_floor->count_rhs = sizeof(object_floor->minor_version);
592     object_floor->uuid = object->SyntaxGUID;
593     object_floor->major_version = object->SyntaxVersion.MajorVersion;
594     object_floor->minor_version = object->SyntaxVersion.MinorVersion;
595
596     syntax_floor->count_lhs = sizeof(syntax_floor->protid) + sizeof(syntax_floor->uuid) +
597                               sizeof(syntax_floor->major_version);
598     syntax_floor->protid = EPM_PROTOCOL_UUID;
599     syntax_floor->count_rhs = sizeof(syntax_floor->minor_version);
600     syntax_floor->uuid = syntax->SyntaxGUID;
601     syntax_floor->major_version = syntax->SyntaxVersion.MajorVersion;
602     syntax_floor->minor_version = syntax->SyntaxVersion.MinorVersion;
603
604     status = RpcTransport_GetTopOfTower(p, &tower_size, protseq, address, endpoint);
605     if (status != RPC_S_OK)
606     {
607         I_RpcFree(*tower);
608         *tower = NULL;
609         return status;
610     }
611     return RPC_S_OK;
612 }
613
614 void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
615 {
616     return HeapAlloc(GetProcessHeap(), 0, len);
617 }
618
619 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
620 {
621     HeapFree(GetProcessHeap(), 0, ptr);
622 }