‘店大欺人’这句话放在哪里都适用,浏览器市场亦是如此。IE当道,其它浏览器如若显示跟其不一致,往往会被打上‘不标准’的烙记,也迫使HTML使用者不得不用IE来检测是否符合‘标准’。真的很杯具,且不说IE自己定义了大量‘不标准’的Tag,谁又能确定IE对标准的执行本身是否‘标准’呢?平常我用Google Chrome,但写Blog时还是需要切换到IE上,谁叫这些控件都是依据IE作为‘标准’的。这也是没有办法的事情,毕竟IE曾经太强大了,致使现在依然余威不散啊。。。
扯远了,我就来发发牢骚了,实际想说的是,为了让LingosHook的HTML展示更接近Lingoes的显示,这几天不得不在尝试让LingosHook也支持适用IE来显示结果,谁叫Lingoes用IE呢。。
下面轻松一下,做个GAME--看图找不同。。


是的,还是第二张图的显示比较好看~第一张图是使用wxWidget自带的wxHtmlWin控件显示的,而第二张则是通过Activx调用IE控件显示的。
wxWidget下封装IE控件,能直接找到的就是wxActivex (这个就是常说的wxIE)了,虽然控件本身老是老了点,2005年发布的,但依然很好用--当然了,为了在wxWidget2.8下编译,为了支持中文显示,还是需要做一些修改的。
如何在wxWidget2.8下编译,这个问题改改并不难,就不说了,就单说说这个中文显示问题吧。wxActivex使用LoadString()来显示内存中的字符串,实现如下:
bool wxIEHtmlWin::LoadString(const wxString& html)


{
char *data = NULL;
size_t len = html.length();
#ifdef UNICODE
len *= 2;
#endif
data = (char *) malloc(len);
memcpy(data, html.c_str(), len);

return LoadStream(new wxOwnedMemInputStream(data, len));
};
如果变量html中的字符都是char类型也没啥问题,memcpy一下就OK,但如果是wchar_t宽字节类型,就不能单单调用一下memcpy了,这个涉及到宽字节到多字节(WC->MB)的问题了。于是照着葫芦画瓢,添加了如下代码,这个问题就过了。。。。
class IStreamFromWString : public IStream


{
private:
DECLARE_OLE_UNKNOWN(IStreamFromWString);

public:
IStreamFromWString(const wxString& str)
: _buffer(NULL), _sz(0), _pos(0)

{
InitBuffer(str);
}
virtual ~IStreamFromWString()

{
FreeBuffer();
}
// ISequentialStream
HRESULT STDMETHODCALLTYPE Read(void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbRead)

{
if(_pos >= _sz)

{
(*pcbRead) = 0;
return S_OK;
}

if((_pos + cb) < _sz)

{
memcpy((void*)pv, (void*)(_buffer + _pos), cb);
(*pcbRead) = cb;
_pos += cb;
return S_OK;
}
else

{
memcpy((void*)pv, (void*)(_buffer + _pos), _sz - _pos);
(*pcbRead) = (_sz - _pos);
_pos = _sz;
return S_OK;
}
};

// IStream

HRESULT STDMETHODCALLTYPE Write(const void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbWritten)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER __RPC_FAR *plibNewPosition)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE CopyTo(IStream __RPC_FAR *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER __RPC_FAR *pcbRead, ULARGE_INTEGER __RPC_FAR *pcbWritten)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE Revert(void)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE Stat(STATSTG __RPC_FAR *pstatstg, DWORD grfStatFlag)
{return E_NOTIMPL;}

HRESULT STDMETHODCALLTYPE Clone(IStream __RPC_FAR *__RPC_FAR *ppstm)
{return E_NOTIMPL;}
private:
void InitBuffer(const wxString& str)

{
_sz = wxConvUTF8.FromWChar(NULL, 0, str.c_str(), str.size());
_buffer = new char[_sz];
wxConvUTF8.FromWChar(_buffer, _sz, str.c_str(), str.size());
//int codepage = 54936;//CP_UTF8;

//int sz = WideCharToMultiByte(codepage, 0, html.c_str(), html.size(), NULL, 0, NULL, NULL);
//if(sz == -1)
// return -1;
//char* buf = new char[sz + 1];
//sz = WideCharToMultiByte(codepage, 0, html.c_str(), html.size(), buf, sz, NULL, NULL);
}
void FreeBuffer()

{
if(_buffer != NULL)
delete [] _buffer;
}
private:
char * _buffer;
size_t _sz;
size_t _pos;
};

DEFINE_OLE_TABLE(IStreamFromWString)
OLE_IINTERFACE(IUnknown)
OLE_IINTERFACE(ISequentialStream)
OLE_IINTERFACE(IStream)
END_OLE_TABLE;

bool wxIEHtmlWin::LoadWString(const wxString& html)


{
IDispatch *pDisp = NULL;
HRESULT hret = m_webBrowser->get_Document(&pDisp);
if (!pDisp)
return false;
wxAutoOleInterface<IDispatch> disp(pDisp);
// get IPersistStreamInit
wxAutoOleInterface<IPersistStreamInit> pPersistStreamInit(IID_IPersistStreamInit, disp);

if (pPersistStreamInit.Ok())

{
HRESULT hr = pPersistStreamInit->InitNew();

if (SUCCEEDED(hr))

{
CComPtr<IStream> is(new IStreamFromWString(html));
hr = pPersistStreamInit->Load(is);
}

return SUCCEEDED(hr);
}
else
return false;
}

可以看出,也没做什么,就是调用一下类似WideCharToMultiByte()就OK了。编码LingosHook的过程中,最让我感慨的事就是--原来char到wchar_t、string到wstring是如此的繁琐,陷阱重重。。。唉,一切都是charset引起的,要是当年ASCII设计者们有点‘国际主义’精神,直接用定义出Unicode多好,哪有中间这么多charset的问题。。。(发牢骚而已,谁也不是先知。。。)
另外定义了个宏__LH_USE_IE__,用于编译期切换所使用的控件,不喜欢IE的,可以继续使用wxHtmlWin,嘿嘿,要留好‘革命的火种’啊。。。
#ifdef __LH_USE_WXIE__

#include "IEHtmlWin.h"

class CLHHtmlWindow : public wxIEHtmlWin


{
public:
CLHHtmlWindow(wxWindow * parent, wxWindowID id = -1, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxPanelNameStr)
: wxIEHtmlWin(parent, id, pos, size, style, name)

{
}

virtual ~CLHHtmlWindow()
{}

public:

void LoadBlankPage()
{ wxIEHtmlWin::LoadWString(wxT("<HTML></HTML>")); }

bool LoadString(const wxString& html)
{ return wxIEHtmlWin::LoadWString(html); }

void SetCharset(const wxString& charset)
{ wxIEHtmlWin::SetCharset(charset); }
};

#else

#include <wx/html/htmlwin.h>

class CLHHtmlWindow : public wxHtmlWindow


{
public:
CLHHtmlWindow(wxWindow * parent, wxWindowID id = -1, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxPanelNameStr)
: wxHtmlWindow(parent, id, pos, size, style, name)

{
}

virtual ~CLHHtmlWindow()
{}

public:

void LoadBlankPage()
{ wxHtmlWindow::SetPage(wxT("<HTML></HTML>")); }
bool LoadString(const wxString& html)

{
wxString str = html;
str.Replace(_("file:///"), _(""), true);
return wxHtmlWindow::SetPage(str);
}

void SetCharset(const wxString& charset)
{}
};

#endif