随笔 - 55, 评论 - 298, 引用 - 17

导航

关于

我是新人,: )

每月存档

最新留言

  • re:Last day at Microsoft
    <p><a href="http://www.moretiffany.com/">tiffany jewelry</a> Choose, buy...
    by sibat0705(注册) on 2010/3/12 20:48:58
  • bVSVKxjrysxCAx
    zKTeMu &lt;a href=&quot;http://jmvdsaxpwywz.com/&quot;&gt;jmvdsaxpwywz&lt;/a&am...
    by algkkzvif(匿名) on 2010/2/21 22:53:15
  • DLXXSrOqat
    F3OQLk &lt;a href=&quot;http://pfpvdtnczscw.com/&quot;&gt;pfpvdtnczscw&lt;/a&am...
    by sfxnoylvca(匿名) on 2010/2/21 21:28:18
  • iNOutySOJKTsFyl
    kSPmy1 &lt;a href=&quot;http://kdajmdtvcxfu.com/&quot;&gt;kdajmdtvcxfu&lt;/a&am...
    by lydyggun(匿名) on 2010/2/21 20:21:46
  • mDUfBYTmJjTiGrv
    IXumnI &lt;a href=&quot;http://lamuwgvmprtw.com/&quot;&gt;lamuwgvmprtw&lt;/a&am...
    by gacyafcvtas(匿名) on 2010/2/14 4:47:26
  • oiUMbbvrAlLbcr
    ot9Ga3 &lt;a href=&quot;http://nwawfslgnomg.com/&quot;&gt;nwawfslgnomg&lt;/a&am...
    by cvalpp(匿名) on 2010/2/14 4:46:45
  • re:Last day at Microsoft
    写的真好,看了觉得很受到启发,谢谢,
    by Zheying Zheng(匿名) on 2010/2/4 10:35:20
  • re:REST API的身份验证(Authentication)
    <p>顶</p>
    by kekesoft(注册) on 2010/1/27 20:27:43
  • re:Last day at Microsoft
    祝你好运啊
    by Eric v(匿名) on 2010/1/24 1:53:34
  • re:Last day at Microsoft
    鄙人正在打算内部调动,跨过欢德福卡海峡去西雅图呢, 以后互通有无,常联系.
    by Charlie 木匠(匿名) on 2010/1/11 4:23:23
  • re:Last day at Microsoft
    Good luck, buddy.
    by Huimiao Liu(匿名) on 2010/1/10 20:18:24
  • re:Last day at Microsoft
    &lt;p&gt;Oops,you are right. 改正了。&lt;/p&gt;
    by demonfox(匿名) on 2010/1/10 18:22:13
  • re:Last day at Microsoft
    2006年8月14日起至2009年1月8日止 是2010年吧
    by q(匿名) on 2010/1/10 14:59:53
  • re:Last day at Microsoft
    &quot;永远选择你最感兴趣的项目而不是升职空间等所谓的职业发展前景&quot; -- 说的很好!Good luck!
    by CoderZh(匿名) on 2010/1/8 20:49:26
  • re:Chrome OS和Android的背后
    Google当然不是在传统的操作系统上去跟微软计算,手机操作系统,Windows CE是公认的烂,Google在这里竞争没有什么不可以。Chrome OS更多的是Google云计算战略的一部分,人家根...
    by 啊(匿名) on 2009/12/22 13:14:12

广告

嵌入并动态加载二进制资源及一个bug的解决

这次做的一个project需要对一个XML文件进行解析,为了保证文件的格式是正确的,于是在进行解析(parsing)之前需要先用schema验证一下,大致的代码如下(C++):

   1: try
   2: {
   3:     HRESULT hr;
   4:     
   5:     hr = pProductListXml.CreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
   6:     if (FAILED(hr))
   7:         return hr;
   8:     
   9:     MSXML2::IXMLDOMDocument2Ptr pSchemaDoc;
  10:     hr = pSchemaDoc.CreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
  11:     if (FAILED(hr))
  12:         return hr;
  13:     
  14:     // TODO: initialize the schema doc
  15:  
  16:     MSXML2::IXMLDOMSchemaCollection2Ptr pSchemaColl;
  17:     hr = pSchemaColl.CreateInstance(__uuidof(MSXML2::XMLSchemaCache60), NULL, CLSCTX_INPROC_SERVER);
  18:     if (FAILED(hr))
  19:         return hr;
  20:  
  21:     hr = pSchemaColl->add(L"", pSchemaDoc.GetInterfacePtr());
  22:     if (FAILED(hr))
  23:         return hr;
  24:  
  25:     pProductListXml->schemas = pSchemaColl.GetInterfacePtr();
  26:     pProductListXml->async = VARIANT_FALSE;
  27:     pProductListXml->validateOnParse = VARIANT_FALSE;
  28:     pProductListXml->resolveExternals = VARIANT_FALSE;
  29:  
  30:     if (VARIANT_TRUE != pProductListXml->load(productListFile))
  31:         return HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR);
  32:  
  33:     MSXML2::IXMLDOMParseErrorPtr pError = pProductListXml->validate();
  34:  
  35:     if (S_OK != pError->errorCode)
  36:         return HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR);
  37: }
  38: catch (_com_error e)
  39: {
  40:     return e.Error();
  41: }

看起来有点繁复,主要是因为是用C++写的关系。进行schema验证的也就是:

MSXML2::IXMLDOMParseErrorPtr pError = pProductListXml->validate();

这一句。

在处理如何初始化schema文件的时候,遇到了点问题。因为schema文件本质上也是一个XML文件,所以我打算将schema文件当做一个二进制资源加入到工程中,然后在运行时将其作为一个字符串直接从内存中加载进来,然后调用pSchemaDoc->loadXML就可以了。所以上面的

  14:     // TODO: initialize the schema doc

 

