Add a DCOM todo list.
[wine] / dlls / ole32 / compobj.c
1 /*
2  *      COMPOBJ library
3  *
4  *      Copyright 1995  Martin von Loewis
5  *      Copyright 1998  Justin Bradford
6  *      Copyright 1999  Francis Beaudet
7  *      Copyright 1999  Sylvain St-Germain
8  *      Copyright 2002  Marcus Meissner
9  *      Copyright 2004  Mike Hearn
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  * TODO list:           (items bunched together depend on each other)
26  *
27  *   - Switch wine_marshal_id to use IPIDs not IIDs
28  *   - Once that's done, replace wine_marshal_id with STDOBJREF
29  *
30  *   - Rewrite the CoLockObjectExternal code, it does totally the wrong
31  *     thing currently (should be controlling the stub manager)
32  *
33  *   - Implement the service control manager (in rpcss) to keep track
34  *     of registered class objects: ISCM::ServerRegisterClsid et al
35  *   - Implement the OXID resolver so we don't need magic pipe names for
36  *     clients and servers to meet up
37  *   - Flip our marshalling on top of the RPC runtime transport API,
38  *     so we no longer use named pipes to communicate
39  *   - Rework threading so re-entrant calls don't need to be sent on
40  *     the incoming pipe
41  *   - Implement RPC thread affinity (should fix InstallShield painting
42  *     problems)
43  *
44  *   - Implement IRemUnknown and marshalling for it, then use that for
45  *     reffing/unreffing the stub manager from a proxy instead of our
46  *     current hack of simply reffing the stub manager once when it's
47  *     registered.
48  *   - Implement table marshalling, then use it to let us do the final
49  *     rework of the threading
50  *
51  *   - Make our custom marshalling use NDR to be wire compatible with
52  *     native DCOM
53  *     
54  *        
55  */
56
57 #include "config.h"
58
59 #include <stdlib.h>
60 #include <stdarg.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <assert.h>
64
65 #define COBJMACROS
66 #define NONAMELESSUNION
67 #define NONAMELESSSTRUCT
68
69 #include "windef.h"
70 #include "winbase.h"
71 #include "winuser.h"
72 #include "objbase.h"
73 #include "ole2.h"
74 #include "ole2ver.h"
75 #include "rpc.h"
76 #include "winerror.h"
77 #include "winreg.h"
78 #include "wownt32.h"
79 #include "wine/unicode.h"
80 #include "objbase.h"
81 #include "ole32_main.h"
82 #include "compobj_private.h"
83
84 #include "wine/debug.h"
85
86 WINE_DEFAULT_DEBUG_CHANNEL(ole);
87
88 typedef LPCSTR LPCOLESTR16;
89
90 /****************************************************************************
91  * This section defines variables internal to the COM module.
92  *
93  * TODO: Most of these things will have to be made thread-safe.
94  */
95
96 static HRESULT COM_GetRegisteredClassObject(REFCLSID rclsid, DWORD dwClsContext, LPUNKNOWN*  ppUnk);
97 static void COM_RevokeAllClasses(void);
98 static void COM_ExternalLockFreeList(void);
99
100 const CLSID CLSID_StdGlobalInterfaceTable = { 0x00000323, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
101
102 APARTMENT MTA, *apts;
103
104 static CRITICAL_SECTION csApartment;
105 static CRITICAL_SECTION_DEBUG critsect_debug =
106 {
107     0, 0, &csApartment,
108     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
109       0, 0, { 0, (DWORD)(__FILE__ ": csApartment") }
110 };
111 static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
112
113 /*
114  * This lock count counts the number of times CoInitialize is called. It is
115  * decreased every time CoUninitialize is called. When it hits 0, the COM
116  * libraries are freed
117  */
118 static LONG s_COMLockCount = 0;
119
120 /*
121  * This linked list contains the list of registered class objects. These
122  * are mostly used to register the factories for out-of-proc servers of OLE
123  * objects.
124  *
125  * TODO: Make this data structure aware of inter-process communication. This
126  *       means that parts of this will be exported to the Wine Server.
127  */
128 typedef struct tagRegisteredClass
129 {
130   CLSID     classIdentifier;
131   LPUNKNOWN classObject;
132   DWORD     runContext;
133   DWORD     connectFlags;
134   DWORD     dwCookie;
135   LPSTREAM  pMarshaledData; /* FIXME: only really need to store OXID and IPID */
136   struct tagRegisteredClass* nextClass;
137 } RegisteredClass;
138
139 static RegisteredClass* firstRegisteredClass = NULL;
140
141 static CRITICAL_SECTION csRegisteredClassList;
142 static CRITICAL_SECTION_DEBUG class_cs_debug =
143 {
144     0, 0, &csRegisteredClassList,
145     { &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
146       0, 0, { 0, (DWORD)(__FILE__ ": csRegisteredClassList") }
147 };
148 static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };
149
150 /*****************************************************************************
151  * This section contains OpenDllList definitions
152  *
153  * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
154  * other functions that do LoadLibrary _without_ giving back a HMODULE.
155  * Without this list these handles would never be freed.
156  *
157  * FIXME: a DLL that says OK when asked for unloading is unloaded in the
158  * next unload-call but not before 600 sec.
159  */
160
161 typedef struct tagOpenDll {
162   HINSTANCE hLibrary;
163   struct tagOpenDll *next;
164 } OpenDll;
165
166 static OpenDll *openDllList = NULL; /* linked list of open dlls */
167
168 static CRITICAL_SECTION csOpenDllList;
169 static CRITICAL_SECTION_DEBUG dll_cs_debug =
170 {
171     0, 0, &csOpenDllList,
172     { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
173       0, 0, { 0, (DWORD)(__FILE__ ": csOpenDllList") }
174 };
175 static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
176
177 static const char aptWinClass[] = "WINE_OLE32_APT_CLASS";
178 static LRESULT CALLBACK COM_AptWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
179
180 static void COMPOBJ_DLLList_Add(HANDLE hLibrary);
181 static void COMPOBJ_DllList_FreeUnused(int Timeout);
182
183 void COMPOBJ_InitProcess( void )
184 {
185     WNDCLASSA wclass;
186
187     /* Dispatching to the correct thread in an apartment is done through
188      * window messages rather than RPC transports. When an interface is
189      * marshalled into another apartment in the same process, a window of the
190      * following class is created. The *caller* of CoMarshalInterface (ie the
191      * application) is responsible for pumping the message loop in that thread.
192      * The WM_USER messages which point to the RPCs are then dispatched to
193      * COM_AptWndProc by the user's code from the apartment in which the interface
194      * was unmarshalled.
195      */
196     memset(&wclass, 0, sizeof(wclass));
197     wclass.lpfnWndProc = &COM_AptWndProc;
198     wclass.hInstance = OLE32_hInstance;
199     wclass.lpszClassName = aptWinClass;
200     RegisterClassA(&wclass);
201 }
202
203 void COMPOBJ_UninitProcess( void )
204 {
205     UnregisterClassA(aptWinClass, OLE32_hInstance);
206 }
207
208 /******************************************************************************
209  * Manage apartments.
210  */
211
212
213 /* The multi-threaded apartment (MTA) contains zero or more threads interacting
214    with free threaded (ie thread safe) COM objects. There is only ever one MTA
215    in a process - you can enter it by calling CoInitializeEx(COINIT_MULTITHREADED)
216  */
217 static void COM_InitMTA(void)
218 {
219     /* OXIDs are object exporter IDs. Each apartment has an OXID, which is unique
220        within a network. That is, two different MTAs on different machines will have
221        different OXIDs.
222
223        This method of generating an OXID is therefore wrong as it doesn't work across
224        a network, but for local RPC only it's OK. We can distinguish between MTAs and
225        STAs because STAs use the thread ID as well, and no thread can have an ID of zero.
226
227        The algorithm Microsoft use is currently unknown.
228      */
229     MTA.oxid = ((OXID)GetCurrentProcessId() << 32);
230     InitializeCriticalSection(&MTA.cs);
231 }
232
233 static void COM_UninitMTA(void)
234 {
235     DeleteCriticalSection(&MTA.cs);
236     MTA.oxid = 0;
237 }
238
239 /* creates an apartment structure which stores OLE thread-local
240  * information. Call with COINIT_UNINITIALIZED to create an apartment
241  * that will be initialized with a model later. Note: do not call
242  * with COINIT_UNINITIALIZED if the apartment has already been initialized
243  * with a different COINIT value */
244 APARTMENT* COM_CreateApartment(DWORD model)
245 {
246     APARTMENT *apt;
247     BOOL create = (NtCurrentTeb()->ReservedForOle == NULL);
248
249     if (create)
250     {
251         apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APARTMENT));
252         apt->tid = GetCurrentThreadId();
253         DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
254                         GetCurrentProcess(), &apt->thread,
255                         THREAD_ALL_ACCESS, FALSE, 0);
256     }
257     else
258         apt = NtCurrentTeb()->ReservedForOle;
259
260     list_init(&apt->proxies);
261     list_init(&apt->stubmgrs);
262     apt->oidc = 1;
263     
264     apt->model = model;
265     if (model & COINIT_APARTMENTTHREADED) {
266       /* FIXME: how does windoze create OXIDs? */
267       apt->oxid = MTA.oxid | GetCurrentThreadId();
268       apt->win = CreateWindowA(aptWinClass, NULL, 0,
269                                0, 0, 0, 0,
270                                0, 0, OLE32_hInstance, NULL);
271       InitializeCriticalSection(&apt->cs);
272     }
273     else if (!(model & COINIT_UNINITIALIZED)) {
274       apt->parent = &MTA;
275       apt->oxid = MTA.oxid;
276     }
277     EnterCriticalSection(&csApartment);
278     if (create)
279     {
280         if (apts) apts->prev = apt;
281         apt->next = apts;
282         apts = apt;
283     }
284     LeaveCriticalSection(&csApartment);
285     NtCurrentTeb()->ReservedForOle = apt;
286     return apt;
287 }
288
289 static void COM_DestroyApartment(APARTMENT *apt)
290 {
291     EnterCriticalSection(&csApartment);
292     if (apt->prev) apt->prev->next = apt->next;
293     if (apt->next) apt->next->prev = apt->prev;
294     if (apts == apt) apts = apt->next;
295     apt->prev = NULL; apt->next = NULL;
296     LeaveCriticalSection(&csApartment);
297     if (apt->model & COINIT_APARTMENTTHREADED) {
298       /* disconnect proxies to release the corresponding stubs.
299        * It is confirmed in "Essential COM" in the sub-chapter on
300        * "Lifecycle Management and Marshalling" that the native version also
301        * disconnects proxies in this function. */
302       /* FIXME: this should also be called for MTA destruction, but that
303        * requires restructuring how apartments work slightly. */
304       MARSHAL_Disconnect_Proxies(apt);
305
306       if (apt->win) DestroyWindow(apt->win);
307       DeleteCriticalSection(&apt->cs);
308     }
309     CloseHandle(apt->thread);
310     HeapFree(GetProcessHeap(), 0, apt);
311 }
312
313 /* The given OXID must be local to this process: you cannot use apartment
314    windows to send RPCs to other processes. This all needs to move to rpcrt4 */
315
316 APARTMENT *COM_ApartmentFromOXID(OXID oxid)
317 {
318     APARTMENT *apt = NULL;
319
320     EnterCriticalSection(&csApartment);
321     apt = apts;
322     while (apt && apt->oxid != oxid) apt = apt->next;
323     LeaveCriticalSection(&csApartment);
324
325     return apt;
326 }
327
328 HWND COM_GetApartmentWin(OXID oxid)
329 {
330     APARTMENT *apt = COM_ApartmentFromOXID(oxid);
331
332     return apt ? apt->win : NULL;
333 }
334
335 /* Currently inter-thread marshalling is not fully implemented, so this does nothing */
336 static LRESULT CALLBACK COM_AptWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
337 {
338   return DefWindowProcA(hWnd, msg, wParam, lParam);
339
340
341 /*****************************************************************************
342  * This section contains OpenDllList implemantation
343  */
344
345 static void COMPOBJ_DLLList_Add(HANDLE hLibrary)
346 {
347     OpenDll *ptr;
348     OpenDll *tmp;
349
350     TRACE("\n");
351
352     EnterCriticalSection( &csOpenDllList );
353
354     if (openDllList == NULL) {
355         /* empty list -- add first node */
356         openDllList = (OpenDll*)HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
357         openDllList->hLibrary=hLibrary;
358         openDllList->next = NULL;
359     } else {
360         /* search for this dll */
361         int found = FALSE;
362         for (ptr = openDllList; ptr->next != NULL; ptr=ptr->next) {
363             if (ptr->hLibrary == hLibrary) {
364                 found = TRUE;
365                 break;
366             }
367         }
368         if (!found) {
369             /* dll not found, add it */
370             tmp = openDllList;
371             openDllList = (OpenDll*)HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
372             openDllList->hLibrary = hLibrary;
373             openDllList->next = tmp;
374         }
375     }
376
377     LeaveCriticalSection( &csOpenDllList );
378 }
379
380 static void COMPOBJ_DllList_FreeUnused(int Timeout)
381 {
382     OpenDll *curr, *next, *prev = NULL;
383     typedef HRESULT(*DllCanUnloadNowFunc)(void);
384     DllCanUnloadNowFunc DllCanUnloadNow;
385
386     TRACE("\n");
387
388     EnterCriticalSection( &csOpenDllList );
389
390     for (curr = openDllList; curr != NULL; ) {
391         DllCanUnloadNow = (DllCanUnloadNowFunc) GetProcAddress(curr->hLibrary, "DllCanUnloadNow");
392
393         if ( (DllCanUnloadNow != NULL) && (DllCanUnloadNow() == S_OK) ) {
394             next = curr->next;
395
396             TRACE("freeing %p\n", curr->hLibrary);
397             FreeLibrary(curr->hLibrary);
398
399             HeapFree(GetProcessHeap(), 0, curr);
400             if (curr == openDllList) {
401                 openDllList = next;
402             } else {
403               prev->next = next;
404             }
405
406             curr = next;
407         } else {
408             prev = curr;
409             curr = curr->next;
410         }
411     }
412
413     LeaveCriticalSection( &csOpenDllList );
414 }
415
416 /******************************************************************************
417  *           CoBuildVersion [COMPOBJ.1]
418  *           CoBuildVersion [OLE32.@]
419  *
420  * RETURNS
421  *      Current build version, hiword is majornumber, loword is minornumber
422  */
423 DWORD WINAPI CoBuildVersion(void)
424 {
425     TRACE("Returning version %d, build %d.\n", rmm, rup);
426     return (rmm<<16)+rup;
427 }
428
429 /******************************************************************************
430  *              CoInitialize    [OLE32.@]
431  *
432  * Initializes the COM libraries by calling CoInitializeEx with
433  * COINIT_APARTMENTTHREADED, ie it enters a STA thread.
434  *
435  * SEE ALSO
436  *   CoInitializeEx
437  */
438 HRESULT WINAPI CoInitialize(
439         LPVOID lpReserved       /* [in] pointer to win32 malloc interface
440                                    (obsolete, should be NULL) */
441 )
442 {
443   /*
444    * Just delegate to the newer method.
445    */
446   return CoInitializeEx(lpReserved, COINIT_APARTMENTTHREADED);
447 }
448
449 /******************************************************************************
450  *              CoInitializeEx  [OLE32.@]
451  *
452  * Initializes the COM libraries. The behavior used to set the win32
453  * IMalloc used for memory management is obsolete. If
454  * COINIT_APARTMENTTHREADED is specified this thread enters a new STA
455  * (single threaded apartment), otherwise COINIT_MULTITHREADED should
456  * be specified which indicates that the thread will enter the MTA.
457  *
458  * Currently STA threading is only partly implemented.
459  *
460  * RETURNS
461  *  S_OK               if successful,
462  *  S_FALSE            if this function was called already.
463  *  RPC_E_CHANGED_MODE if a previous call to CoInitializeEx specified another
464  *                     threading model.
465  *
466  * SEE ALSO
467  *   CoUninitialize
468  */
469 HRESULT WINAPI CoInitializeEx(
470         LPVOID lpReserved,      /* [in] pointer to win32 malloc interface
471                                    (obsolete, should be NULL) */
472         DWORD dwCoInit          /* [in] A value from COINIT specifies the threading model */
473 )
474 {
475   HRESULT hr = S_OK;
476   APARTMENT *apt;
477
478   TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
479
480   if (lpReserved!=NULL)
481   {
482     ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
483   }
484
485   apt = NtCurrentTeb()->ReservedForOle;
486   if (apt && !(apt->model == COINIT_UNINITIALIZED))
487   {
488     if (dwCoInit != apt->model)
489     {
490       /* Changing the threading model after it's been set is illegal. If this warning is triggered by Wine
491          code then we are probably using the wrong threading model to implement that API. */
492       ERR("Attempt to change threading model of this apartment from 0x%lx to 0x%lx\n", apt->model, dwCoInit);
493       return RPC_E_CHANGED_MODE;
494     }
495     hr = S_FALSE;
496   }
497   else
498     hr = S_OK;
499
500   /*
501    * Check the lock count. If this is the first time going through the initialize
502    * process, we have to initialize the libraries.
503    *
504    * And crank-up that lock count.
505    */
506   if (InterlockedExchangeAdd(&s_COMLockCount,1)==0)
507   {
508     /*
509      * Initialize the various COM libraries and data structures.
510      */
511     TRACE("() - Initializing the COM libraries\n");
512
513     COM_InitMTA();
514
515     RunningObjectTableImpl_Initialize();
516   }
517
518   if (!apt || apt->model == COINIT_UNINITIALIZED) apt = COM_CreateApartment(dwCoInit);
519
520   InterlockedIncrement(&apt->inits);
521   if (hr == S_OK) NtCurrentTeb()->ReservedForOle = apt;
522
523   return hr;
524 }
525
526 /* On COM finalization for a STA thread, the message queue is flushed to ensure no
527    pending RPCs are ignored. Non-COM messages are discarded at this point.
528  */
529 void COM_FlushMessageQueue(void)
530 {
531     MSG message;
532     APARTMENT *apt = NtCurrentTeb()->ReservedForOle;
533
534     if (!apt || !apt->win) return;
535
536     TRACE("Flushing STA message queue\n");
537
538     while (PeekMessageA(&message, NULL, 0, 0, PM_REMOVE)) {
539         if (message.hwnd != apt->win) continue;
540         TranslateMessage(&message);
541         DispatchMessageA(&message);
542     }
543 }
544
545 /***********************************************************************
546  *           CoUninitialize   [OLE32.@]
547  *
548  * This method will decrement the refcount on the COM libraries,
549  * potentially unloading them. The current thread leaves the apartment
550  * it's currently in. If not in an apartment, the routine does
551  * nothing.
552  *
553  * If COM is to be shut down, any outstanding proxies are
554  * disconnected, all registered class objects are unregistered and the
555  * message queue for the thread is flushed (if native does
556  * this or not is unknown).
557  *
558  * SEE ALSO
559  *   CoInitializeEx
560  */
561 void WINAPI CoUninitialize(void)
562 {
563   LONG lCOMRefCnt;
564   APARTMENT *apt;
565
566   TRACE("()\n");
567
568   apt = NtCurrentTeb()->ReservedForOle;
569   if (!apt) return;
570   if (InterlockedDecrement(&apt->inits)==0) {
571     NtCurrentTeb()->ReservedForOle = NULL;
572     COM_DestroyApartment(apt);
573     apt = NULL;
574   }
575
576   /*
577    * Decrease the reference count.
578    * If we are back to 0 locks on the COM library, make sure we free
579    * all the associated data structures.
580    */
581   lCOMRefCnt = InterlockedExchangeAdd(&s_COMLockCount,-1);
582   if (lCOMRefCnt==1)
583   {
584     TRACE("() - Releasing the COM libraries\n");
585
586     RunningObjectTableImpl_UnInitialize();
587
588     /* Release the references to the registered class objects */
589     COM_RevokeAllClasses();
590
591     /* This will free the loaded COM Dlls  */
592     CoFreeAllLibraries();
593
594     /* This will free list of external references to COM objects */
595     COM_ExternalLockFreeList();
596
597     /* This ensures we deal with any pending RPCs */
598     COM_FlushMessageQueue();
599
600     COM_UninitMTA();
601   }
602   else if (lCOMRefCnt<1) {
603     ERR( "CoUninitialize() - not CoInitialized.\n" );
604     InterlockedExchangeAdd(&s_COMLockCount,1); /* restore the lock count. */
605   }
606 }
607
608 /******************************************************************************
609  *              CoDisconnectObject      [COMPOBJ.15]
610  *              CoDisconnectObject      [OLE32.@]
611  *
612  * Disconnects all connections to this object from remote processes. Dispatches
613  * pending RPCs while blocking new RPCs from occurring, and then calls
614  * IMarshal::DisconnectObject on the given object.
615  *
616  * Typically called when the object server is forced to shut down, for instance by
617  * the user.
618  */
619 HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
620 {
621     FIXME("(%p, %lx): stub - probably harmless\n",lpUnk,reserved);
622     return S_OK;
623 }
624
625 /******************************************************************************
626  *              CoCreateGuid[OLE32.@]
627  *
628  * Simply forwards to UuidCreate in RPCRT4.
629  *
630  * SEE ALSO
631  *   UuidCreate
632  *
633  */
634 HRESULT WINAPI CoCreateGuid(
635         GUID *pguid /* [out] points to the GUID to initialize */
636 ) {
637     return UuidCreate(pguid);
638 }
639
640 /******************************************************************************
641  *              CLSIDFromString [OLE32.@]
642  *              IIDFromString   [OLE32.@]
643  *
644  * Converts a unique identifier from its string representation into
645  * the GUID struct.
646  *
647  * In Windows, if idstr is not a valid CLSID string then it gets
648  * treated as a ProgID. Wine currently doesn't do this. If idstr is
649  * NULL it's treated as an all-zero GUID.
650  *
651  * RETURNS
652  *   S_OK on success
653  *   CO_E_CLASSSTRING if idstr is not a valid CLSID
654  */
655 HRESULT WINAPI __CLSIDFromStringA(
656         LPCSTR idstr,           /* [in] string representation of guid */
657         CLSID *id)              /* [out] GUID converted from string */
658 {
659   const BYTE *s = (const BYTE *) idstr;
660   int   i;
661   BYTE table[256];
662
663   if (!s)
664           s = "{00000000-0000-0000-0000-000000000000}";
665   else {  /* validate the CLSID string */
666
667       if (strlen(s) != 38)
668           return CO_E_CLASSSTRING;
669
670       if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') || (s[24]!='-') || (s[37]!='}'))
671           return CO_E_CLASSSTRING;
672
673       for (i=1; i<37; i++) {
674           if ((i == 9)||(i == 14)||(i == 19)||(i == 24)) continue;
675           if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
676                 ((s[i] >= 'a') && (s[i] <= 'f'))  ||
677                 ((s[i] >= 'A') && (s[i] <= 'F'))))
678               return CO_E_CLASSSTRING;
679       }
680   }
681
682   TRACE("%s -> %p\n", s, id);
683
684   /* quick lookup table */
685   memset(table, 0, 256);
686
687   for (i = 0; i < 10; i++) {
688     table['0' + i] = i;
689   }
690   for (i = 0; i < 6; i++) {
691     table['A' + i] = i+10;
692     table['a' + i] = i+10;
693   }
694
695   /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
696
697   id->Data1 = (table[s[1]] << 28 | table[s[2]] << 24 | table[s[3]] << 20 | table[s[4]] << 16 |
698                table[s[5]] << 12 | table[s[6]] << 8  | table[s[7]] << 4  | table[s[8]]);
699   id->Data2 = table[s[10]] << 12 | table[s[11]] << 8 | table[s[12]] << 4 | table[s[13]];
700   id->Data3 = table[s[15]] << 12 | table[s[16]] << 8 | table[s[17]] << 4 | table[s[18]];
701
702   /* these are just sequential bytes */
703   id->Data4[0] = table[s[20]] << 4 | table[s[21]];
704   id->Data4[1] = table[s[22]] << 4 | table[s[23]];
705   id->Data4[2] = table[s[25]] << 4 | table[s[26]];
706   id->Data4[3] = table[s[27]] << 4 | table[s[28]];
707   id->Data4[4] = table[s[29]] << 4 | table[s[30]];
708   id->Data4[5] = table[s[31]] << 4 | table[s[32]];
709   id->Data4[6] = table[s[33]] << 4 | table[s[34]];
710   id->Data4[7] = table[s[35]] << 4 | table[s[36]];
711
712   return S_OK;
713 }
714
715 /*****************************************************************************/
716
717 HRESULT WINAPI CLSIDFromString(
718         LPOLESTR idstr,         /* [in] string representation of GUID */
719         CLSID *id )             /* [out] GUID represented by above string */
720 {
721     char xid[40];
722     HRESULT ret;
723
724     if (!WideCharToMultiByte( CP_ACP, 0, idstr, -1, xid, sizeof(xid), NULL, NULL ))
725         return CO_E_CLASSSTRING;
726
727
728     ret = __CLSIDFromStringA(xid,id);
729     if(ret != S_OK) { /* It appears a ProgID is also valid */
730         ret = CLSIDFromProgID(idstr, id);
731     }
732     return ret;
733 }
734
735 /* Converts a GUID into the respective string representation. */
736 HRESULT WINE_StringFromCLSID(
737         const CLSID *id,        /* [in] GUID to be converted */
738         LPSTR idstr             /* [out] pointer to buffer to contain converted guid */
739 ) {
740   static const char *hex = "0123456789ABCDEF";
741   char *s;
742   int   i;
743
744   if (!id)
745         { ERR("called with id=Null\n");
746           *idstr = 0x00;
747           return E_FAIL;
748         }
749
750   sprintf(idstr, "{%08lX-%04X-%04X-%02X%02X-",
751           id->Data1, id->Data2, id->Data3,
752           id->Data4[0], id->Data4[1]);
753   s = &idstr[25];
754
755   /* 6 hex bytes */
756   for (i = 2; i < 8; i++) {
757     *s++ = hex[id->Data4[i]>>4];
758     *s++ = hex[id->Data4[i] & 0xf];
759   }
760
761   *s++ = '}';
762   *s++ = '\0';
763
764   TRACE("%p->%s\n", id, idstr);
765
766   return S_OK;
767 }
768
769
770 /******************************************************************************
771  *              StringFromCLSID [OLE32.@]
772  *              StringFromIID   [OLE32.@]
773  *
774  * Converts a GUID into the respective string representation.
775  * The target string is allocated using the OLE IMalloc.
776  *
777  * RETURNS
778  *   S_OK
779  *   E_FAIL
780  */
781 HRESULT WINAPI StringFromCLSID(
782         REFCLSID id,    /* [in] the GUID to be converted */
783         LPOLESTR *idstr /* [out] a pointer to a to-be-allocated pointer pointing to the resulting string */
784 ) {
785         char            buf[80];
786         HRESULT       ret;
787         LPMALLOC        mllc;
788
789         if ((ret = CoGetMalloc(0,&mllc)))
790                 return ret;
791
792         ret=WINE_StringFromCLSID(id,buf);
793         if (!ret) {
794             DWORD len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 );
795             *idstr = IMalloc_Alloc( mllc, len * sizeof(WCHAR) );
796             MultiByteToWideChar( CP_ACP, 0, buf, -1, *idstr, len );
797         }
798         return ret;
799 }
800
801 /******************************************************************************
802  *              StringFromGUID2 [COMPOBJ.76]
803  *              StringFromGUID2 [OLE32.@]
804  *
805  * Modified version of StringFromCLSID that allows you to specify max
806  * buffer size.
807  *
808  * RETURNS
809  *      The length of the resulting string, 0 if there was any problem.
810  */
811 INT WINAPI StringFromGUID2(
812     REFGUID id,    /* [in]  GUID to convert to string */
813     LPOLESTR str,  /* [out] Unicode buffer to hold result */
814     INT cmax)
815 {
816   char          xguid[80];
817
818   if (WINE_StringFromCLSID(id,xguid))
819         return 0;
820   return MultiByteToWideChar( CP_ACP, 0, xguid, -1, str, cmax );
821 }
822
823 /******************************************************************************
824  *               ProgIDFromCLSID [OLE32.@]
825  *
826  * Converts a class id into the respective Program ID. (By using a
827  * registry lookup)
828  *
829  * RETURNS
830  *   S_OK
831  *   E_OUTOFMEMORY
832  *   REGDB_E_CLASSNOTREG if the given clsid has no associated ProgID
833  */
834 HRESULT WINAPI ProgIDFromCLSID(
835   REFCLSID clsid, /* [in] class id as found in registry */
836   LPOLESTR *lplpszProgID/* [out] associated Prog ID */
837 )
838 {
839   char     strCLSID[50], *buf, *buf2;
840   DWORD    buf2len;
841   HKEY     xhkey;
842   LPMALLOC mllc;
843   HRESULT  ret = S_OK;
844
845   WINE_StringFromCLSID(clsid, strCLSID);
846
847   buf = HeapAlloc(GetProcessHeap(), 0, strlen(strCLSID)+14);
848   sprintf(buf,"CLSID\\%s\\ProgID", strCLSID);
849   if (RegOpenKeyA(HKEY_CLASSES_ROOT, buf, &xhkey))
850     ret = REGDB_E_CLASSNOTREG;
851
852   HeapFree(GetProcessHeap(), 0, buf);
853
854   if (ret == S_OK)
855   {
856     buf2 = HeapAlloc(GetProcessHeap(), 0, 255);
857     buf2len = 255;
858     if (RegQueryValueA(xhkey, NULL, buf2, &buf2len))
859       ret = REGDB_E_CLASSNOTREG;
860
861     if (ret == S_OK)
862     {
863       if (CoGetMalloc(0,&mllc))
864         ret = E_OUTOFMEMORY;
865       else
866       {
867           DWORD len = MultiByteToWideChar( CP_ACP, 0, buf2, -1, NULL, 0 );
868           *lplpszProgID = IMalloc_Alloc(mllc, len * sizeof(WCHAR) );
869           MultiByteToWideChar( CP_ACP, 0, buf2, -1, *lplpszProgID, len );
870       }
871     }
872     HeapFree(GetProcessHeap(), 0, buf2);
873   }
874
875   RegCloseKey(xhkey);
876   return ret;
877 }
878
879 HRESULT WINAPI CLSIDFromProgID16(
880         LPCOLESTR16 progid,     /* [in] program id as found in registry */
881         LPCLSID riid            /* [out] associated CLSID */
882 ) {
883         char    *buf,buf2[80];
884         DWORD   buf2len;
885         HRESULT err;
886         HKEY    xhkey;
887
888         buf = HeapAlloc(GetProcessHeap(),0,strlen(progid)+8);
889         sprintf(buf,"%s\\CLSID",progid);
890         if ((err=RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&xhkey))) {
891                 HeapFree(GetProcessHeap(),0,buf);
892                 return CO_E_CLASSSTRING;
893         }
894         HeapFree(GetProcessHeap(),0,buf);
895         buf2len = sizeof(buf2);
896         if ((err=RegQueryValueA(xhkey,NULL,buf2,&buf2len))) {
897                 RegCloseKey(xhkey);
898                 return CO_E_CLASSSTRING;
899         }
900         RegCloseKey(xhkey);
901         return __CLSIDFromStringA(buf2,riid);
902 }
903
904 /******************************************************************************
905  *              CLSIDFromProgID [COMPOBJ.61]
906  *
907  * Converts a program id into the respective GUID. (By using a
908  * registry lookup)
909  *
910  * RETURNS
911  *      S_OK
912  *      CO_E_CLASSSTRING if the given ProgID cannot be found
913  */
914 HRESULT WINAPI CLSIDFromProgID(
915         LPCOLESTR progid,       /* [in] Unicode program id as found in registry */
916         LPCLSID riid )          /* [out] associated CLSID */
917 {
918     static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 };
919     char buf2[80];
920     DWORD buf2len = sizeof(buf2);
921     HKEY xhkey;
922
923     WCHAR *buf = HeapAlloc( GetProcessHeap(),0,(strlenW(progid)+8) * sizeof(WCHAR) );
924     strcpyW( buf, progid );
925     strcatW( buf, clsidW );
926     if (RegOpenKeyW(HKEY_CLASSES_ROOT,buf,&xhkey))
927     {
928         HeapFree(GetProcessHeap(),0,buf);
929         return CO_E_CLASSSTRING;
930     }
931     HeapFree(GetProcessHeap(),0,buf);
932
933     if (RegQueryValueA(xhkey,NULL,buf2,&buf2len))
934     {
935         RegCloseKey(xhkey);
936         return CO_E_CLASSSTRING;
937     }
938     RegCloseKey(xhkey);
939     return __CLSIDFromStringA(buf2,riid);
940 }
941
942
943
944 /*****************************************************************************
945  *             CoGetPSClsid [OLE32.@]
946  *
947  * This function returns the CLSID of the proxy/stub factory that
948  * implements IPSFactoryBuffer for the specified interface.
949  *
950  * The standard marshaller activates the object with the CLSID
951  * returned and uses the CreateProxy and CreateStub methods on its
952  * IPSFactoryBuffer interface to construct the proxies and stubs for a
953  * given object.
954  *
955  * CoGetPSClsid determines this CLSID by searching the
956  * HKEY_CLASSES_ROOT\Interface\{string form of riid}\ProxyStubClsid32
957  * in the registry and any interface id registered by
958  * CoRegisterPSClsid within the current process.
959  *
960  * FIXME: We only search the registry, not ids registered with
961  * CoRegisterPSClsid.
962  *
963  * RETURNS
964  *   S_OK
965  *   E_OUTOFMEMORY
966  *   E_INVALIDARG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
967  */
968 HRESULT WINAPI CoGetPSClsid(
969           REFIID riid,     /* [in]  Interface whose proxy/stub CLSID is to be returned */
970           CLSID *pclsid )  /* [out] Where to store returned proxy/stub CLSID */
971 {
972     char *buf, buf2[40];
973     DWORD buf2len;
974     HKEY xhkey;
975
976     TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
977
978     /* Get the input iid as a string */
979     WINE_StringFromCLSID(riid, buf2);
980     /* Allocate memory for the registry key we will construct.
981        (length of iid string plus constant length of static text */
982     buf = HeapAlloc(GetProcessHeap(), 0, strlen(buf2)+27);
983     if (buf == NULL)
984     {
985        return (E_OUTOFMEMORY);
986     }
987
988     /* Construct the registry key we want */
989     sprintf(buf,"Interface\\%s\\ProxyStubClsid32", buf2);
990
991     /* Open the key.. */
992     if (RegOpenKeyA(HKEY_CLASSES_ROOT, buf, &xhkey))
993     {
994        WARN("No PSFactoryBuffer object is registered for this IID\n");
995        HeapFree(GetProcessHeap(),0,buf);
996        return (E_INVALIDARG);
997     }
998     HeapFree(GetProcessHeap(),0,buf);
999
1000     /* ... Once we have the key, query the registry to get the
1001        value of CLSID as a string, and convert it into a
1002        proper CLSID structure to be passed back to the app */
1003     buf2len = sizeof(buf2);
1004     if ( (RegQueryValueA(xhkey,NULL,buf2,&buf2len)) )
1005     {
1006        RegCloseKey(xhkey);
1007        return E_INVALIDARG;
1008     }
1009     RegCloseKey(xhkey);
1010
1011     /* We have the CLSid we want back from the registry as a string, so
1012        lets convert it into a CLSID structure */
1013     if ( (__CLSIDFromStringA(buf2,pclsid)) != NOERROR) {
1014        return E_INVALIDARG;
1015     }
1016
1017     TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
1018     return (S_OK);
1019 }
1020
1021
1022
1023 /***********************************************************************
1024  *              WriteClassStm (OLE32.@)
1025  *
1026  * This function write a CLSID on stream
1027  */
1028 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
1029 {
1030     TRACE("(%p,%p)\n",pStm,rclsid);
1031
1032     if (rclsid==NULL)
1033         return E_INVALIDARG;
1034
1035     return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
1036 }
1037
1038 /***********************************************************************
1039  *              ReadClassStm (OLE32.@)
1040  *
1041  * This function read a CLSID from a stream
1042  */
1043 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
1044 {
1045     ULONG nbByte;
1046     HRESULT res;
1047
1048     TRACE("(%p,%p)\n",pStm,pclsid);
1049
1050     if (pclsid==NULL)
1051         return E_INVALIDARG;
1052
1053     res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
1054
1055     if (FAILED(res))
1056         return res;
1057
1058     if (nbByte != sizeof(CLSID))
1059         return S_FALSE;
1060     else
1061         return S_OK;
1062 }
1063
1064
1065 /***
1066  * COM_GetRegisteredClassObject
1067  *
1068  * This internal method is used to scan the registered class list to
1069  * find a class object.
1070  *
1071  * Params:
1072  *   rclsid        Class ID of the class to find.
1073  *   dwClsContext  Class context to match.
1074  *   ppv           [out] returns a pointer to the class object. Complying
1075  *                 to normal COM usage, this method will increase the
1076  *                 reference count on this object.
1077  */
1078 static HRESULT COM_GetRegisteredClassObject(
1079         REFCLSID    rclsid,
1080         DWORD       dwClsContext,
1081         LPUNKNOWN*  ppUnk)
1082 {
1083   HRESULT hr = S_FALSE;
1084   RegisteredClass* curClass;
1085
1086   EnterCriticalSection( &csRegisteredClassList );
1087
1088   /*
1089    * Sanity check
1090    */
1091   assert(ppUnk!=0);
1092
1093   /*
1094    * Iterate through the whole list and try to match the class ID.
1095    */
1096   curClass = firstRegisteredClass;
1097
1098   while (curClass != 0)
1099   {
1100     /*
1101      * Check if we have a match on the class ID.
1102      */
1103     if (IsEqualGUID(&(curClass->classIdentifier), rclsid))
1104     {
1105       /*
1106        * Since we don't do out-of process or DCOM just right away, let's ignore the
1107        * class context.
1108        */
1109
1110       /*
1111        * We have a match, return the pointer to the class object.
1112        */
1113       *ppUnk = curClass->classObject;
1114
1115       IUnknown_AddRef(curClass->classObject);
1116
1117       hr = S_OK;
1118       goto end;
1119     }
1120
1121     /*
1122      * Step to the next class in the list.
1123      */
1124     curClass = curClass->nextClass;
1125   }
1126
1127 end:
1128   LeaveCriticalSection( &csRegisteredClassList );
1129   /*
1130    * If we get to here, we haven't found our class.
1131    */
1132   return hr;
1133 }
1134
1135 /******************************************************************************
1136  *              CoRegisterClassObject   [OLE32.@]
1137  * 
1138  * Registers the class object for a given class ID. Servers housed in EXE
1139  * files use this method instead of exporting DllGetClassObject to allow
1140  * other code to connect to their objects.
1141  *
1142  * RETURNS
1143  *   S_OK on success, 
1144  *   E_INVALIDARG if lpdwRegister or pUnk are NULL, 
1145  *   CO_E_OBJISREG if the object is already registered. We should not return this.
1146  *
1147  * SEE ALSO
1148  *   CoRevokeClassObject, CoGetClassObject
1149  *
1150  * BUGS
1151  *  MSDN claims that multiple interface registrations are legal, but we
1152  *  can't do that with our current implementation.
1153  */
1154 HRESULT WINAPI CoRegisterClassObject(
1155         REFCLSID rclsid,       /* [in] CLSID of the object to register */
1156         LPUNKNOWN pUnk,        /* [in] IUnknown of the object */
1157         DWORD dwClsContext,    /* [in] CLSCTX flags indicating the context in which to run the executable */
1158         DWORD flags,           /* [in] REGCLS flags indicating how connections are made */
1159         LPDWORD lpdwRegister)  /* [out] A unique cookie that can be passed to CoRevokeClassObject */
1160 {
1161   RegisteredClass* newClass;
1162   LPUNKNOWN        foundObject;
1163   HRESULT          hr;
1164
1165   TRACE("(%s,%p,0x%08lx,0x%08lx,%p)\n",
1166         debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
1167
1168   if ( (lpdwRegister==0) || (pUnk==0) )
1169     return E_INVALIDARG;
1170
1171   if (!COM_CurrentApt())
1172   {
1173       ERR("COM was not initialized\n");
1174       return CO_E_NOTINITIALIZED;
1175   }
1176
1177   *lpdwRegister = 0;
1178
1179   /*
1180    * First, check if the class is already registered.
1181    * If it is, this should cause an error.
1182    */
1183   hr = COM_GetRegisteredClassObject(rclsid, dwClsContext, &foundObject);
1184   if (hr == S_OK) {
1185     IUnknown_Release(foundObject);
1186     return CO_E_OBJISREG;
1187   }
1188
1189   newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
1190   if ( newClass == NULL )
1191     return E_OUTOFMEMORY;
1192
1193   EnterCriticalSection( &csRegisteredClassList );
1194
1195   newClass->classIdentifier = *rclsid;
1196   newClass->runContext      = dwClsContext;
1197   newClass->connectFlags    = flags;
1198   /*
1199    * Use the address of the chain node as the cookie since we are sure it's
1200    * unique. FIXME: not on 64-bit platforms.
1201    */
1202   newClass->dwCookie        = (DWORD)newClass;
1203   newClass->nextClass       = firstRegisteredClass;
1204
1205   /*
1206    * Since we're making a copy of the object pointer, we have to increase its
1207    * reference count.
1208    */
1209   newClass->classObject     = pUnk;
1210   IUnknown_AddRef(newClass->classObject);
1211
1212   firstRegisteredClass = newClass;
1213   LeaveCriticalSection( &csRegisteredClassList );
1214
1215   *lpdwRegister = newClass->dwCookie;
1216
1217   if (dwClsContext & CLSCTX_LOCAL_SERVER) {
1218       IClassFactory *classfac;
1219
1220       hr = IUnknown_QueryInterface(newClass->classObject, &IID_IClassFactory, 
1221                                    (LPVOID*)&classfac);
1222       if (hr) return hr;
1223
1224       hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
1225       if (hr) {
1226           FIXME("Failed to create stream on hglobal, %lx\n", hr);
1227           IUnknown_Release(classfac);
1228           return hr;
1229       }
1230       hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
1231                               (LPVOID)classfac, MSHCTX_LOCAL, NULL,
1232                               MSHLFLAGS_TABLESTRONG);
1233       if (hr) {
1234           FIXME("CoMarshalInterface failed, %lx!\n",hr);
1235           IUnknown_Release(classfac);
1236           return hr;
1237       }
1238
1239       IUnknown_Release(classfac);
1240
1241       RPC_StartLocalServer(&newClass->classIdentifier, newClass->pMarshaledData);
1242   }
1243   return S_OK;
1244 }
1245
1246 /***********************************************************************
1247  *           CoRevokeClassObject [OLE32.@]
1248  *
1249  * This method will remove a class object from the class registry
1250  *
1251  * See the Windows documentation for more details.
1252  */
1253 HRESULT WINAPI CoRevokeClassObject(
1254         DWORD dwRegister)
1255 {
1256   HRESULT hr = E_INVALIDARG;
1257   RegisteredClass** prevClassLink;
1258   RegisteredClass*  curClass;
1259
1260   TRACE("(%08lx)\n",dwRegister);
1261
1262   EnterCriticalSection( &csRegisteredClassList );
1263
1264   /*
1265    * Iterate through the whole list and try to match the cookie.
1266    */
1267   curClass      = firstRegisteredClass;
1268   prevClassLink = &firstRegisteredClass;
1269
1270   while (curClass != 0)
1271   {
1272     /*
1273      * Check if we have a match on the cookie.
1274      */
1275     if (curClass->dwCookie == dwRegister)
1276     {
1277       /*
1278        * Remove the class from the chain.
1279        */
1280       *prevClassLink = curClass->nextClass;
1281
1282       /*
1283        * Release the reference to the class object.
1284        */
1285       IUnknown_Release(curClass->classObject);
1286
1287       if (curClass->pMarshaledData)
1288       {
1289         LARGE_INTEGER zero;
1290         memset(&zero, 0, sizeof(zero));
1291         /* FIXME: stop local server thread */
1292         IStream_Seek(curClass->pMarshaledData, zero, SEEK_SET, NULL);
1293         CoReleaseMarshalData(curClass->pMarshaledData);
1294       }
1295
1296       /*
1297        * Free the memory used by the chain node.
1298        */
1299       HeapFree(GetProcessHeap(), 0, curClass);
1300
1301       hr = S_OK;
1302       goto end;
1303     }
1304
1305     /*
1306      * Step to the next class in the list.
1307      */
1308     prevClassLink = &(curClass->nextClass);
1309     curClass      = curClass->nextClass;
1310   }
1311
1312 end:
1313   LeaveCriticalSection( &csRegisteredClassList );
1314   /*
1315    * If we get to here, we haven't found our class.
1316    */
1317   return hr;
1318 }
1319
1320 /***********************************************************************
1321  *      compobj_RegReadPath     [internal]
1322  *
1323  *      Reads a registry value and expands it when necessary
1324  */
1325 HRESULT compobj_RegReadPath(char * keyname, char * valuename, char * dst, DWORD dstlen)
1326 {
1327         HRESULT hres;
1328         HKEY key;
1329         DWORD keytype;
1330         char src[MAX_PATH];
1331         DWORD dwLength = dstlen;
1332
1333         if((hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
1334           if( (hres = RegQueryValueExA(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
1335             if (keytype == REG_EXPAND_SZ) {
1336               if (dstlen <= ExpandEnvironmentStringsA(src, dst, dstlen)) hres = ERROR_MORE_DATA;
1337             } else {
1338               lstrcpynA(dst, src, dstlen);
1339             }
1340           }
1341           RegCloseKey (key);
1342         }
1343         return hres;
1344 }
1345
1346 /***********************************************************************
1347  *           CoGetClassObject [COMPOBJ.7]
1348  *           CoGetClassObject [OLE32.@]
1349  *
1350  * FIXME.  If request allows of several options and there is a failure
1351  *         with one (other than not being registered) do we try the
1352  *         others or return failure?  (E.g. inprocess is registered but
1353  *         the DLL is not found but the server version works)
1354  */
1355 HRESULT WINAPI CoGetClassObject(
1356     REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
1357     REFIID iid, LPVOID *ppv
1358 ) {
1359     LPUNKNOWN   regClassObject;
1360     HRESULT     hres = E_UNEXPECTED;
1361     char        xclsid[80];
1362     HINSTANCE hLibrary;
1363     typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
1364     DllGetClassObjectFunc DllGetClassObject;
1365
1366     WINE_StringFromCLSID((LPCLSID)rclsid,xclsid);
1367
1368     TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
1369
1370     if (pServerInfo) {
1371         FIXME("\tpServerInfo: name=%s\n",debugstr_w(pServerInfo->pwszName));
1372         FIXME("\t\tpAuthInfo=%p\n",pServerInfo->pAuthInfo);
1373     }
1374
1375     /*
1376      * First, try and see if we can't match the class ID with one of the
1377      * registered classes.
1378      */
1379     if (S_OK == COM_GetRegisteredClassObject(rclsid, dwClsContext, &regClassObject))
1380     {
1381       /*
1382        * Get the required interface from the retrieved pointer.
1383        */
1384       hres = IUnknown_QueryInterface(regClassObject, iid, ppv);
1385
1386       /*
1387        * Since QI got another reference on the pointer, we want to release the
1388        * one we already have. If QI was unsuccessful, this will release the object. This
1389        * is good since we are not returning it in the "out" parameter.
1390        */
1391       IUnknown_Release(regClassObject);
1392
1393       return hres;
1394     }
1395
1396     /* first try: in-process */
1397     if ((CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER) & dwClsContext) {
1398         char keyname[MAX_PATH];
1399         char dllpath[MAX_PATH+1];
1400
1401         sprintf(keyname,"CLSID\\%s\\InprocServer32",xclsid);
1402
1403         if ( compobj_RegReadPath(keyname, NULL, dllpath, sizeof(dllpath)) != ERROR_SUCCESS) {
1404             /* failure: CLSID is not found in registry */
1405            WARN("class %s not registered inproc\n", xclsid);
1406             hres = REGDB_E_CLASSNOTREG;
1407         } else {
1408           if ((hLibrary = LoadLibraryExA(dllpath, 0, LOAD_WITH_ALTERED_SEARCH_PATH)) == 0) {
1409             /* failure: DLL could not be loaded */
1410             ERR("couldn't load InprocServer32 dll %s\n", dllpath);
1411             hres = E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
1412           } else if (!(DllGetClassObject = (DllGetClassObjectFunc)GetProcAddress(hLibrary, "DllGetClassObject"))) {
1413             /* failure: the dll did not export DllGetClassObject */
1414             ERR("couldn't find function DllGetClassObject in %s\n", dllpath);
1415             FreeLibrary( hLibrary );
1416             hres = CO_E_DLLNOTFOUND;
1417           } else {
1418             /* OK: get the ClassObject */
1419             COMPOBJ_DLLList_Add( hLibrary );
1420             return DllGetClassObject(rclsid, iid, ppv);
1421           }
1422         }
1423     }
1424
1425     /* Next try out of process */
1426     if (CLSCTX_LOCAL_SERVER & dwClsContext)
1427     {
1428         return create_marshalled_proxy(rclsid,iid,ppv);
1429     }
1430
1431     /* Finally try remote: this requires networked DCOM (a lot of work) */
1432     if (CLSCTX_REMOTE_SERVER & dwClsContext)
1433     {
1434         FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
1435         hres = E_NOINTERFACE;
1436     }
1437
1438     return hres;
1439 }
1440 /***********************************************************************
1441  *        CoResumeClassObjects (OLE32.@)
1442  *
1443  * Resumes classobjects registered with REGCLS suspended
1444  */
1445 HRESULT WINAPI CoResumeClassObjects(void)
1446 {
1447        FIXME("stub\n");
1448         return S_OK;
1449 }
1450
1451 /***********************************************************************
1452  *        GetClassFile (OLE32.@)
1453  *
1454  * This function supplies the CLSID associated with the given filename.
1455  */
1456 HRESULT WINAPI GetClassFile(LPCOLESTR filePathName,CLSID *pclsid)
1457 {
1458     IStorage *pstg=0;
1459     HRESULT res;
1460     int nbElm, length, i;
1461     LONG sizeProgId;
1462     LPOLESTR *pathDec=0,absFile=0,progId=0;
1463     LPWSTR extension;
1464     static const WCHAR bkslashW[] = {'\\',0};
1465     static const WCHAR dotW[] = {'.',0};
1466
1467     TRACE("%s, %p\n", debugstr_w(filePathName), pclsid);
1468
1469     /* if the file contain a storage object the return the CLSID written by IStorage_SetClass method*/
1470     if((StgIsStorageFile(filePathName))==S_OK){
1471
1472         res=StgOpenStorage(filePathName,NULL,STGM_READ | STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
1473
1474         if (SUCCEEDED(res))
1475             res=ReadClassStg(pstg,pclsid);
1476
1477         IStorage_Release(pstg);
1478
1479         return res;
1480     }
1481     /* if the file is not a storage object then attemps to match various bits in the file against a
1482        pattern in the registry. this case is not frequently used ! so I present only the psodocode for
1483        this case
1484
1485      for(i=0;i<nFileTypes;i++)
1486
1487         for(i=0;j<nPatternsForType;j++){
1488
1489             PATTERN pat;
1490             HANDLE  hFile;
1491
1492             pat=ReadPatternFromRegistry(i,j);
1493             hFile=CreateFileW(filePathName,,,,,,hFile);
1494             SetFilePosition(hFile,pat.offset);
1495             ReadFile(hFile,buf,pat.size,&r,NULL);
1496             if (memcmp(buf&pat.mask,pat.pattern.pat.size)==0){
1497
1498                 *pclsid=ReadCLSIDFromRegistry(i);
1499                 return S_OK;
1500             }
1501         }
1502      */
1503
1504     /* if the above strategies fail then search for the extension key in the registry */
1505
1506     /* get the last element (absolute file) in the path name */
1507     nbElm=FileMonikerImpl_DecomposePath(filePathName,&pathDec);
1508     absFile=pathDec[nbElm-1];
1509
1510     /* failed if the path represente a directory and not an absolute file name*/
1511     if (!lstrcmpW(absFile, bkslashW))
1512         return MK_E_INVALIDEXTENSION;
1513
1514     /* get the extension of the file */
1515     extension = NULL;
1516     length=lstrlenW(absFile);
1517     for(i = length-1; (i >= 0) && *(extension = &absFile[i]) != '.'; i--)
1518         /* nothing */;
1519
1520     if (!extension || !lstrcmpW(extension, dotW))
1521         return MK_E_INVALIDEXTENSION;
1522
1523     res=RegQueryValueW(HKEY_CLASSES_ROOT, extension, NULL, &sizeProgId);
1524
1525     /* get the progId associated to the extension */
1526     progId = CoTaskMemAlloc(sizeProgId);
1527     res = RegQueryValueW(HKEY_CLASSES_ROOT, extension, progId, &sizeProgId);
1528
1529     if (res==ERROR_SUCCESS)
1530         /* return the clsid associated to the progId */
1531         res= CLSIDFromProgID(progId,pclsid);
1532
1533     for(i=0; pathDec[i]!=NULL;i++)
1534         CoTaskMemFree(pathDec[i]);
1535     CoTaskMemFree(pathDec);
1536
1537     CoTaskMemFree(progId);
1538
1539     if (res==ERROR_SUCCESS)
1540         return res;
1541
1542     return MK_E_INVALIDEXTENSION;
1543 }
1544 /***********************************************************************
1545  *           CoCreateInstance [COMPOBJ.13]
1546  *           CoCreateInstance [OLE32.@]
1547  */
1548 HRESULT WINAPI CoCreateInstance(
1549         REFCLSID rclsid,
1550         LPUNKNOWN pUnkOuter,
1551         DWORD dwClsContext,
1552         REFIID iid,
1553         LPVOID *ppv)
1554 {
1555   HRESULT hres;
1556   LPCLASSFACTORY lpclf = 0;
1557
1558   if (!COM_CurrentApt()) return CO_E_NOTINITIALIZED;
1559         
1560   /*
1561    * Sanity check
1562    */
1563   if (ppv==0)
1564     return E_POINTER;
1565
1566   /*
1567    * Initialize the "out" parameter
1568    */
1569   *ppv = 0;
1570
1571   /*
1572    * The Standard Global Interface Table (GIT) object is a process-wide singleton.
1573    * Rather than create a class factory, we can just check for it here
1574    */
1575   if (IsEqualIID(rclsid, &CLSID_StdGlobalInterfaceTable)) {
1576     if (StdGlobalInterfaceTableInstance == NULL) 
1577       StdGlobalInterfaceTableInstance = StdGlobalInterfaceTable_Construct();
1578     hres = IGlobalInterfaceTable_QueryInterface( (IGlobalInterfaceTable*) StdGlobalInterfaceTableInstance, iid, ppv);
1579     if (hres) return hres;
1580     
1581     TRACE("Retrieved GIT (%p)\n", *ppv);
1582     return S_OK;
1583   }
1584   
1585   /*
1586    * Get a class factory to construct the object we want.
1587    */
1588   hres = CoGetClassObject(rclsid,
1589                           dwClsContext,
1590                           NULL,
1591                           &IID_IClassFactory,
1592                           (LPVOID)&lpclf);
1593
1594   if (FAILED(hres)) {
1595     FIXME("no classfactory created for CLSID %s, hres is 0x%08lx\n",
1596           debugstr_guid(rclsid),hres);
1597     return hres;
1598   }
1599
1600   /*
1601    * Create the object and don't forget to release the factory
1602    */
1603         hres = IClassFactory_CreateInstance(lpclf, pUnkOuter, iid, ppv);
1604         IClassFactory_Release(lpclf);
1605         if(FAILED(hres))
1606           FIXME("no instance created for interface %s of class %s, hres is 0x%08lx\n",
1607                 debugstr_guid(iid), debugstr_guid(rclsid),hres);
1608
1609         return hres;
1610 }
1611
1612 /***********************************************************************
1613  *           CoCreateInstanceEx [OLE32.@]
1614  */
1615 HRESULT WINAPI CoCreateInstanceEx(
1616   REFCLSID      rclsid,
1617   LPUNKNOWN     pUnkOuter,
1618   DWORD         dwClsContext,
1619   COSERVERINFO* pServerInfo,
1620   ULONG         cmq,
1621   MULTI_QI*     pResults)
1622 {
1623   IUnknown* pUnk = NULL;
1624   HRESULT   hr;
1625   ULONG     index;
1626   ULONG     successCount = 0;
1627
1628   /*
1629    * Sanity check
1630    */
1631   if ( (cmq==0) || (pResults==NULL))
1632     return E_INVALIDARG;
1633
1634   if (pServerInfo!=NULL)
1635     FIXME("() non-NULL pServerInfo not supported!\n");
1636
1637   /*
1638    * Initialize all the "out" parameters.
1639    */
1640   for (index = 0; index < cmq; index++)
1641   {
1642     pResults[index].pItf = NULL;
1643     pResults[index].hr   = E_NOINTERFACE;
1644   }
1645
1646   /*
1647    * Get the object and get its IUnknown pointer.
1648    */
1649   hr = CoCreateInstance(rclsid,
1650                         pUnkOuter,
1651                         dwClsContext,
1652                         &IID_IUnknown,
1653                         (VOID**)&pUnk);
1654
1655   if (hr)
1656     return hr;
1657
1658   /*
1659    * Then, query for all the interfaces requested.
1660    */
1661   for (index = 0; index < cmq; index++)
1662   {
1663     pResults[index].hr = IUnknown_QueryInterface(pUnk,
1664                                                  pResults[index].pIID,
1665                                                  (VOID**)&(pResults[index].pItf));
1666
1667     if (pResults[index].hr == S_OK)
1668       successCount++;
1669   }
1670
1671   /*
1672    * Release our temporary unknown pointer.
1673    */
1674   IUnknown_Release(pUnk);
1675
1676   if (successCount == 0)
1677     return E_NOINTERFACE;
1678
1679   if (successCount!=cmq)
1680     return CO_S_NOTALLINTERFACES;
1681
1682   return S_OK;
1683 }
1684
1685 /***********************************************************************
1686  *           CoLoadLibrary (OLE32.@)
1687  */
1688 HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
1689 {
1690     TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
1691
1692     return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
1693 }
1694
1695 /***********************************************************************
1696  *           CoFreeLibrary [OLE32.@]
1697  *
1698  * NOTES: don't believe the documentation
1699  */
1700 void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
1701 {
1702     FreeLibrary(hLibrary);
1703 }
1704
1705
1706 /***********************************************************************
1707  *           CoFreeAllLibraries [OLE32.@]
1708  *
1709  * NOTES: don't believe the documentation
1710  */
1711 void WINAPI CoFreeAllLibraries(void)
1712 {
1713     /* NOP */
1714 }
1715
1716
1717 /***********************************************************************
1718  *           CoFreeUnusedLibraries [COMPOBJ.17]
1719  *           CoFreeUnusedLibraries [OLE32.@]
1720  *
1721  * FIXME: Calls to CoFreeUnusedLibraries from any thread always route
1722  * through the main apartment's thread to call DllCanUnloadNow
1723  */
1724 void WINAPI CoFreeUnusedLibraries(void)
1725 {
1726     COMPOBJ_DllList_FreeUnused(0);
1727 }
1728
1729 /***********************************************************************
1730  *           CoFileTimeNow [COMPOBJ.82]
1731  *           CoFileTimeNow [OLE32.@]
1732  *
1733  * RETURNS
1734  *      the current system time in lpFileTime
1735  */
1736 HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime ) /* [out] the current time */
1737 {
1738     GetSystemTimeAsFileTime( lpFileTime );
1739     return S_OK;
1740 }
1741
1742 /***********************************************************************
1743  *           CoLoadLibrary (OLE32.@)
1744  */
1745 static void COM_RevokeAllClasses()
1746 {
1747   EnterCriticalSection( &csRegisteredClassList );
1748
1749   while (firstRegisteredClass!=0)
1750   {
1751     CoRevokeClassObject(firstRegisteredClass->dwCookie);
1752   }
1753
1754   LeaveCriticalSection( &csRegisteredClassList );
1755 }
1756
1757 /****************************************************************************
1758  *  COM External Lock methods implementation
1759  *
1760  *  This api provides a linked list to managed external references to
1761  *  COM objects.
1762  *
1763  *  The public interface consists of three calls:
1764  *      COM_ExternalLockAddRef
1765  *      COM_ExternalLockRelease
1766  *      COM_ExternalLockFreeList
1767  */
1768
1769 #define EL_END_OF_LIST 0
1770 #define EL_NOT_FOUND   0
1771
1772 /*
1773  * Declaration of the static structure that manage the
1774  * external lock to COM  objects.
1775  */
1776 typedef struct COM_ExternalLock     COM_ExternalLock;
1777 typedef struct COM_ExternalLockList COM_ExternalLockList;
1778
1779 struct COM_ExternalLock
1780 {
1781   IUnknown         *pUnk;     /* IUnknown referenced */
1782   ULONG            uRefCount; /* external lock counter to IUnknown object*/
1783   COM_ExternalLock *next;     /* Pointer to next element in list */
1784 };
1785
1786 struct COM_ExternalLockList
1787 {
1788   COM_ExternalLock *head;     /* head of list */
1789 };
1790
1791 /*
1792  * Declaration and initialization of the static structure that manages
1793  * the external lock to COM objects.
1794  */
1795 static COM_ExternalLockList elList = { EL_END_OF_LIST };
1796
1797 /*
1798  * Private methods used to managed the linked list
1799  */
1800
1801
1802 static COM_ExternalLock* COM_ExternalLockLocate(
1803   COM_ExternalLock *element,
1804   IUnknown         *pUnk);
1805
1806 /****************************************************************************
1807  * Internal - Insert a new IUnknown* to the linked list
1808  */
1809 static BOOL COM_ExternalLockInsert(
1810   IUnknown *pUnk)
1811 {
1812   COM_ExternalLock *newLock      = NULL;
1813   COM_ExternalLock *previousHead = NULL;
1814
1815   /*
1816    * Allocate space for the new storage object
1817    */
1818   newLock = HeapAlloc(GetProcessHeap(), 0, sizeof(COM_ExternalLock));
1819
1820   if (newLock!=NULL) {
1821     if ( elList.head == EL_END_OF_LIST ) {
1822       elList.head = newLock;    /* The list is empty */
1823     } else {
1824       /* insert does it at the head */
1825       previousHead  = elList.head;
1826       elList.head = newLock;
1827     }
1828
1829     /* Set new list item data member */
1830     newLock->pUnk      = pUnk;
1831     newLock->uRefCount = 1;
1832     newLock->next      = previousHead;
1833
1834     return TRUE;
1835   }
1836   return FALSE;
1837 }
1838
1839 /****************************************************************************
1840  * Internal - Method that removes an item from the linked list.
1841  */
1842 static void COM_ExternalLockDelete(
1843   COM_ExternalLock *itemList)
1844 {
1845   COM_ExternalLock *current = elList.head;
1846
1847   if ( current == itemList ) {
1848     /* this section handles the deletion of the first node */
1849     elList.head = itemList->next;
1850     HeapFree( GetProcessHeap(), 0, itemList);
1851   } else {
1852     do {
1853       if ( current->next == itemList ){   /* We found the item to free  */
1854         current->next = itemList->next;  /* readjust the list pointers */
1855         HeapFree( GetProcessHeap(), 0, itemList);
1856         break;
1857       }
1858
1859       /* Skip to the next item */
1860       current = current->next;
1861
1862     } while ( current != EL_END_OF_LIST );
1863   }
1864 }
1865
1866 /****************************************************************************
1867  * Internal - Recursivity agent for IUnknownExternalLockList_Find
1868  *
1869  * NOTES: how long can the list be ?? (recursive!!!)
1870  */
1871 static COM_ExternalLock* COM_ExternalLockLocate( COM_ExternalLock *element, IUnknown *pUnk)
1872 {
1873   if ( element == EL_END_OF_LIST )
1874     return EL_NOT_FOUND;
1875   else if ( element->pUnk == pUnk )    /* We found it */
1876     return element;
1877   else                                 /* Not the right guy, keep on looking */
1878     return COM_ExternalLockLocate( element->next, pUnk);
1879 }
1880
1881 /****************************************************************************
1882  * Public - Method that increments the count for a IUnknown* in the linked
1883  * list.  The item is inserted if not already in the list.
1884  */
1885 static void COM_ExternalLockAddRef(IUnknown *pUnk)
1886 {
1887   COM_ExternalLock *externalLock = COM_ExternalLockLocate(elList.head, pUnk);
1888
1889   /*
1890    * Add an external lock to the object. If it was already externally
1891    * locked, just increase the reference count. If it was not.
1892    * add the item to the list.
1893    */
1894   if ( externalLock == EL_NOT_FOUND )
1895     COM_ExternalLockInsert(pUnk);
1896   else
1897     externalLock->uRefCount++;
1898
1899   /*
1900    * Add an internal lock to the object
1901    */
1902   IUnknown_AddRef(pUnk);
1903 }
1904
1905 /****************************************************************************
1906  * Public - Method that decrements the count for a IUnknown* in the linked
1907  * list.  The item is removed from the list if its count end up at zero or if
1908  * bRelAll is TRUE.
1909  */
1910 static void COM_ExternalLockRelease(
1911   IUnknown *pUnk,
1912   BOOL   bRelAll)
1913 {
1914   COM_ExternalLock *externalLock = COM_ExternalLockLocate(elList.head, pUnk);
1915
1916   if ( externalLock != EL_NOT_FOUND ) {
1917     do {
1918       externalLock->uRefCount--;  /* release external locks      */
1919       IUnknown_Release(pUnk);     /* release local locks as well */
1920
1921       if ( bRelAll == FALSE ) break;  /* perform single release */
1922
1923     } while ( externalLock->uRefCount > 0 );
1924
1925     if ( externalLock->uRefCount == 0 )  /* get rid of the list entry */
1926       COM_ExternalLockDelete(externalLock);
1927   }
1928 }
1929 /****************************************************************************
1930  * Public - Method that frees the content of the list.
1931  */
1932 static void COM_ExternalLockFreeList()
1933 {
1934   COM_ExternalLock *head;
1935
1936   head = elList.head;                 /* grab it by the head             */
1937   while ( head != EL_END_OF_LIST ) {
1938     COM_ExternalLockDelete(head);     /* get rid of the head stuff       */
1939     head = elList.head;               /* get the new head...             */
1940   }
1941 }
1942
1943 /****************************************************************************
1944  * Public - Method that dump the content of the list.
1945  */
1946 void COM_ExternalLockDump()
1947 {
1948   COM_ExternalLock *current = elList.head;
1949
1950   DPRINTF("\nExternal lock list contains:\n");
1951
1952   while ( current != EL_END_OF_LIST ) {
1953     DPRINTF( "\t%p with %lu references count.\n", current->pUnk, current->uRefCount);
1954
1955     /* Skip to the next item */
1956     current = current->next;
1957   }
1958 }
1959
1960 /******************************************************************************
1961  *              CoLockObjectExternal    [OLE32.@]
1962  */
1963 HRESULT WINAPI CoLockObjectExternal(
1964     LPUNKNOWN pUnk,             /* [in] object to be locked */
1965     BOOL fLock,         /* [in] do lock */
1966     BOOL fLastUnlockReleases) /* [in] unlock all */
1967 {
1968     TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
1969           pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
1970
1971         if (fLock) {
1972             /*
1973              * Increment the external lock coutner, COM_ExternalLockAddRef also
1974              * increment the object's internal lock counter.
1975              */
1976             COM_ExternalLockAddRef( pUnk);
1977         } else {
1978             /*
1979              * Decrement the external lock coutner, COM_ExternalLockRelease also
1980              * decrement the object's internal lock counter.
1981              */
1982             COM_ExternalLockRelease( pUnk, fLastUnlockReleases);
1983         }
1984
1985         return S_OK;
1986 }
1987
1988 /***********************************************************************
1989  *           CoInitializeWOW (OLE32.@)
1990  */
1991 HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y) {
1992     FIXME("(0x%08lx,0x%08lx),stub!\n",x,y);
1993     return 0;
1994 }
1995
1996 /***********************************************************************
1997  *           CoGetState [OLE32.@]
1998  *
1999  * NOTES: might be incomplete
2000  */
2001 HRESULT WINAPI CoGetState(IUnknown ** ppv)
2002 {
2003         APARTMENT * apt = COM_CurrentInfo();
2004
2005         FIXME("\n");
2006
2007         if(apt && apt->state) {
2008             IUnknown_AddRef(apt->state);
2009             *ppv = apt->state;
2010             FIXME("-- %p\n", *ppv);
2011             return S_OK;
2012         }
2013         *ppv = NULL;
2014         return E_FAIL;
2015
2016 }
2017
2018 /***********************************************************************
2019  *           CoSetState [OLE32.@]
2020  *
2021  */
2022 HRESULT WINAPI CoSetState(IUnknown * pv)
2023 {
2024     APARTMENT * apt = COM_CurrentInfo();
2025
2026     if (!apt) apt = COM_CreateApartment(COINIT_UNINITIALIZED);
2027
2028         FIXME("(%p),stub!\n", pv);
2029
2030         if (pv) {
2031             IUnknown_AddRef(pv);
2032         }
2033
2034         if (apt->state) {
2035             TRACE("-- release %p now\n", apt->state);
2036             IUnknown_Release(apt->state);
2037         }
2038         apt->state = pv;
2039         return S_OK;
2040 }
2041
2042
2043 /******************************************************************************
2044  *              OleGetAutoConvert        [OLE32.@]
2045  */
2046 HRESULT WINAPI OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pClsidNew)
2047 {
2048     HKEY hkey = 0;
2049     char buf[200];
2050     WCHAR wbuf[200];
2051     DWORD len;
2052     HRESULT res = S_OK;
2053
2054     sprintf(buf,"CLSID\\");WINE_StringFromCLSID(clsidOld,&buf[6]);
2055     if (RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&hkey))
2056     {
2057         res = REGDB_E_CLASSNOTREG;
2058         goto done;
2059     }
2060     len = 200;
2061     /* we can just query for the default value of AutoConvertTo key like that,
2062        without opening the AutoConvertTo key and querying for NULL (default) */
2063     if (RegQueryValueA(hkey,"AutoConvertTo",buf,&len))
2064     {
2065         res = REGDB_E_KEYMISSING;
2066         goto done;
2067     }
2068     MultiByteToWideChar( CP_ACP, 0, buf, -1, wbuf, sizeof(wbuf)/sizeof(WCHAR) );
2069     CLSIDFromString(wbuf,pClsidNew);
2070 done:
2071     if (hkey) RegCloseKey(hkey);
2072     return res;
2073 }
2074
2075 /******************************************************************************
2076  *              OleSetAutoConvert        [OLE32.@]
2077  */
2078 HRESULT WINAPI OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew)
2079 {
2080     HKEY hkey = 0;
2081     char buf[200], szClsidNew[200];
2082     HRESULT res = S_OK;
2083
2084     TRACE("(%s,%s)\n", debugstr_guid(clsidOld), debugstr_guid(clsidNew));
2085     sprintf(buf,"CLSID\\");WINE_StringFromCLSID(clsidOld,&buf[6]);
2086     WINE_StringFromCLSID(clsidNew, szClsidNew);
2087     if (RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&hkey))
2088     {
2089         res = REGDB_E_CLASSNOTREG;
2090         goto done;
2091     }
2092     if (RegSetValueA(hkey, "AutoConvertTo", REG_SZ, szClsidNew, strlen(szClsidNew)+1))
2093     {
2094         res = REGDB_E_WRITEREGDB;
2095         goto done;
2096     }
2097
2098 done:
2099     if (hkey) RegCloseKey(hkey);
2100     return res;
2101 }
2102
2103 /******************************************************************************
2104  *              OleDoAutoConvert        [OLE32.@]
2105  */
2106 HRESULT WINAPI OleDoAutoConvert(IStorage *pStg, LPCLSID pClsidNew)
2107 {
2108     FIXME("(%p,%p) : stub\n",pStg,pClsidNew);
2109     return E_NOTIMPL;
2110 }
2111
2112 /******************************************************************************
2113  *              CoTreatAsClass        [OLE32.@]
2114  *
2115  * Sets TreatAs value of a class
2116  */
2117 HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
2118 {
2119     HKEY hkey = 0;
2120     char buf[47];
2121     char szClsidNew[39];
2122     HRESULT res = S_OK;
2123     char auto_treat_as[39];
2124     LONG auto_treat_as_size = sizeof(auto_treat_as);
2125     CLSID id;
2126
2127     sprintf(buf,"CLSID\\");WINE_StringFromCLSID(clsidOld,&buf[6]);
2128     WINE_StringFromCLSID(clsidNew, szClsidNew);
2129     if (RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&hkey))
2130     {
2131         res = REGDB_E_CLASSNOTREG;
2132         goto done;
2133     }
2134     if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
2135     {
2136        if (!RegQueryValueA(hkey, "AutoTreatAs", auto_treat_as, &auto_treat_as_size) &&
2137            !__CLSIDFromStringA(auto_treat_as, &id))
2138        {
2139            if (RegSetValueA(hkey, "TreatAs", REG_SZ, auto_treat_as, strlen(auto_treat_as)+1))
2140            {
2141                res = REGDB_E_WRITEREGDB;
2142                goto done;
2143            }
2144        }
2145        else
2146        {
2147            RegDeleteKeyA(hkey, "TreatAs");
2148            goto done;
2149        }
2150     }
2151     else if (RegSetValueA(hkey, "TreatAs", REG_SZ, szClsidNew, strlen(szClsidNew)+1))
2152     {
2153        res = REGDB_E_WRITEREGDB;
2154         goto done;
2155     }
2156
2157 done:
2158     if (hkey) RegCloseKey(hkey);
2159     return res;
2160 }
2161
2162 /******************************************************************************
2163  *              CoGetTreatAsClass        [OLE32.@]
2164  *
2165  * Reads the TreatAs value from a class.
2166  */
2167 HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
2168 {
2169     HKEY hkey = 0;
2170     char buf[200], szClsidNew[200];
2171     HRESULT res = S_OK;
2172     LONG len = sizeof(szClsidNew);
2173
2174     FIXME("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
2175     sprintf(buf,"CLSID\\");WINE_StringFromCLSID(clsidOld,&buf[6]);
2176     memcpy(clsidNew,clsidOld,sizeof(CLSID)); /* copy over old value */
2177
2178     if (RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&hkey))
2179     {
2180         res = REGDB_E_CLASSNOTREG;
2181         goto done;
2182     }
2183     if (RegQueryValueA(hkey, "TreatAs", szClsidNew, &len))
2184     {
2185         res = S_FALSE;
2186         goto done;
2187     }
2188     res = __CLSIDFromStringA(szClsidNew,clsidNew);
2189     if (FAILED(res))
2190         FIXME("Failed CLSIDFromStringA(%s), hres %lx?\n",szClsidNew,res);
2191 done:
2192     if (hkey) RegCloseKey(hkey);
2193     return res;
2194     
2195 }
2196
2197 /***********************************************************************
2198  *           IsEqualGUID [OLE32.@]
2199  *
2200  * Compares two Unique Identifiers.
2201  *
2202  * RETURNS
2203  *      TRUE if equal
2204  */
2205 #undef IsEqualGUID
2206 BOOL WINAPI IsEqualGUID(
2207      REFGUID rguid1, /* [in] unique id 1 */
2208      REFGUID rguid2  /* [in] unique id 2 */
2209      )
2210 {
2211     return !memcmp(rguid1,rguid2,sizeof(GUID));
2212 }
2213
2214 /***********************************************************************
2215  *           CoInitializeSecurity [OLE32.@]
2216  */
2217 HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc,
2218                                     SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
2219                                     void* pReserved1, DWORD dwAuthnLevel,
2220                                     DWORD dwImpLevel, void* pReserved2,
2221                                     DWORD dwCapabilities, void* pReserved3)
2222 {
2223   FIXME("(%p,%ld,%p,%p,%ld,%ld,%p,%ld,%p) - stub!\n", pSecDesc, cAuthSvc,
2224         asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pReserved2,
2225         dwCapabilities, pReserved3);
2226   return S_OK;
2227 }
2228
2229 /***********************************************************************
2230  *           CoSuspendClassObjects [OLE32.@]
2231  */
2232 HRESULT WINAPI CoSuspendClassObjects(void)
2233 {
2234     FIXME("\n");
2235     return S_OK;
2236 }
2237
2238 /***********************************************************************
2239  *           CoAddRefServerProcess [OLE32.@]
2240  */
2241 ULONG WINAPI CoAddRefServerProcess(void)
2242 {
2243     FIXME("\n");
2244     return 2;
2245 }
2246
2247 /***********************************************************************
2248  *           CoReleaseServerProcess [OLE32.@]
2249  */
2250 ULONG WINAPI CoReleaseServerProcess(void)
2251 {
2252     FIXME("\n");
2253     return 1;
2254 }