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