展开实现后就是这样的

   1: _bstr_t schemaXml = GetProductsSchemaXml();
   2:  
   3: if (_bstr_t(L"") == schemaXml || VARIANT_TRUE != pSchemaDoc->loadXML(schemaXml))
   4: {
   5:     return HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR);
   6: }

没问题吧?

最后就是如何实现GetProductsSchemaXml()了,这个帮助函数的代码如下:

   1: _bstr_t GetProductsSchemaXml()
   2: {
   3:     HMODULE hDll = ::GetModuleHandle(NAME_OF_THIS_DLL);
   4:     if (NULL == hDll)
   5:         return _bstr_t(L"");
   6:  
   7:     HRSRC  hResource = ::FindResource(hDll, MAKEINTRESOURCE(IDR_PRODUCTS_SCHEMA), L"BIN");
   8:     if (NULL == hResource)
   9:         return _bstr_t(L"");
  10:  
  11:     HGLOBAL hSchema = ::LoadResource(hDll, hResource);
  12:     if (NULL == hSchema)
  13:         return _bstr_t(L"");
  14:  
  15:     WCHAR* pSchemaXml = (WCHAR*)::LockResource(hSchema);
  16:     if (NULL == pSchemaXml)
  17:         return _bstr_t(L"");
  18:  
  19:     DWORD size = ::SizeofResource(hDll, hResource);
  20:  
  21:     return _bstr_t(CStringW(pSchemaXml, size));
  22: }

嗯,思路是很简单的,取得带有该二进制资源的DLL的句柄,然后从DLL里加载资源,最后将该资源作为一个字符串返回。

XML schema就是一个xsd文件,我们可以通过资源编辑器来将它嵌入到编译成的DLL中:

image

请注意我把资源的类型定名为"BIN”,这个其实是无所谓的,你爱叫什么都可以,只是记得这里的类型要和FindResource的第三个参数一致(参看上面的代码:::FindResource(.., .., L”BIN”) )。

另外请注意因为我的代码全部是Unicode的(仔细的朋友应该注意到了诸如:L”…” 以及 "WCHAR*” 这类明显的Unicode代码的特征),所以products.xsd这个文件也是以Unicode格式存盘的:

image

好了,到此为止工作基本上就完成了。

------------------------------------------------------------------------ 自以为工作已经结束的分割线 ---------------------------------------------------------------------------

但是,在实际测试的时候,pSchemaDoc->loadXML却一直返回VARIANT_FALSE这个失败的结果。怎么回事呢。

开动debugger来检视pSchemaXml(或者hResource,或者hSchema,这三者在这里其实都指向同一个内存地址,当然从语义上它们是不同的)指向的内存区,觉得内容看起来是对的呀:

 

image

调试了很久,一直到打开内存观察器(从菜单的Debug->Windows->Memory那里打开)我才意识到问题所在:

image

大家注意到红色框中的内容了么?也还记得刚才我说因为程序是Unicode的,所以我把products.xsd也以Unicode编码存盘的么?这就是问题所在了。因为UTF-16编码(UTF-16是Unicode的一种)文件的开头都有这个两字节的文件头(2-byte header),主要是用来区分UTF-16和UTF-8的,但就是这个“自动”多加出来的两个字节,使得pSchemaDoc->loadXML无法正确理解字符串(因为完全无法理解开头的FFFE这两个字节),所以当然就失败了。

另外顺便说一句:FFFE这个2 byte header也叫Byte Order Mark,也就是用来标记文件是low-endian的还是big-endian的。FFFE指的是low-endian,而FEFF指的是big-endian,有兴趣的同学可以试试在notepad里制定用Unicode big endian来存盘,然后再用任何十六进制编辑器看看开头的两个字节为何。

事情到这里也有了解决方案了,我只要在实际取得pSchemaXml的时候在LockResource返回的内存地址上再加2个bytes就行了。

可是,最后我发现其实还有更简单的方法,因为各种的混乱,我自己疏忽了。

事实上,你是可以用一个单字节的字符串(也就是ANSI编码的字符串)来初始化一个CStringW的对象的,所以根本就没有必要特意将products.xsd存为Unicode,所以最后我直接将products.xsd以ANSI编码存盘,然后GetProdutsSchemaXml这么写就可以了:

   1: _bstr_t GetProductsSchemaXml()
   2: {
   3:     HMODULE hDll = ::GetModuleHandle(NAME_OF_THIS_DLL);
   4:     if (NULL == hDll)
   5:         return _bstr_t(L"");
   6:  
   7:     HRSRC  hResource = ::FindResource(hDll, MAKEINTRESOURCE(IDR_PRODUCTS_SCHEMA), L"BIN");
   8:     if (NULL == hResource)
   9:         return _bstr_t(L"");
  10:  
  11:     HGLOBAL hSchema = ::LoadResource(hDll, hResource);
  12:     if (NULL == hSchema)
  13:         return _bstr_t(L"");
  14:  
  15:     // the products.xsd resource is stored in ANSI encoding, so we cast the pointer to char* instead of WCHAR*
  16:     char* pSchemaXml = (char*)::LockResource(hSchema);
  17:     if (NULL == pSchemaXml)
  18:         return _bstr_t(L"");
  19:  
  20:     DWORD size = ::SizeofResource(hDll, hResource);
  21:  
  22:     return _bstr_t(CStringW(pSchemaXml, size));
  23: }

 

总之不算什么了不起的软件技巧啦,只是所有这些小东西加起来可能还有点意思,因此写下来和大家分享。

posted on 2009-09-09 16:51:37 by demonfox  评论(0) 阅读(3169)

Powered by: Joycode.MVC引擎 0.5.2.0