cryptui: Use CryptQueryObject to import files in CryptUIWizImport.
[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 = (RpcBinding *)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 = (RpcBinding *)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 = (RpcBinding*)*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 = (PRPC_SERVER_INTERFACE)IfSpec;
191   unsigned long 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 = (RpcBinding*)(BindingVector->BindingH[i]);
201     TRACE(" protseq[%ld]=%s\n", i, debugstr_a(bind->Protseq));
202     TRACE(" endpoint[%ld]=%s\n", i, debugstr_a(bind->Endpoint));
203   }
204   if (UuidVector) {
205     for (i=0; i<UuidVector->Count; i++)
206       TRACE(" obj[%ld]=%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 = (RpcBinding*)(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  *             RpcEpUnregister (RPCRT4.@)
285  */
286 RPC_STATUS WINAPI RpcEpUnregister( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR *BindingVector,
287                                    UUID_VECTOR *UuidVector )
288 {
289   PRPC_SERVER_INTERFACE If = (PRPC_SERVER_INTERFACE)IfSpec;
290   unsigned long i;
291   RPC_STATUS status = RPC_S_OK;
292   error_status_t status2;
293   ept_entry_t *entries;
294   handle_t handle;
295
296   TRACE("(%p,%p,%p)\n", IfSpec, BindingVector, UuidVector);
297   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
298   for (i=0; i<BindingVector->Count; i++) {
299     RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[i]);
300     TRACE(" protseq[%ld]=%s\n", i, debugstr_a(bind->Protseq));
301     TRACE(" endpoint[%ld]=%s\n", i, debugstr_a(bind->Endpoint));
302   }
303   if (UuidVector) {
304     for (i=0; i<UuidVector->Count; i++)
305       TRACE(" obj[%ld]=%s\n", i, debugstr_guid(UuidVector->Uuid[i]));
306   }
307
308   entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entries) * BindingVector->Count * (UuidVector ? UuidVector->Count : 1));
309   if (!entries)
310       return RPC_S_OUT_OF_MEMORY;
311
312   status = get_epm_handle_server(&handle);
313   if (status != RPC_S_OK)
314   {
315     HeapFree(GetProcessHeap(), 0, entries);
316     return status;
317   }
318
319   for (i = 0; i < BindingVector->Count; i++)
320   {
321       unsigned j;
322       RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[i]);
323       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
324       {
325           status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax,
326                                   bind->Protseq, bind->Endpoint,
327                                   bind->NetworkAddr,
328                                   &entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
329           if (status != RPC_S_OK) break;
330
331           if (UuidVector)
332               memcpy(&entries[i * UuidVector->Count + j].object, &UuidVector->Uuid[j], sizeof(GUID));
333           else
334               memset(&entries[i].object, 0, sizeof(entries[i].object));
335       }
336   }
337
338   if (status == RPC_S_OK)
339   {
340       __TRY
341       {
342           ept_insert(handle, BindingVector->Count * (UuidVector ? UuidVector->Count : 1),
343                      entries, TRUE, &status2);
344       }
345       __EXCEPT(rpc_filter)
346       {
347           status2 = GetExceptionCode();
348       }
349       __ENDTRY
350       if (status2 == RPC_S_SERVER_UNAVAILABLE)
351           status2 = EPT_S_NOT_REGISTERED;
352       if (status2 != RPC_S_OK)
353           ERR("ept_insert failed with error %d\n", status2);
354       status = status2; /* FIXME: convert status? */
355   }
356   RpcBindingFree(&handle);
357
358   for (i = 0; i < BindingVector->Count; i++)
359   {
360       unsigned j;
361       for (j = 0; j < (UuidVector ? UuidVector->Count : 1); j++)
362           I_RpcFree(entries[i*(UuidVector ? UuidVector->Count : 1) + j].tower);
363   }
364
365   HeapFree(GetProcessHeap(), 0, entries);
366
367   return status;
368 }
369
370 /***********************************************************************
371  *             RpcEpResolveBinding (RPCRT4.@)
372  */
373 RPC_STATUS WINAPI RpcEpResolveBinding( RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec )
374 {
375   PRPC_CLIENT_INTERFACE If = (PRPC_CLIENT_INTERFACE)IfSpec;
376   RpcBinding* bind = (RpcBinding*)Binding;
377   RPC_STATUS status;
378   error_status_t status2;
379   handle_t handle;
380   ept_lookup_handle_t entry_handle = NULL;
381   twr_t *tower;
382   twr_t *towers[4] = { NULL };
383   unsigned32 num_towers, i;
384   GUID uuid = GUID_NULL;
385   char *resolved_endpoint = NULL;
386
387   TRACE("(%p,%p)\n", Binding, IfSpec);
388   TRACE(" protseq=%s\n", debugstr_a(bind->Protseq));
389   TRACE(" obj=%s\n", debugstr_guid(&bind->ObjectUuid));
390   TRACE(" networkaddr=%s\n", debugstr_a(bind->NetworkAddr));
391   TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID));
392
393   /* just return for fully bound handles */
394   if (bind->Endpoint && (bind->Endpoint[0] != '\0'))
395     return RPC_S_OK;
396
397   status = get_epm_handle_client(Binding, &handle);
398   if (status != RPC_S_OK) return status;
399   
400   status = TowerConstruct(&If->InterfaceId, &If->TransferSyntax, bind->Protseq,
401                           ((RpcBinding *)handle)->Endpoint,
402                           bind->NetworkAddr, &tower);
403   if (status != RPC_S_OK)
404   {
405       WARN("couldn't get tower\n");
406       RpcBindingFree(&handle);
407       return status;
408   }
409
410   while (TRUE)
411   {
412     __TRY
413     {
414       ept_map(handle, &uuid, tower, &entry_handle, sizeof(towers)/sizeof(towers[0]), &num_towers, towers, &status2);
415       /* FIXME: translate status2? */
416     }
417     __EXCEPT(rpc_filter)
418     {
419       status2 = GetExceptionCode();
420     }
421     __ENDTRY
422     if (status2 == RPC_S_SERVER_UNAVAILABLE &&
423         is_epm_destination_local(handle))
424     {
425       if (start_rpcss())
426         continue;
427     }
428     break;
429   };
430
431   RpcBindingFree(&handle);
432   I_RpcFree(tower);
433
434   if (status2 != RPC_S_OK)
435   {
436     ERR("ept_map failed for ifid %s, protseq %s, networkaddr %s\n", debugstr_guid(&If->TransferSyntax.SyntaxGUID), bind->Protseq, bind->NetworkAddr);
437     return status2;
438   }
439
440   for (i = 0; i < num_towers; i++)
441   {
442     /* only parse the tower if we haven't already found a suitable
443     * endpoint, otherwise just free the tower */
444     if (!resolved_endpoint)
445     {
446       status = TowerExplode(towers[i], NULL, NULL, NULL, &resolved_endpoint, NULL);
447       TRACE("status = %ld\n", status);
448     }
449     I_RpcFree(towers[i]);
450   }
451
452   if (resolved_endpoint)
453   {
454     RPCRT4_ResolveBinding(Binding, resolved_endpoint);
455     I_RpcFree(resolved_endpoint);
456     return RPC_S_OK;
457   }
458
459   WARN("couldn't find an endpoint\n");
460   return EPT_S_NOT_REGISTERED;
461 }
462
463 /*****************************************************************************
464  * TowerExplode (RPCRT4.@)
465  */
466 RPC_STATUS WINAPI TowerExplode(
467     const twr_t *tower, PRPC_SYNTAX_IDENTIFIER object, PRPC_SYNTAX_IDENTIFIER syntax,
468     char **protseq, char **endpoint, char **address)
469 {
470     size_t tower_size;
471     RPC_STATUS status;
472     const unsigned char *p;
473     u_int16 floor_count;
474     const twr_uuid_floor_t *object_floor;
475     const twr_uuid_floor_t *syntax_floor;
476
477     TRACE("(%p, %p, %p, %p, %p, %p)\n", tower, object, syntax, protseq,
478           endpoint, address);
479
480     if (protseq)
481         *protseq = NULL;
482     if (endpoint)
483         *endpoint = NULL;
484     if (address)
485         *address = NULL;
486
487     tower_size = tower->tower_length;
488
489     if (tower_size < sizeof(u_int16))
490         return EPT_S_NOT_REGISTERED;
491
492     p = &tower->tower_octet_string[0];
493
494     floor_count = *(const u_int16 *)p;
495     p += sizeof(u_int16);
496     tower_size -= sizeof(u_int16);
497     TRACE("floor_count: %d\n", floor_count);
498     /* FIXME: should we do something with the floor count? at the moment we don't */
499
500     if (tower_size < sizeof(*object_floor) + sizeof(*syntax_floor))
501         return EPT_S_NOT_REGISTERED;
502
503     object_floor = (const twr_uuid_floor_t *)p;
504     p += sizeof(*object_floor);
505     tower_size -= sizeof(*object_floor);
506     syntax_floor = (const twr_uuid_floor_t *)p;
507     p += sizeof(*syntax_floor);
508     tower_size -= sizeof(*syntax_floor);
509
510     if ((object_floor->count_lhs != sizeof(object_floor->protid) +
511         sizeof(object_floor->uuid) + sizeof(object_floor->major_version)) ||
512         (object_floor->protid != EPM_PROTOCOL_UUID) ||
513         (object_floor->count_rhs != sizeof(object_floor->minor_version)))
514         return EPT_S_NOT_REGISTERED;
515
516     if ((syntax_floor->count_lhs != sizeof(syntax_floor->protid) +
517         sizeof(syntax_floor->uuid) + sizeof(syntax_floor->major_version)) ||
518         (syntax_floor->protid != EPM_PROTOCOL_UUID) ||
519         (syntax_floor->count_rhs != sizeof(syntax_floor->minor_version)))
520         return EPT_S_NOT_REGISTERED;
521
522     status = RpcTransport_ParseTopOfTower(p, tower_size, protseq, address, endpoint);
523     if ((status == RPC_S_OK) && syntax && object)
524     {
525         syntax->SyntaxGUID = syntax_floor->uuid;
526         syntax->SyntaxVersion.MajorVersion = syntax_floor->major_version;
527         syntax->SyntaxVersion.MinorVersion = syntax_floor->minor_version;
528         object->SyntaxGUID = object_floor->uuid;
529         object->SyntaxVersion.MajorVersion = object_floor->major_version;
530         object->SyntaxVersion.MinorVersion = object_floor->minor_version;
531     }
532     return status;
533 }
534
535 /***********************************************************************
536  *             TowerConstruct (RPCRT4.@)
537  */
538 RPC_STATUS WINAPI TowerConstruct(
539     const RPC_SYNTAX_IDENTIFIER *object, const RPC_SYNTAX_IDENTIFIER *syntax,
540     const char *protseq, const char *endpoint, const char *address,
541     twr_t **tower)
542 {
543     size_t tower_size;
544     RPC_STATUS status;
545     unsigned char *p;
546     twr_uuid_floor_t *object_floor;
547     twr_uuid_floor_t *syntax_floor;
548
549     TRACE("(%p, %p, %s, %s, %s, %p)\n", object, syntax, debugstr_a(protseq),
550           debugstr_a(endpoint), debugstr_a(address), tower);
551
552     *tower = NULL;
553
554     status = RpcTransport_GetTopOfTower(NULL, &tower_size, protseq, address, endpoint);
555
556     if (status != RPC_S_OK)
557         return status;
558
559     tower_size += sizeof(u_int16) + sizeof(*object_floor) + sizeof(*syntax_floor);
560     *tower = I_RpcAllocate(FIELD_OFFSET(twr_t, tower_octet_string[tower_size]));
561     if (!*tower)
562         return RPC_S_OUT_OF_RESOURCES;
563
564     (*tower)->tower_length = tower_size;
565     p = &(*tower)->tower_octet_string[0];
566     *(u_int16 *)p = 5; /* number of floors */
567     p += sizeof(u_int16);
568     object_floor = (twr_uuid_floor_t *)p;
569     p += sizeof(*object_floor);
570     syntax_floor = (twr_uuid_floor_t *)p;
571     p += sizeof(*syntax_floor);
572
573     object_floor->count_lhs = sizeof(object_floor->protid) + sizeof(object_floor->uuid) +
574                               sizeof(object_floor->major_version);
575     object_floor->protid = EPM_PROTOCOL_UUID;
576     object_floor->count_rhs = sizeof(object_floor->minor_version);
577     object_floor->uuid = object->SyntaxGUID;
578     object_floor->major_version = object->SyntaxVersion.MajorVersion;
579     object_floor->minor_version = object->SyntaxVersion.MinorVersion;
580
581     syntax_floor->count_lhs = sizeof(syntax_floor->protid) + sizeof(syntax_floor->uuid) +
582                               sizeof(syntax_floor->major_version);
583     syntax_floor->protid = EPM_PROTOCOL_UUID;
584     syntax_floor->count_rhs = sizeof(syntax_floor->minor_version);
585     syntax_floor->uuid = syntax->SyntaxGUID;
586     syntax_floor->major_version = syntax->SyntaxVersion.MajorVersion;
587     syntax_floor->minor_version = syntax->SyntaxVersion.MinorVersion;
588
589     status = RpcTransport_GetTopOfTower(p, &tower_size, protseq, address, endpoint);
590     if (status != RPC_S_OK)
591     {
592         I_RpcFree(*tower);
593         *tower = NULL;
594         return status;
595     }
596     return RPC_S_OK;
597 }
598
599 void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len)
600 {
601     return HeapAlloc(GetProcessHeap(), 0, len);
602 }
603
604 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
605 {
606     HeapFree(GetProcessHeap(), 0, ptr);
607 }