‘店大欺人’这句话放在哪里都适用,浏览器市场亦是如此。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