riched20: Fix one more memory leak.
[wine] / dlls / atl / atl_ax.c
1 /*
2  * Active Template Library ActiveX functions (atl.dll)
3  *
4  * Copyright 2006 Andrey Turkin
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winerror.h"
30 #include "winuser.h"
31 #include "wine/debug.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "ole2.h"
35 #include "exdisp.h"
36 #include "atlbase.h"
37 #include "atliface.h"
38 #include "atlwin.h"
39
40 #include "wine/unicode.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(atl);
43
44 /**********************************************************************
45  * AtlAxWin class window procedure
46  */
47 LRESULT static CALLBACK AtlAxWin_wndproc( HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam )
48 {
49     if ( wMsg == WM_CREATE )
50     {
51             DWORD len = GetWindowTextLengthW( hWnd ) + 1;
52             WCHAR *ptr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
53             if (!ptr)
54                 return 1;
55             GetWindowTextW( hWnd, ptr, len );
56             AtlAxCreateControlEx( ptr, hWnd, NULL, NULL, NULL, NULL, NULL );
57             HeapFree( GetProcessHeap(), 0, ptr );
58             return 0;
59     }
60     return DefWindowProcW( hWnd, wMsg, wParam, lParam );
61 }
62
63 /***********************************************************************
64  *           AtlAxWinInit          [ATL.@]
65  * Initializes the control-hosting code: registering the AtlAxWin, 
66  * AtlAxWin7 and AtlAxWinLic7 window classes and some messages.
67  *
68  * RETURNS
69  *  TRUE or FALSE
70  */
71  
72 BOOL WINAPI AtlAxWinInit(void)
73 {
74     WNDCLASSEXW wcex;
75     const WCHAR AtlAxWin[] = {'A','t','l','A','x','W','i','n',0};
76
77     FIXME("semi-stub\n");
78
79     if ( FAILED( OleInitialize(NULL) ) )
80         return FALSE;
81
82     wcex.cbSize        = sizeof(wcex);
83     wcex.style         = 0;
84     wcex.cbClsExtra    = 0;
85     wcex.cbWndExtra    = 0;
86     wcex.hInstance     = GetModuleHandleW( NULL );
87     wcex.hIcon         = NULL;
88     wcex.hCursor       = NULL;
89     wcex.hbrBackground = NULL;
90     wcex.lpszMenuName  = NULL;
91     wcex.hIconSm       = 0;
92
93     wcex.lpfnWndProc   = AtlAxWin_wndproc;
94     wcex.lpszClassName = AtlAxWin;
95     if ( !RegisterClassExW( &wcex ) )
96         return FALSE;
97
98     return TRUE;
99 }
100
101 /***********************************************************************
102  *           AtlAxCreateControl           [ATL.@]
103  */
104 HRESULT WINAPI AtlAxCreateControl(LPCOLESTR lpszName, HWND hWnd,
105         IStream *pStream, IUnknown **ppUnkContainer)
106 {
107     return AtlAxCreateControlEx( lpszName, hWnd, pStream, ppUnkContainer,
108             NULL, NULL, NULL );
109 }
110
111 /***********************************************************************
112  *           AtlAxCreateControlEx            [ATL.@]
113  *
114  * REMARKS
115  *   See http://www.codeproject.com/com/cwebpage.asp for some background
116  *
117  */
118 HRESULT WINAPI AtlAxCreateControlEx(LPCOLESTR lpszName, HWND hWnd,
119         IStream *pStream, IUnknown **ppUnkContainer, IUnknown **ppUnkControl,
120         REFIID iidSink, IUnknown *punkSink)
121 {
122     CLSID controlId;
123     HRESULT hRes;
124     IOleObject *pControl;
125     IUnknown *pUnkControl;
126     IPersistStreamInit *pPSInit;
127     IUnknown *pContainer;
128     enum {IsGUID=0,IsHTML=1,IsURL=2} content;
129
130     TRACE("(%s %p %p %p %p %p %p)\n", debugstr_w(lpszName), hWnd, pStream, 
131             ppUnkContainer, ppUnkControl, iidSink, punkSink);
132
133     hRes = CLSIDFromString( (LPOLESTR) lpszName, &controlId );
134     if ( FAILED(hRes) )
135         hRes = CLSIDFromProgID( lpszName, &controlId );
136     if ( SUCCEEDED( hRes ) )
137         content = IsGUID;
138     else {
139         /* FIXME - check for MSHTML: prefix! */
140         content = IsURL;
141         memcpy( &controlId, &CLSID_WebBrowser, sizeof(controlId) );
142     }
143
144     hRes = CoCreateInstance( &controlId, 0, CLSCTX_ALL, &IID_IOleObject, 
145             (void**) &pControl );
146     if ( FAILED( hRes ) )
147     {
148         WARN( "cannot create ActiveX control %s instance - error 0x%08x\n",
149                 debugstr_guid( &controlId ), hRes );
150         return hRes;
151     }
152
153     hRes = IOleObject_QueryInterface( pControl, &IID_IPersistStreamInit, (void**) &pPSInit );
154     if ( SUCCEEDED( hRes ) )
155     {
156         if (!pStream)
157             IPersistStreamInit_InitNew( pPSInit );
158         else
159             IPersistStreamInit_Load( pPSInit, pStream );
160         IPersistStreamInit_Release( pPSInit );
161     } else
162         WARN("cannot get IID_IPersistStreamInit out of control\n");
163
164     IOleObject_QueryInterface( pControl, &IID_IUnknown, (void**) &pUnkControl );
165     IOleObject_Release( pControl );
166      
167
168     hRes = AtlAxAttachControl( pUnkControl, hWnd, &pContainer );
169     if ( FAILED( hRes ) )
170         WARN("cannot attach control to window\n");
171
172     if ( content == IsURL )
173     {
174         IWebBrowser2 *browser;
175
176         hRes = IOleObject_QueryInterface( pControl, &IID_IWebBrowser2, (void**) &browser );
177         if ( !browser )
178             WARN( "Cannot query IWebBrowser2 interface: %08x\n", hRes );
179         else {
180             VARIANT url;
181             
182             IWebBrowser2_put_Visible( browser, VARIANT_TRUE ); /* it seems that native does this on URL (but do not on MSHTML:! why? */
183
184             V_VT(&url) = VT_BSTR;
185             V_BSTR(&url) = SysAllocString( lpszName );
186
187             hRes = IWebBrowser2_Navigate2( browser, &url, NULL, NULL, NULL, NULL );
188             if ( FAILED( hRes ) )
189                 WARN( "IWebBrowser2::Navigate2 failed: %08x\n", hRes );
190             SysFreeString( V_BSTR(&url) );
191
192             IWebBrowser2_Release( browser );
193         }
194     }
195
196     if (ppUnkContainer)
197     {
198         *ppUnkContainer = pContainer;
199         if ( pContainer )
200             IUnknown_AddRef( pContainer );
201     }
202     if (ppUnkControl)
203     {
204         *ppUnkControl = pUnkControl;
205         if ( pUnkControl )
206             IUnknown_AddRef( pUnkControl );
207     }
208
209     IUnknown_Release( pUnkControl );
210     if ( pContainer )
211         IUnknown_Release( pContainer );
212
213     return S_OK;
214 }
215
216 /***********************************************************************
217  *           AtlAxAttachControl           [ATL.@]
218  */
219 HRESULT WINAPI AtlAxAttachControl(IUnknown* pControl, HWND hWnd, IUnknown** ppUnkContainer)
220 {
221     FIXME( "(%p %p %p) - stub\n", pControl, hWnd, ppUnkContainer );
222     return E_NOTIMPL;
223 }
224
225 /**********************************************************************
226  * Helper function for AX_ConvertDialogTemplate
227  */
228 static inline BOOL advance_array(WORD **pptr, DWORD *palloc, DWORD *pfilled, const WORD *data, DWORD size)
229 {
230     if ( (*pfilled + size) > *palloc )
231     {
232         *palloc = ((*pfilled+size) + 0xFF) & ~0xFF;
233         *pptr = HeapReAlloc( GetProcessHeap(), 0, *pptr, *palloc * sizeof(WORD) );
234         if (!*pptr)
235             return FALSE;
236     }
237     RtlMoveMemory( *pptr+*pfilled, data, size * sizeof(WORD) );
238     *pfilled += size;
239     return TRUE;
240 }
241
242 /**********************************************************************
243  * Convert ActiveX control templates to AtlAxWin class instances
244  */
245 static LPDLGTEMPLATEW AX_ConvertDialogTemplate(LPCDLGTEMPLATEW src_tmpl)
246 {
247 #define GET_WORD(x)  (*(const  WORD *)(x))
248 #define GET_DWORD(x) (*(const DWORD *)(x))
249 #define PUT_BLOCK(x,y) do {if (!advance_array(&output, &allocated, &filled, (x), (y))) return NULL;} while (0)
250 #define PUT_WORD(x)  do {WORD w = (x);PUT_BLOCK(&w, 1);} while(0)
251 #define PUT_DWORD(x)  do {DWORD w = (x);PUT_BLOCK(&w, 2);} while(0)
252     const WORD *tmp, *src = (const WORD *)src_tmpl;
253     WORD *output; 
254     DWORD allocated, filled; /* in WORDs */
255     BOOL ext;
256     WORD signature, dlgver, rescount;
257     DWORD style;
258
259     filled = 0; allocated = 256;
260     output = HeapAlloc( GetProcessHeap(), 0, allocated * sizeof(WORD) );
261     if (!output)
262         return NULL;
263     
264     /* header */
265     tmp = src;
266     signature = GET_WORD(src);
267     dlgver = GET_WORD(src + 1);
268     if (signature == 1 && dlgver == 0xFFFF)
269     {
270         ext = TRUE;
271         src += 6;
272         style = GET_DWORD(src);
273         src += 2;
274         rescount = GET_WORD(src++);
275         src += 4;
276         if ( GET_WORD(src) == 0xFFFF ) /* menu */
277             src += 2;
278         else
279             src += strlenW(src) + 1;
280         if ( GET_WORD(src) == 0xFFFF ) /* class */
281             src += 2;
282         else
283             src += strlenW(src) + 1;
284         src += strlenW(src) + 1; /* title */
285         if ( style & (DS_SETFONT | DS_SHELLFONT) )
286         {
287             src += 3;
288             src += strlenW(src) + 1;
289         }
290     } else {
291         ext = FALSE;
292         style = GET_DWORD(src);
293         src += 4;
294         rescount = GET_WORD(src++);
295         src += 4;
296         if ( GET_WORD(src) == 0xFFFF ) /* menu */
297             src += 2;
298         else
299             src += strlenW(src) + 1;
300         if ( GET_WORD(src) == 0xFFFF ) /* class */
301             src += 2;
302         else
303             src += strlenW(src) + 1;
304         src += strlenW(src) + 1; /* title */
305         if ( style & DS_SETFONT )
306         {
307             src++;
308             src += strlenW(src) + 1;
309         }
310     }
311     PUT_BLOCK(tmp, src-tmp);
312
313     while(rescount--)
314     {
315         src = (const WORD *)( ( ((ULONG)src) + 3) & ~3); /* align on DWORD boundary */
316                 filled = (filled + 1) & ~1; /* depends on DWORD-aligned allocation unit */
317
318         tmp = src;
319         if (ext)
320             src += 11;
321         else
322             src += 9;
323         PUT_BLOCK(tmp, src-tmp);
324
325         tmp = src;
326         if ( GET_WORD(src) == 0xFFFF ) /* class */
327         {
328             src += 2;
329         } else
330         {
331             src += strlenW(src) + 1;
332         }
333         src += strlenW(src) + 1; /* title */
334         if ( GET_WORD(tmp) == '{' ) /* all this mess created because of this line */
335         {
336             const WCHAR AtlAxWin[9]={'A','t','l','A','x','W','i','n',0};
337             PUT_BLOCK(AtlAxWin, sizeof(AtlAxWin)/sizeof(WORD));
338             PUT_BLOCK(tmp, strlenW(tmp)+1);
339         } else
340             PUT_BLOCK(tmp, src-tmp);
341
342         if ( GET_WORD(src) )
343         {
344             WORD size = (GET_WORD(src)+sizeof(WORD)-1) / sizeof(WORD); /* quite ugly :( Maybe use BYTE* instead of WORD* everywhere ? */
345             PUT_BLOCK(src, size);
346             src+=size;
347         }
348         else
349         {
350             PUT_WORD(0);
351             src++;
352         }
353     }
354     return (LPDLGTEMPLATEW) output;
355 }
356
357 /***********************************************************************
358  *           AtlAxCreateDialogA           [ATL.@]
359  *
360  * Creates a dialog window
361  *
362  * PARAMS
363  *  hInst   [I] Application instance
364  *  name    [I] Dialog box template name
365  *  owner   [I] Dialog box parent HWND
366  *  dlgProc [I] Dialog box procedure
367  *  param   [I] This value will be passed to dlgProc as WM_INITDIALOG's message lParam 
368  *
369  * RETURNS
370  *  Window handle of dialog window.
371  */
372 HWND WINAPI AtlAxCreateDialogA(HINSTANCE hInst, LPCSTR name, HWND owner, DLGPROC dlgProc ,LPARAM param)
373 {
374     HWND res = NULL;
375     int length;
376     WCHAR *nameW;
377
378     if ( HIWORD(name) == 0 )
379         return AtlAxCreateDialogW( hInst, (LPCWSTR) name, owner, dlgProc, param );
380
381     length = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
382     nameW = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) );
383     if (nameW)
384     {
385         MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, length );
386         res = AtlAxCreateDialogW( hInst, nameW, owner, dlgProc, param );
387         HeapFree( GetProcessHeap(), 0, nameW );
388     }
389     return res;
390 }
391
392 /***********************************************************************
393  *           AtlAxCreateDialogW           [ATL.@]
394  *
395  * See AtlAxCreateDialogA
396  *
397  */
398 HWND WINAPI AtlAxCreateDialogW(HINSTANCE hInst, LPCWSTR name, HWND owner, DLGPROC dlgProc ,LPARAM param)
399 {
400     HRSRC hrsrc;
401     HGLOBAL hgl;
402     LPCDLGTEMPLATEW ptr;
403     LPDLGTEMPLATEW newptr;
404     HWND res;
405
406     FIXME("(%p %s %p %p %lx) - not tested\n", hInst, debugstr_w(name), owner, dlgProc, param);
407
408     hrsrc = FindResourceW( hInst, name, (LPWSTR)RT_DIALOG );
409     if ( !hrsrc )
410         return NULL;
411     hgl = LoadResource (hInst, hrsrc);
412     if ( !hgl )
413         return NULL;
414     ptr = (LPCDLGTEMPLATEW)LockResource ( hgl );
415     if (!ptr)
416     {
417         FreeResource( hgl );
418         return NULL;
419     }
420     newptr = AX_ConvertDialogTemplate( ptr );
421     if ( newptr )
422     {
423             res = CreateDialogIndirectParamW( hInst, newptr, owner, dlgProc, param );
424             HeapFree( GetProcessHeap(), 0, newptr );
425     } else
426         res = NULL;
427     FreeResource ( hrsrc );
428     return res;
429 }