1 #include "IEHtmlWin.h"
2 #include <wx/strconv.h>
3 #include <wx/string.h>
4 #include <wx/event.h>
5 #include <wx/listctrl.h>
6 #include <wx/mstream.h>
7 #include <oleidl.h>
8 #include <winerror.h>
9 #include <exdispid.h>
10 #include <exdisp.h>
11 #include <olectl.h>
12 #include <Mshtml.h>
13 #include <sstream>
14 using namespace std;
15
16 DEFINE_EVENT_TYPE(wxEVT_COMMAND_MSHTML_BEFORENAVIGATE2);
17 DEFINE_EVENT_TYPE(wxEVT_COMMAND_MSHTML_NEWWINDOW2);
18 DEFINE_EVENT_TYPE(wxEVT_COMMAND_MSHTML_DOCUMENTCOMPLETE);
19 DEFINE_EVENT_TYPE(wxEVT_COMMAND_MSHTML_PROGRESSCHANGE);
20 DEFINE_EVENT_TYPE(wxEVT_COMMAND_MSHTML_STATUSTEXTCHANGE);
21 DEFINE_EVENT_TYPE(wxEVT_COMMAND_MSHTML_TITLECHANGE);
22
23 IMPLEMENT_DYNAMIC_CLASS(wxMSHTMLEvent, wxNotifyEvent);
24
25
26 //////////////////////////////////////////////////////////////////////
27 BEGIN_EVENT_TABLE(wxIEHtmlWin, wxActiveX)
28 END_EVENT_TABLE()
29
30 class FS_DWebBrowserEvents2 : public IDispatch
31 {
32 private:
33 DECLARE_OLE_UNKNOWN(FS_DWebBrowserEvents2);
34
35
36 wxIEHtmlWin *m_iewin;
37
38 public:
FS_DWebBrowserEvents2(wxIEHtmlWin * iewin)39 FS_DWebBrowserEvents2(wxIEHtmlWin *iewin) : m_iewin(iewin) {}
~FS_DWebBrowserEvents2()40 virtual ~FS_DWebBrowserEvents2()
41 {
42 }
43
44 //IDispatch
GetIDsOfNames(REFIID r,OLECHAR ** o,unsigned int i,LCID l,DISPID * d)45 STDMETHODIMP GetIDsOfNames(REFIID r, OLECHAR** o, unsigned int i, LCID l, DISPID* d)
46 {
47 return E_NOTIMPL;
48 };
49
GetTypeInfo(unsigned int i,LCID l,ITypeInfo ** t)50 STDMETHODIMP GetTypeInfo(unsigned int i, LCID l, ITypeInfo** t)
51 {
52 return E_NOTIMPL;
53 };
54
GetTypeInfoCount(unsigned int * i)55 STDMETHODIMP GetTypeInfoCount(unsigned int* i)
56 {
57 return E_NOTIMPL;
58 };
59
Post(WXTYPE etype,wxString text,long l1=0,long l2=0)60 void Post(WXTYPE etype, wxString text, long l1 = 0, long l2 = 0)
61 {
62 if (! m_iewin || ! m_iewin->GetParent())
63 return;
64
65 wxMSHTMLEvent event;
66 event.SetId(m_iewin->GetId());
67 event.SetEventType(etype);
68 event.m_text1 = text;
69 event.m_long1 = l1;
70 event.m_long2 = l2;
71
72 m_iewin->GetParent()->AddPendingEvent(event);
73 };
74
Process(WXTYPE etype,wxString text=_T (""),long l1=0,long l2=0)75 bool Process(WXTYPE etype, wxString text = _T(""), long l1 = 0, long l2 = 0)
76 {
77 if (! m_iewin || ! m_iewin->GetParent())
78 return true;
79
80 wxMSHTMLEvent event;
81 event.SetId(m_iewin->GetId());
82 event.SetEventType(etype);
83 event.m_text1 = text;
84 event.m_long1 = l1;
85 event.m_long2 = l2;
86
87 m_iewin->GetParent()->ProcessEvent(event);
88
89 return event.IsAllowed();
90 };
91
GetStrArg(VARIANT & v)92 wxString GetStrArg(VARIANT& v)
93 {
94 VARTYPE vt = v.vt & ~VT_BYREF;
95
96 if (vt == VT_VARIANT)
97 return GetStrArg(*v.pvarVal);
98 else if (vt == VT_BSTR)
99 {
100 if (v.vt & VT_BYREF)
101 return (v.pbstrVal ? *v.pbstrVal : L"");
102 else
103 return v.bstrVal;
104 }
105 else
106 return _T("");
107 };
108
109 #define STR_ARG(arg) GetStrArg(pDispParams->rgvarg[arg])
110
111 #define LONG_ARG(arg)\
112 (pDispParams->rgvarg[arg].lVal)
113
114
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,unsigned int * puArgErr)115 STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
116 WORD wFlags, DISPPARAMS * pDispParams,
117 VARIANT * pVarResult, EXCEPINFO * pExcepInfo,
118 unsigned int * puArgErr)
119 {
120 if (wFlags & DISPATCH_PROPERTYGET)
121 return E_NOTIMPL;
122
123 switch (dispIdMember)
124 {
125 case DISPID_BEFORENAVIGATE2:
126 if (Process(wxEVT_COMMAND_MSHTML_BEFORENAVIGATE2, STR_ARG(5)))
127 *pDispParams->rgvarg->pboolVal = VARIANT_FALSE;
128 else
129 *pDispParams->rgvarg->pboolVal = VARIANT_TRUE;
130 break;
131
132 case DISPID_NEWWINDOW2:
133 if (Process(wxEVT_COMMAND_MSHTML_NEWWINDOW2))
134 *pDispParams->rgvarg->pboolVal = VARIANT_FALSE;
135 else
136 *pDispParams->rgvarg->pboolVal = VARIANT_TRUE;
137 break;
138
139 case DISPID_PROGRESSCHANGE:
140 Post(wxEVT_COMMAND_MSHTML_PROGRESSCHANGE, _T(""), LONG_ARG(1), LONG_ARG(0));
141 break;
142
143 case DISPID_DOCUMENTCOMPLETE:
144 Post(wxEVT_COMMAND_MSHTML_DOCUMENTCOMPLETE, STR_ARG(0));
145 break;
146
147 case DISPID_STATUSTEXTCHANGE:
148 Post(wxEVT_COMMAND_MSHTML_STATUSTEXTCHANGE, STR_ARG(0));
149 break;
150
151 case DISPID_TITLECHANGE:
152 Post(wxEVT_COMMAND_MSHTML_TITLECHANGE, STR_ARG(0));
153 break;
154 }
155
156 return S_OK;
157 }
158 };
159
160 #undef STR_ARG
161
162 DEFINE_OLE_TABLE(FS_DWebBrowserEvents2)
163 OLE_IINTERFACE(IUnknown)
164 OLE_INTERFACE(DIID_DWebBrowserEvents2, DWebBrowserEvents2)
165 END_OLE_TABLE;
166
167
168 static const CLSID CLSID_MozillaBrowser =
169 { 0x1339B54C, 0x3453, 0x11D2,
170 { 0x93, 0xB9, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00 } };
172
173
174 //#define PROGID "Shell.Explorer"
175 #define PROGID CLSID_WebBrowser
176 //#define PROGID CLSID_MozillaBrowser
177 //#define PROGID CLSID_HTMLDocument
178 //#define PROGID "MSCAL.Calendar"
179 //#define PROGID "WordPad.Document.1"
180 //#define PROGID "SoftwareFX.ChartFX.20"
181
wxIEHtmlWin(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)182 wxIEHtmlWin::wxIEHtmlWin(wxWindow * parent, wxWindowID id,
183 const wxPoint& pos,
184 const wxSize& size,
185 long style,
186 const wxString& name) :
187 wxActiveX(parent, PROGID, id, pos, size, style, name)
188 {
189 SetupBrowser();
190 }
191
192
~wxIEHtmlWin()193 wxIEHtmlWin::~wxIEHtmlWin()
194 {
195 }
196
SetupBrowser()197 void wxIEHtmlWin::SetupBrowser()
198 {
199 HRESULT hret;
200
201 // Get IWebBrowser2 Interface
202 hret = m_webBrowser.QueryInterface(IID_IWebBrowser2, m_ActiveX);
203 assert(SUCCEEDED(hret));
204
205 // Web Browser Events
206 FS_DWebBrowserEvents2 *events = new FS_DWebBrowserEvents2(this);
207 hret = ConnectAdvise(DIID_DWebBrowserEvents2, events);
208 if (! SUCCEEDED(hret))
209 delete events;
210
211 // web browser setup
212 m_webBrowser->put_MenuBar(VARIANT_FALSE);
213 m_webBrowser->put_AddressBar(VARIANT_FALSE);
214 m_webBrowser->put_StatusBar(VARIANT_FALSE);
215 m_webBrowser->put_ToolBar(VARIANT_FALSE);
216
217 m_webBrowser->put_RegisterAsBrowser(VARIANT_TRUE);
218 m_webBrowser->put_RegisterAsDropTarget(VARIANT_TRUE);
219
220 m_webBrowser->Navigate( L"about:blank", NULL, NULL, NULL, NULL );
221 }
222
223
SetEditMode(bool seton)224 void wxIEHtmlWin::SetEditMode(bool seton)
225 {
226 m_bAmbientUserMode = ! seton;
227 AmbientPropertyChanged(DISPID_AMBIENT_USERMODE);
228 };
229
GetEditMode()230 bool wxIEHtmlWin::GetEditMode()
231 {
232 return ! m_bAmbientUserMode;
233 };
234
235
SetCharset(wxString charset)236 void wxIEHtmlWin::SetCharset(wxString charset)
237 {
238 // HTML Document ?
239 IDispatch *pDisp = NULL;
240 HRESULT hret = m_webBrowser->get_Document(&pDisp);
241 wxAutoOleInterface<IDispatch> disp(pDisp);
242
243 if (disp.Ok())
244 {
245 wxAutoOleInterface<IHTMLDocument2> doc(IID_IHTMLDocument2, disp);
246 if (doc.Ok())
247 doc->put_charset((BSTR) (const wchar_t *) charset.wc_str(wxConvUTF8));
248 //doc->put_charset((BSTR) wxConvUTF8.cMB2WC(charset).data());
249 };
250 };
251
252
253 class IStreamAdaptorBase : public IStream
254 {
255 private:
256 DECLARE_OLE_UNKNOWN(IStreamAdaptorBase);
257
258 public:
IStreamAdaptorBase()259 IStreamAdaptorBase() {}
~IStreamAdaptorBase()260 virtual ~IStreamAdaptorBase() {}
261
262 // ISequentialStream
263 HRESULT STDMETHODCALLTYPE Read(void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbRead) = 0;
Write(const void __RPC_FAR * pv,ULONG cb,ULONG __RPC_FAR * pcbWritten)264 HRESULT STDMETHODCALLTYPE Write(const void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbWritten) {return E_NOTIMPL;}
265
266 // IStream
Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER __RPC_FAR * plibNewPosition)267 HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER __RPC_FAR *plibNewPosition) {return E_NOTIMPL;}
SetSize(ULARGE_INTEGER libNewSize)268 HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize) {return E_NOTIMPL;}
CopyTo(IStream __RPC_FAR * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER __RPC_FAR * pcbRead,ULARGE_INTEGER __RPC_FAR * pcbWritten)269 HRESULT STDMETHODCALLTYPE CopyTo(IStream __RPC_FAR *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER __RPC_FAR *pcbRead, ULARGE_INTEGER __RPC_FAR *pcbWritten) {return E_NOTIMPL;}
Commit(DWORD grfCommitFlags)270 HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags) {return E_NOTIMPL;}
Revert(void)271 HRESULT STDMETHODCALLTYPE Revert(void) {return E_NOTIMPL;}
LockRegion(ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)272 HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {return E_NOTIMPL;}
UnlockRegion(ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)273 HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) {return E_NOTIMPL;}
Stat(STATSTG __RPC_FAR * pstatstg,DWORD grfStatFlag)274 HRESULT STDMETHODCALLTYPE Stat(STATSTG __RPC_FAR *pstatstg, DWORD grfStatFlag) {return E_NOTIMPL;}
Clone(IStream __RPC_FAR * __RPC_FAR * ppstm)275 HRESULT STDMETHODCALLTYPE Clone(IStream __RPC_FAR *__RPC_FAR *ppstm) {return E_NOTIMPL;}
276 };
277
278 DEFINE_OLE_TABLE(IStreamAdaptorBase)
279 OLE_IINTERFACE(IUnknown)
280 OLE_IINTERFACE(ISequentialStream)
281 OLE_IINTERFACE(IStream)
282 END_OLE_TABLE;
283
284 class IStreamAdaptor : public IStreamAdaptorBase
285 {
286 private:
287 istream *m_is;
288
289 public:
290
IStreamAdaptor(istream * is)291 IStreamAdaptor(istream *is) : IStreamAdaptorBase(), m_is(is)
292 {
293 wxASSERT(m_is != NULL);
294 }
~IStreamAdaptor()295 ~IStreamAdaptor()
296 {
297 delete m_is;
298 }
299
300 // ISequentialStream
Read(void __RPC_FAR * pv,ULONG cb,ULONG __RPC_FAR * pcbRead)301 HRESULT STDMETHODCALLTYPE Read(void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbRead)
302 {
303 m_is->read((char *) pv, cb);
304 if (pcbRead)
305 *pcbRead = m_is->gcount();
306
307 return S_OK;
308 };
309 };
310
311 class IwxStreamAdaptor : public IStreamAdaptorBase
312 {
313 private:
314 wxInputStream *m_is;
315
316 public:
317
IwxStreamAdaptor(wxInputStream * is)318 IwxStreamAdaptor(wxInputStream *is) : IStreamAdaptorBase(), m_is(is)
319 {
320 wxASSERT(m_is != NULL);
321 }
~IwxStreamAdaptor()322 ~IwxStreamAdaptor()
323 {
324 delete m_is;
325 }
326
327 // ISequentialStream
Read(void __RPC_FAR * pv,ULONG cb,ULONG __RPC_FAR * pcbRead)328 HRESULT STDMETHODCALLTYPE Read(void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbRead)
329 {
330 m_is->Read((char *) pv, cb);
331 if (pcbRead)
332 *pcbRead = m_is->LastRead();
333
334 return S_OK;
335 };
336 };
337
338
LoadUrl(const wxString & url)339 void wxIEHtmlWin::LoadUrl(const wxString& url)
340 {
341 VARIANTARG navFlag, targetFrame, postData, headers;
342 navFlag.vt = VT_EMPTY;
343 navFlag.vt = VT_I2;
344 navFlag.iVal = navNoReadFromCache;
345 targetFrame.vt = VT_EMPTY;
346 postData.vt = VT_EMPTY;
347 headers.vt = VT_EMPTY;
348
349 HRESULT hret = 0;
350
351 IDispatch *pDisp = NULL;
352
353 hret = m_webBrowser->get_Document(&pDisp);
354 wxAutoOleInterface<IDispatch> disp(pDisp);
355
356 #if 0
357 if (m_webBrowser.Ok()&&disp.Ok())
358 {
359 wxAutoOleInterface<IHTMLDocument2> doc(IID_IHTMLDocument2, disp);
360 if (doc.Ok()){
361 IHTMLLocation *p;
362 if( doc->get_location(&p) == S_OK )
363 {
364 hret=p->replace((BSTR) (const wchar_t *) url.wc_str(/*wxConvUTF8*/wxConvLocal));
365 if( hret == S_OK )
366 return;
367 }
368 }
369 }
370 #endif
371 hret = m_webBrowser->Navigate((BSTR) (const wchar_t *) url.wc_str(/*wxConvUTF8*/wxConvLocal),
372 &navFlag, &targetFrame, &postData, &headers);
373 };
374
375 class wxOwnedMemInputStream : public wxMemoryInputStream
376 {
377 public:
378 char *m_data;
379
wxOwnedMemInputStream(char * data,size_t len)380 wxOwnedMemInputStream(char *data, size_t len) :
381 wxMemoryInputStream(data, len), m_data(data)
382 {}
~wxOwnedMemInputStream()383 ~wxOwnedMemInputStream()
384 {
385 free(m_data);
386 }
387 };
388
LoadString(wxString html)389 bool wxIEHtmlWin::LoadString(wxString html)
390 {
391 char *data = NULL;
392 size_t len = html.length();
393 #ifdef UNICODE
394 len *= 2;
395 #endif
396 data = (char *) malloc(len);
397 memcpy(data, html.c_str(), len);
398 return LoadStream(new wxOwnedMemInputStream(data, len));
399 };
400
LoadStream(IStreamAdaptorBase * pstrm)401 bool wxIEHtmlWin::LoadStream(IStreamAdaptorBase *pstrm)
402 {
403 wxAutoOleInterface<IStream> strm(pstrm);
404
405 // Document Interface
406 IDispatch *pDisp = NULL;
407 HRESULT hret = m_webBrowser->get_Document(&pDisp);
408 if (! pDisp)
409 return false;
410 wxAutoOleInterface<IDispatch> disp(pDisp);
411
412
413 // get IPersistStreamInit
414 wxAutoOleInterface<IPersistStreamInit>
415 pPersistStreamInit(IID_IPersistStreamInit, disp);
416
417 if (pPersistStreamInit.Ok())
418 {
419 HRESULT hr = pPersistStreamInit->InitNew();
420 if (SUCCEEDED(hr))
421 hr = pPersistStreamInit->Load(strm);
422
423 return SUCCEEDED(hr);
424 }
425 else
426 return false;
427 };
428
LoadStream(istream * is)429 bool wxIEHtmlWin::LoadStream(istream *is)
430 {
431 // wrap reference around stream
432 IStreamAdaptor *pstrm = new IStreamAdaptor(is);
433 pstrm->AddRef();
434
435 return LoadStream(pstrm);
436 };
437
LoadStream(wxInputStream * is)438 bool wxIEHtmlWin::LoadStream(wxInputStream *is)
439 {
440 // wrap reference around stream
441 IwxStreamAdaptor *pstrm = new IwxStreamAdaptor(is);
442 pstrm->AddRef();
443
444 return LoadStream(pstrm);
445 };
446
447
GoBack()448 bool wxIEHtmlWin::GoBack()
449 {
450 HRESULT hret = 0;
451 hret = m_webBrowser->GoBack();
452 return hret == S_OK;
453 }
454
GoForward()455 bool wxIEHtmlWin::GoForward()
456 {
457 HRESULT hret = 0;
458 hret = m_webBrowser->GoForward();
459 return hret == S_OK;
460 }
461
GoHome()462 bool wxIEHtmlWin::GoHome()
463 {
464 HRESULT hret = 0;
465 hret = m_webBrowser->GoHome();
466 return hret == S_OK;
467 }
468
GoSearch()469 bool wxIEHtmlWin::GoSearch()
470 {
471 HRESULT hret = 0;
472 hret = m_webBrowser->GoSearch();
473 return hret == S_OK;
474 }
475
Refresh(wxIEHtmlRefreshLevel level)476 bool wxIEHtmlWin::Refresh(wxIEHtmlRefreshLevel level)
477 {
478 VARIANTARG levelArg;
479 HRESULT hret = 0;
480
481 levelArg.vt = VT_I2;
482 levelArg.iVal = level;
483 hret = m_webBrowser->Refresh2(&levelArg);
484 return hret == S_OK;
485 }
486
Stop()487 bool wxIEHtmlWin::Stop()
488 {
489 HRESULT hret = 0;
490 hret = m_webBrowser->Stop();
491 return hret == S_OK;
492 }
493
494
495 ///////////////////////////////////////////////////////////////////////////////
496
GetSelObject(IOleObject * oleObject)497 static wxAutoOleInterface<IHTMLSelectionObject> GetSelObject(IOleObject *oleObject)
498 {
499 // Query for IWebBrowser interface
500 wxAutoOleInterface<IWebBrowser2> wb(IID_IWebBrowser2, oleObject);
501 if (! wb.Ok())
502 return wxAutoOleInterface<IHTMLSelectionObject>();
503
504 IDispatch *iDisp = NULL;
505 HRESULT hr = wb->get_Document(&iDisp);
506 if (hr != S_OK)
507 return wxAutoOleInterface<IHTMLSelectionObject>();
508
509 // Query for Document Interface
510 wxAutoOleInterface<IHTMLDocument2> hd(IID_IHTMLDocument2, iDisp);
511 iDisp->Release();
512
513 if (! hd.Ok())
514 return wxAutoOleInterface<IHTMLSelectionObject>();
515
516 IHTMLSelectionObject *_so = NULL;
517 hr = hd->get_selection(&_so);
518
519 // take ownership of selection object
520 wxAutoOleInterface<IHTMLSelectionObject> so(_so);
521
522 return so;
523 };
524
GetSelRange(IOleObject * oleObject)525 static wxAutoOleInterface<IHTMLTxtRange> GetSelRange(IOleObject *oleObject)
526 {
527 wxAutoOleInterface<IHTMLTxtRange> tr;
528
529 wxAutoOleInterface<IHTMLSelectionObject> so(GetSelObject(oleObject));
530 if (! so)
531 return tr;
532
533 IDispatch *iDisp = NULL;
534 HRESULT hr = so->createRange(&iDisp);
535 if (hr != S_OK)
536 return tr;
537
538 // Query for IHTMLTxtRange interface
539 tr.QueryInterface(IID_IHTMLTxtRange, iDisp);
540 iDisp->Release();
541 return tr;
542 };
543
544
GetStringSelection(bool asHTML)545 wxString wxIEHtmlWin::GetStringSelection(bool asHTML)
546 {
547 wxAutoOleInterface<IHTMLTxtRange> tr(GetSelRange(m_oleObject));
548 if (! tr)
549 return _T("");
550
551 BSTR text = NULL;
552 HRESULT hr = E_FAIL;
553
554 if (asHTML)
555 hr = tr->get_htmlText(&text);
556 else
557 hr = tr->get_text(&text);
558 if (hr != S_OK)
559 return _T("");
560
561 wxString s = text;
562 SysFreeString(text);
563
564 return s;
565 };
566
GetText(bool asHTML)567 wxString wxIEHtmlWin::GetText(bool asHTML)
568 {
569 if (! m_webBrowser.Ok())
570 return _T("");
571
572 // get document dispatch interface
573 IDispatch *iDisp = NULL;
574 HRESULT hr = m_webBrowser->get_Document(&iDisp);
575 if (hr != S_OK)
576 return _T("");
577
578 // Query for Document Interface
579 wxAutoOleInterface<IHTMLDocument2> hd(IID_IHTMLDocument2, iDisp);
580 iDisp->Release();
581
582 if (! hd.Ok())
583 return _T("");
584
585 // get body element
586 IHTMLElement *_body = NULL;
587 hd->get_body(&_body);
588 if (! _body)
589 return _T("");
590 wxAutoOleInterface<IHTMLElement> body(_body);
591
592 // get inner text
593 BSTR text = NULL;
594 hr = E_FAIL;
595
596 if (asHTML)
597 hr = body->get_innerHTML(&text);
598 else
599 hr = body->get_innerText(&text);
600 if (hr != S_OK)
601 return _T("");
602
603 wxString s = text;
604 SysFreeString(text);
605
606 return s;
607 };
608