HABIT IS POWER

习惯就是力量

0%

读书也需要学习吗?在我认真的考虑这个问题之前,我的答案是不用。

侧方位停车

换过一次驾照,开车已经开了4W多公里了,按理说侧方位停车对我来说应该事件了如指掌的事情,可是知道昨天我才感觉自己真的掌握了其中的“奥妙”。
说起这个事情,需要先说下我之前是怎么侧方位停车的:如果是个标准的侧方位车位,我可以停进去,但都没办法一次到位,入库后也至少需要调整个一两次才能满意;而如果车位稍微短一点(前后车停的不标准),我就得有人帮我看下或在旁边指导。
这个事情一直在发生着,因为我一般不需要侧方位停车 :),但也是最近才经常需要侧方位停车,于是我终于忍受不了这个事情,开始琢磨怎么一次到位。
于是我认真的考虑了这个问题:侧方位停车,然后我向解决一个需求一样分析了这个问题,并google了各种攻略,然后实际测试验证,现在竟然也能一次到位了。

思考

于是我问自己:为什么没有早点重视这个问题?以前凭感觉完成的事情,在“系统的学习”了之后竟然有这么大的提高!
好了,终于要回到我们开始的话题了。
同样的事情发生在读书这个事情上,读书需要学习吗?或者说你真的学会了读书这件事情吗?还是你只是凭着感觉在读书?
我想大部分人和我一样,并没有一个好的读书习惯,每年读的书也就几本或者更少,我认为这样的状态就是凭着感觉在读书,其实没有真正学会读书。
之前看知乎上关于读书的一个问题讨论,国外的小孩小时候就有读书的习惯,外国小孩的“读书”和中国小孩很会读书的“读书”显然不是同一件事情。

和任何一件事情一样,读书也是需要学习的。

认真对待一件事情,你才可能学会他;要想打败你的对手,你需要先尊重你的对手。

最后说句自勉的话:不是我不看书,只是我没找到合适的书,我需要掌握找书的技巧。

需求:

用手机拍下二维码,调用起应用,如果应用没有安装就提示下载应用

解决方案:

手机淘宝就是个现成的例子

1、手机淘宝的原始二维码链接:http://ma.taobao.com/311fY

2、跳转后:http://h5.m.taobao.com/1212/express-pcqr.html?_qr=http%3A%2F%2Fma.taobao.com%2F311fY

3、查看这个页面的代码你能直接发现实现的原理

<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"/> <meta content="yes" name="apple-mobile-web-app-capable"/> <meta content="yes" name="apple-touch-fullscreen"/> <meta content="telephone=no,email=no" name="format-detection"/> <link rel="apple-touch-icon" href="favicon.png"/> <link rel="Shortcut Icon" href="favicon.png" type="image/x-icon"/> <title>手机淘宝购物送刮免单</title> </head> <body><script type="text/javascript">
with(document)with(body)with(insertBefore(createElement("script"),firstChild))setAttribute("exparams","req_url=http%3a%2f%2fh5%2em%2etaobao%2ecom%2f1212%2fexpress%2dpcqr%2ehtml&amp;category=&amp;userid=&amp;b2c_orid=&amp;b2c_auction=&amp;at_isb=&amp;atp_isdpp=&amp;at_ssid=&amp;bbid=&amp;aplus&amp;at_cart=&amp;at_udid=",id="tb-beacon-aplus",src=(location>"https"?"//s":"//a")+".tbcdn.cn/s/aplus_wap.js")
</script>
 <script src="http://g.tbcdn.cn/mtb/lib-smartbanner/0.3.1/function.js"></script> <script src="http://g.tbcdn.cn/mtb/lib-httpurl/1.0.0/httpurl.js"></script> <script>var smartbanner=lib.smartbanner;var HttpUrl=lib.httpurl();var redirect="http://m.taobao.com/go/act/scrapoutfree.html";var param=new HttpUrl(location.href);var ttid=param.search&&param.search.ttid;if(smartbanner.ttidInTaobaoApp()||smartbanner.uaInTaobaoApp()){if(ttid){redirect+="?ttid=";redirect+=ttid}location.href=redirect}else{redirect="taobaowebview://m.taobao.com/?weburl="+encodeURIComponent(redirect);var sbInstance=smartbanner.getInstance({href:redirect,url:"http://m.taobao.com/go/act/other/1212pcwuliuerweima.html",crossplat:true});sbInstance&&sbInstance.install()};</script> </body> </html>

**核心的JS文件是:http://g.tbcdn.cn/mtb/lib-smartbanner/0.3.1/function.js **

!function(a,b){function c(){var a={},b=location.search.slice(1).split("&");if(b[0].length)for(var c=0;c<b.length;c++){var d=b[c].split("=");a[d[0]]=d[1]}return a}function d(a){a&&a.apuri&&(a.ap_uri=a.apuri,delete a.apuri);var b=a||{},c=g.getElementById("buried");ttid=c&&c.value,href=location.href.toString(),ttid&&-1==href.indexOf("ttid")&&(href.indexOf("?")>-1?href=href.replace("?","?ttid="+ttid):href.indexOf("#")>-1?href=href.replace("#","?ttid="+ttid+"#"):href+="?ttid="+ttid),b.ap_ref=encodeURIComponent(href),h&&h.length>1&&(b.ap_cna=h[1]);var d=[];for(var e in b)d.push(e+"="+encodeURIComponent(b[e]));var f=g.createElement("script");f.src="http://log.m.taobao.com/js.do?"+d.join("&").replace(/%20/g,"+"),g.body.appendChild(f)}function e(){var a=c(),b=a.ttid,d=/.+@taobao_(iphone|android|apad|ipad)_.+/i;return b=b?decodeURIComponent(b):"",d.test(b)}function f(){var a=navigator.userAgent;return null!=a.match(/WindVane/i)?!0:!1}var g=a.document,h=g.cookie.match(/(?:^|\s)cna=([^;]+)(?:;|$)/),i=g.createElement("frame"),j=function(a){var b=this,c=navigator.standalone,d=navigator.userAgent;return null!=d.match(/iPhone|iPod|iPad/i)?(this.platform="ios",this.isIpad=null!=d.match(/iPad/i)):null!=d.match(/Android/i)?null!=d.match(/Mobile/i)&&(this.platform="android",this.isChrome=null!=d.match(/Chrome/i)&&null==d.match(/Version\/\d+\.\d+(\.\d+)?\sChrome\//i)):null!=d.match(/Linux/i)&&(this.platform="android"),!this.platform||c?(this.invaliable=!0,null):(this.init(a)&&(this.create(),window.onblur=function(){clearTimeout(b.timeload),b.timeload=null}),this)};j.prototype={constructor:j,init:function(a){var b=this.options=a,d=b.isInstance||function(){return f()||e()};if(d())return this.invaliable=!0,null;a.version||(a.version="v1"),this.cover=b.cover||!1,this.isDownload=b.download||!1,this.timeout=b.timeout||600;var h=b.from||"h5",i=b.crossplat||!1;if(this.bannerUrl="ios"!=this.platform||i?b.url||"http://m.taobao.com/channel/act/sale/tbdl1.html":b.appstoreUrl||(this.isIpad?"https://itunes.apple.com/app/id438865278":"http://itunes.apple.com/cn/app/id387682726?mt=8"),b.href){var j=b.href,k=c(),l=g.getElementById("buried"),m=k.ttid||l&&l.value,n=k.sprefer,o=k.ali_trackid,p=location.href;p=-1!=p.indexOf("?")?p.split("?")[0]:p,p=-1!=p.indexOf("#")?p.split("#")[0]:p;var q={from:h,url:encodeURIComponent(p)},r=b.point;if(r)for(var s in r)q[s]=r[s];m&&(q.ttid=m),n&&(q.sprefer=n),o&&(q.ali_trackid=o),q.ap_uri="",q=encodeURIComponent(JSON.stringify(q));var t,u=j.indexOf("#");if(-1!=u&&(t=j.slice(u),j=j.replace(t,"")),-1==j.indexOf("?")?j+="?":j.indexOf("?")!=j.length-1&&(j+="&"),j+="point="+q+(t?t:""),j=-1!=j.indexOf("://")?j:"taobao://"+j,this.isChrome){var v=j.split("://"),w=v[0],x=v[1],y=b.package||"com.taobao.taobao";j="intent://"+x+"#Intent;scheme="+w+";package="+y+";end"}this.paramUrl=j}return!0},reset:function(a){this.iClose||(this.init(a),this.resetHtml&&this.resetHtml(a))},create:function(){this.iClose||(i.parentNode||(i.setAttribute("id","J_smartFrame"),i.style.cssText="display:none",g.body.appendChild(i)),this.frame=i)},download:function(b){var c=Date.now();(!b||c-b<this.timeout+200)&&(this.cover?a.location.replace(this.bannerUrl):a.location.href=this.bannerUrl)},redirect:function(b){var c=this.options&&this.options.version,e=this.frame,f=b?"click_sb_"+c+"_manual":"click_sb_"+c+"_auto";this.paramUrl&&(d({ap_uri:f}),this.paramUrl=this.paramUrl.replace("%22ap_uri%22%3A%22%22",encodeURIComponent('"ap_uri":"'+f+'"')),this.isChrome?a.location.href=this.paramUrl:e&&e.setAttribute("src",this.paramUrl))},install:function(a){var b=this,c=Date.now();b.isDownload||(b.timeload=setTimeout(function(){b.download(c)},b.timeout)),b.redirect(a)}},b.smartbanner=function(a){var c=a.type,d=b.smartbanner.BannerUI,e=b.smartbanner.PopUI;if("banner"!==c&&c){if("pop"===c){if(e)return new e(a)}else if("func"===c)return b.smartbanner.getInstance(a)}else if(d)return new d(a)},b.smartbanner.getInstance=function(a,b){b||(b=Object.create({}));for(var c in j.prototype)b[c]=j.prototype[c];return j.call(b,a)},b.smartbanner.aplus=d,b.smartbanner.getParam=c,b.smartbanner.ttidInTaobaoApp=e,b.smartbanner.uaInTaobaoApp=f}(window,window.lib||(window.lib={}));

看了下,github上有这个库:
jquery.smartbanner

有现成的方案可以参考真好 :)

这不是一篇教程,而是一篇索引,带我自己(也希望能带上你)走上iOS开发之路。

问题:

假设你有一定的编程基础,希望开始自己的iOS开发之旅,
那么你可能发现目前(2014/1/15)developer.apple.com上的最新iOS SDK对应的是iOS7,Xcode 5,而书店、网上能找到的教程大部分都是iOS 5, Xcode 4,

教程和Xcode版本不匹配(操作系统、SDK、开发工具其实都升级了),学习起来磕磕绊绊

两种解决方案:
方案1.其实最简单的方法就是下载教程对应的Xcode版本,然后跟着教程中的例子做就好了【这么简单的方案,本文就不打算讨论了】。

方案2.如果你像我一样非要用最新的Xcode,又想看老的教程书籍入门iOS开发,那该怎么办?(旁白:偏执狂、脑袋被门夹过……)
那么希望我自己的这点小总结能对你有些许帮助 :)

方案二细化

1.先看Apple官方提供的入门例子(这个例子一定是最新的,不用担心iOS SDK或者Xcode版本的问题),对iOS开发一个初步认识

马上着手开发 iOS 应用程序 (Start Developing iOS Apps Today)

2.Xcode 5之后的Single View Application 工程模板使用的Storyboard,但是目前(2014/1/15) 市面上的很多iOS开发教程都是Xcode4时代出版或发表的,那么掌握下面这个技能,能帮助你“无缝对接” 大部分Xcode4的iOS教程:

给Xcode5下Empty Application模板创建的工程添加Interface Builder

之前参考的这篇文章链接失效了,需要的话可以搜索下标题还是能找到其他的转载:

使用 Xcode 5 和 Interface Builder创建 Hello World App

3.找一本iOS 5的入门教程,动手写代码 :)

这里推荐一本不错的入门教程:

《iOS编程》英文版书名:《iOS Programming: The Big Nerd Ranch Guide (3rd Edition)》

这边书有80页试读的PDF可以下载来先看看,觉得好的话再买下(google 《iOS编程(第3版)》80页抢鲜试读就能找到试读本的下载)

4.关于Storyboard的使用可以看下这个教程:

iOS 7教程-Storyboards Part1

Storyboards Tutorial in iOS 7: Part 2

未完待续……

文件夹“安全”提示:请求的安全信息不可用或无法显示
The requested security information is either unavailable or cannot be displayed

问题:

用小米盒子迅雷远程下载,文件没有下载完就被我强制拔下了移动硬盘,然后我的移动硬盘上TDDownloader目录下就出现了一堆的“僵尸文件”,打不开,删除掉,恶心死了

问题分析

右键文件的属性,查看文件夹的“安全”属性页,提示:

请求的安全信息不可用或无法显示

尝试过的方法包括以下这些,但不止这些个方法,都不行:
暴力删除无法删除的文件

WIN7 文件夹“安全”提示:请求的安全信息不可用或无法显示

问题解决

在Mac下直接删除目录,直接成功!

我在Mac下挂载移动硬盘发现那些文件都看不见了,TDDownloader下是空的,于是尝试直接Command+delete删除了TDDownload目录;

回到Windows下插上移动硬盘:世界清静了

#强迫症是个大问题

Offline Install .Net 3.5 With Win8.1 ISO

问题:

Windows 8.1下安装VS2008,提示缺少.NET 3.5,但是.NET 3.5在Win8下属于系统自带功能,无法直接使用离线安装包安装,只能通过“启用或关闭Windows 功能”来打开

控制面板 -> 程序和功能 -> 启用或关闭Windows 功能

但是坑爹的是“启用或关闭Windows 功能”竟然是在线去下载,我这网速下200多MB的安装包都不知道要几天

解决方案:

思路:

还好发现Win8下可以使用系统安装包ISO中的安装源来安装,直接用ISO来安装就不用等待下载了

操作步骤:

1、直接右键装载Win8.1系统安装盘ISO(我这里装载后的盘符是E:)

2、从系统Charm菜单上选择搜索,输入CMD,然后右键选择“以管理员权限打开”

3、命令行下输入(注意,E:来自于第一步装载ISO后的盘符):

dism.exe /online /enable-feature /featurename:NetFX3 /Source:E:\sources\sxs

4、成功后提示

启用一个或多个功能
[=========================100%=========================]
操作成功完成。

5、去“启用或关闭Windows 功能”确认下”.NET Framework 3.5(包括.NET 2.0 和 3.0)”前面的CheckBox已经勾选,成功 :)

控制面板 -> 程序和功能 -> 启用或关闭Windows 功能

PS.

我的安装VS2008失败的问题也同时解决了,重新安装一次,成功 :)

重现步骤:

  • 0、开发环境:Windows 7 SP1 64bit, VS2008 SP1
  • 1、进程中修改了stdout这个句柄的值:调用了prinft和cout都会修改stdout,TRACE不会修改stdout
  • 2、使用curl请求一个会跳转的url(HTTP 跳转的话curl会收到包含302的response header)
  • 3、crash

问题影响

  • curl版本7.21.7和7.28.1都有这个问题

问题原因和解决方案

很可能是curl的一个bug,目前没有找到完美的解决方案,只能在进程中不要使用printf和cout

代码

如果你调用了printf或者cout,你会发现在curl初始化的时候,调用了这个代码,这个时候的stdout已经被修改了

// code block from url.c
/*
 * Initialize the UserDefined fields within a SessionHandle.
 * This may be safely called on a new or existing SessionHandle.
 */
CURLcode Curl_init_userdefined(struct UserDefined *set)
{
  CURLcode res = CURLE_OK;

  set->out = stdout; /* default output to stdout */ // linyehui:这个set.out有问题
  set->in  = stdin;  /* default input from stdin */
  set->err  = stderr;  /* default stderr to stderr */

实际crash的地方:

// code block from sendf.c

/* Curl_client_write() sends data to the write callback(s)

   The bit pattern defines to what "streams" to write to. Body and/or header.
   The defines are in sendf.h of course.

   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
   local character encoding.  This is a problem and should be changed in
   the future to leave the original data alone.
 */
CURLcode Curl_client_write(struct connectdata *conn,
                           int type,
                           char *ptr,
                           size_t len)
{
    // ...  
    /* If the previous block of data ended with CR and this block of data is
       just a NL, then the length might be zero */
    if(len) {
      wrote = data->set.fwrite_func(ptr, 1, len, data->set.out); // linyehui:这个set.out有问题
    }
    else {
      wrote = len;
    }

// ...

呵呵……

很早之前我写过一篇Blog:网页通过External接口与WebBrowser交互,文中的交互其实只介绍了JS调用C++的部分,而C++调用JS由于微软自己的例子太多,那篇文章就没介绍,不过我最近遇到了一个新问题,和C++调用JS有关,所以重新梳理了下这块的逻辑,把之前的代码完善了下。

我遇到的问题:

内嵌IE浏览器控件WebBrowser的内嵌页host.html中使用iframe又嵌套了一个页面iframe.html,iframe.html上有个JS方法,我用C++调用不到,而host.html上的JS方法可以正常调用到。

问题分析:

从JS来说,这是个跨域问题,host.html和iframe.html不在一个域内;
JS解决跨域问题的方案其实也是有的,但这个不是我们本文的重点,本文的重点是怎么通过WebBrowser控件直接来解决这个“跨域调用”的问题。

问题的根本原因在于:

调用网页的JS需要拿到IHTMLDocument2接口,而每个iframe都有自己对应的IHTMLDocument2,所以我们只要能拿到iframe对应的IHTMLDocument2就能解决问题了。

解决方案:直接上代码吧

/* -------------------------------------------------------------------------
//	FileName	:	calljs_helper.h
//	Creator		:	linyehui
//	Date		:	2013/11/16 01:18:09
//	Brief		:	调用WebBrowser控件内嵌页上的JS函数,iframe中的也能调到
//
//	$Id: $
// -----------------------------------------------------------------------*/
#ifndef __CALLJS_HELPER_H__
#define __CALLJS_HELPER_H__

// -------------------------------------------------------------------------
namespace calljs_helper
{
    bool CallFunction(
        CComPtr<IWebBrowser2> spIWebBrowser, 
        LPCTSTR lpFuncName,
        const vector<wstring>& paramArray, 
        CComVariant * pVarResult = NULL,
        bool bEnumFrame = true);

} // namespace

// -------------------------------------------------------------------------
// $Log: $

#endif /* __CALLJS_HELPER_H__ */



/* -------------------------------------------------------------------------
//	FileName	:	calljs_helper.cpp
//	Creator		:	linyehui
//	Date		:	2013/11/16 01:18:14
//	Brief		:	调用WebBrowser控件内嵌页上的JS函数,iframe中的也能调到
//
//	$Id: $
// -----------------------------------------------------------------------*/

#include "stdafx.h"
#include "calljs_helper.h"

// -------------------------------------------------------------------------

CComPtr<IWebBrowser2> HtmlWindowToHtmlWebBrowser(CComPtr<IHTMLWindow2> spWindow)
{
    ATLASSERT(spWindow != NULL);
    CComQIPtr<IServiceProvider>  spServiceProvider = spWindow;
    if (spServiceProvider == NULL)
    {
        return CComPtr<IWebBrowser2>();
    }

    CComPtr<IWebBrowser2> spWebBrws;
    HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws);
    if (hRes != S_OK)
    {
        return CComPtr<IWebBrowser2>();
    }

    return spWebBrws;
}

// Converts a IHTMLWindow2 object to a IHTMLDocument2. Returns NULL in case of failure.
// It takes into account accessing the DOM across frames loaded from different domains.
CComPtr<IHTMLDocument2> HtmlWindowToHtmlDocument(CComPtr<IHTMLWindow2> spWindow)
{
    ATLASSERT(spWindow != NULL);
    CComPtr<IHTMLDocument2> spDocument;
    HRESULT hRes = spWindow->get_document(&spDocument);
    if ((S_OK == hRes) && (spDocument != NULL))
    {
        // The html document was properly retrieved.
        return spDocument;
    }

    // hRes could be E_ACCESSDENIED that means a security restriction that
    // prevents scripting across frames that loads documents from different internet domains.
    CComPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(spWindow);
    if (spBrws == NULL)
    {
        return CComPtr<IHTMLDocument2>();
    }

    // Get the document object from the IWebBrowser2 object.
    CComPtr<IDispatch> spDisp;
    hRes = spBrws->get_Document(&spDisp);
    spDocument = spDisp;
    return spDocument;
}

bool CallFunctionInDocument(
                            CComPtr<IHTMLDocument2> spDocument2, 
                            LPCTSTR lpFuncName,
                            const vector<wstring>& paramArray, 
                            CComVariant * pVarResult)
{
    if (!spDocument2)
        return false;

    CComPtr<IDispatch> spScript; 
    if (FAILED(spDocument2->get_Script(&spScript))) { return false; }

    CComBSTR bstrMember(lpFuncName); 
    DISPID dispid = NULL; 
    HRESULT hr = spScript->GetIDsOfNames(IID_NULL, &bstrMember, 1, LOCALE_SYSTEM_DEFAULT, &dispid); 
    if (FAILED(hr)) { return false; }

    const int arraySize = paramArray.size(); 

    DISPPARAMS dispparams; 
    memset(&dispparams, 0, sizeof dispparams); 
    dispparams.cArgs = arraySize; 
    dispparams.rgvarg = new VARIANT[dispparams.cArgs]; 

    for (int i = 0; i < arraySize; i++)
    {
        CComBSTR bstr = paramArray[arraySize - 1 - i].c_str(); // back reading
        bstr.CopyTo(&dispparams.rgvarg[i].bstrVal); 
        dispparams.rgvarg[i].vt = VT_BSTR; 
    }
    dispparams.cNamedArgs = 0; 

    EXCEPINFO excepInfo; 
    memset(&excepInfo, 0, sizeof excepInfo); 
    CComVariant vaResult; 
    UINT nArgErr = (UINT)-1;  // initialize to invalid arg

    hr = spScript->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr); 

    delete [] dispparams.rgvarg; 
    if (FAILED(hr)) { return false; }

    if (pVarResult) { *pVarResult = vaResult; }

    return true;
}

void EnumFrame(
                              CComPtr<IHTMLDocument2> spIHTMLDocument2, 
                              LPCTSTR lpFuncName,const vector<wstring>& paramArray, 
                              CComVariant* pVarResult)
{
    if ( !spIHTMLDocument2 )
        return;

    CComPtr< IHTMLFramesCollection2 > spFramesCollection2;
    spIHTMLDocument2->get_frames( &spFramesCollection2 );

    long nFrameCount=0;
    HRESULT hr = spFramesCollection2->get_length( &nFrameCount );
    if ( FAILED ( hr ) || 0 == nFrameCount )
        return;

    for(long i = 0; i < nFrameCount; i++)
    {
        CComVariant vDispWin2;
        hr = spFramesCollection2->item( &CComVariant(i), &vDispWin2 );
        if ( FAILED ( hr ) ) 
            continue;

        CComQIPtr< IHTMLWindow2 > spWin2 = vDispWin2.pdispVal;
        if( !spWin2 )
            continue;

        CComPtr < IHTMLDocument2 > spDoc2;
        spDoc2 = HtmlWindowToHtmlDocument(spWin2);
        if (!spDoc2)
            continue;

        CallFunctionInDocument(spDoc2, lpFuncName, paramArray, pVarResult);
    }
}

bool calljs_helper::CallFunction(
                  CComPtr<IWebBrowser2> spIWebBrowser, 
                  LPCTSTR lpFuncName,
                  const vector<wstring>& paramArray, 
                  CComVariant * pVarResult,
                  bool bEnumFrame)
{
    if (!spIWebBrowser) 
        return false;

    CComPtr<IDispatch> spDispDoc; 
    HRESULT hr = spIWebBrowser->get_Document(&spDispDoc); 
    if (FAILED(hr))
        return false;

    CComQIPtr<IHTMLDocument2> spDocument2 = spDispDoc; 
    if (!spDocument2)
        return false;

    CallFunctionInDocument(spDocument2, lpFuncName, paramArray, pVarResult);
    
    if (bEnumFrame)
    {
        EnumFrame(spDocument2, lpFuncName, paramArray, pVarResult);
    }
    
    return true; 
}

// -------------------------------------------------------------------------
// $Log: $

完整的例子代码在附件中。

另外再附上帮助我解决问题的参考资料:

IHTMLWindow2的get_document方法有时候会返回E_ACCESSDENIED

VC++实现浏览器自动填表

webbrowser控件的IHTMLDocument3 or2接口的iframe问题(非安全设置)

window.name实现的跨域数据传输

JavaScript跨域总结与解决办法

The End.

在我开始接触之前,有个很早的项目,使用的是DuiEngine一开始的开源代码,具体时间和版本已经找不清楚了,然后呢现在我需要把他升级到最新版本的DuiEngine,在从老的库升级到新版本时遇到了下面这些问题。

DuiEngine版本

http://duiengine.googlecode.com/svn/trunk
Revision: 246
Author: setoutsoft@qq.com
Date: 2013年8月26日,星期一 14:23:51
Message:
修改窗口默认属性的实现,删除原来的baseClass

升级过程中遇到的问题及解决方案

1.skin xml描述类的变化

1.1 原来winres.h中的资源ID是手动一个个添加的,新版本的DuiEngine已经可以用工具自动生成

解决方案:

删除之前生成的winrex.h和name2id.xml,用命令行重新生成,其实就是给每个资源在xml中添加一个name属性,体力活,耐心改下,有个简单的方案是把你之前在winres.h中定义的ID作为xml中的name属性,比如:IDC_TXT_TITLE

..\3rdparty\duiengine\tool\residbuilder -y -p skin -i skin\index.xml -r .\duires\winres.rc2 -n .\duires\name2id.xml -h .\duires\winres.h

1.2 模板生成的索引xml文件名字从idmap.xml改成了index.xml,这个只是名字的区别,DuiEngine的Demo目录结构还是老的,没有影响

1.3 resid type=”PNG”被resid type=”IMGX”替代,直接查找替换即可

1.4 header=“1”属性被去掉了,改成新的标签了,具体使用方法可以在DuiEngine Demo中搜索
解决方案:删除header=”1”属性,改成用

1.5 如果对应的xml文件是布局类型的xml,那么记得在index.xml中加个属性layer=”1”

1
<resid type="XML"  id_name="IDR_MAIN_DIALOG" id="103" layer="1" file="xml\dlg_main.xml"  />

1.6 duiedit改名成了edit,需要在xml添加几个配置,照着Demo工程COPY就好了

1.7 imgframe替换了原来的imghorzex,png等skin类型

2.编译中对ATL的依赖

我个人比较喜欢WTL,但是新版本中用WTL.mini进行了替换,好在可以用USING_ATL预编译宏进行开关选择继续使用WTL,使用的时候DuiEngine和使用的工程都需要使用这个预编译宏,否则会出现link error

最后我发现了问题的所在,是一个编译选项配错了,改成默认的就可以了

C/C++->Language->Treat wchar_t as Built-in Type

改成默认的Yes就可以link成功,编译通过了,我不知道是不是我自己不小心改出来的,不过好像google code取下来的代码就是有这个问题的

小结

昨天下午开始做这个事情,工程我已经迁移完成了,改动的过程比较痛苦,但DuiEngine整个UI库的设计理念没有太大的变化,所以都是些细节,作者也比较懒,没有一个升级的指引,源代码就是文档 :(

新版本还是改了很多Bug的,我比较奇怪的是作者为什么要把WTL给去掉呢?自己实现的SimpleWnd功能还不全,合并呢?

我本来可以沿用老版本的DuiEngine,但是本着开源代码就是要用最新的原则,我还是进行了版本升级,过程比现象中的要纠结,但时间比想象中花的少。

Thanks DuiEngine, gl hf :)

前言

假设你清楚的理解什么是WTL,什么是DuiEngine,那么请继续往下看 :)

在WTL向导生成的工程中使用DuiEngine

1. 用WTL向导生成一个Module Dialog的工程:WTLDemo

2. 用DuiEngine向导生成一个最简单的工程:DuiDemo

3. 从DuiDemo目录下复制这些目录和文件到WTLDemo目录下

1
2
3
4
duires目录
skin目录
MainDlg.h和MainDlg.cpp
UIHandler.h和UIHandler.cpp

4. 修改.rc文件,最方便的使用BeyondCompare等工具直接比较合并,其实需要合并过来的就是这三段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
3 TEXTINCLUDE 
BEGIN
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)\r\n"
"LANGUAGE 4, 2\r\n"
"#pragma code_page(936)\r\n"
"#include ""duires\\winres.rc2"" //DUI×ÊÔ´\r\n"
"#endi\0"
END



/////////////////////////////////////////////////////////////////////////////
//
// XML2
//

IDR_NAME2ID XML2 "duires\\name2id.xml"

/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_MAINWND ICON "duires\\WTLDemo.ico"



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE 4, 2
#pragma code_page(936)
#include "duires\winres.rc2" //DUI×ÊÔ´
#endif
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

** resource.h中新增两行 **

1
2
#define IDR_NAME2ID                     112
#define IDI_MAINWND 113

** stdafx.h中加入duiengine的头文件和lib **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// dui include
#include <duistd.h>
#include <duihostwnd.h>
#include <duictrls.h>
#include <duiwndnotify.h>
#include <DuiCSS.h>

#include "resource.h"
#include "duires/res.h"

using namespace DuiEngine;

#ifdef DLL_DUI
# pragma comment(lib, "duiengine.lib")
#else
# pragma comment(lib, "duiengine_static.lib")
#endif
  • 5.WTLDemo.cpp中新增DuiEngine的初始化和销毁,以及调用逻辑,直接贴下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "stdafx.h"

#include "resource.h"

#include "MainDlg.h"
#include "DuiSystem.h"
#include "DuiDefaultLogger.h"

CAppModule _Module;

//////////////////////////////////////////////////////////////////////////

int RunUI()
{
int nRet = 0;
CMainDlg dlgMain;
nRet = dlgMain.DoModal();

return nRet;
}

int RunDuiEngine(HRESULT hRes, HINSTANCE hInstance)
{
DUIASSERT(SUCCEEDED(hRes));

TCHAR szCurrentDir[MAX_PATH + 1] = {0};
memset( szCurrentDir, 0, sizeof(szCurrentDir) );
GetModuleFileName( NULL, szCurrentDir, MAX_PATH );
LPTSTR lpInsertPos = _tcsrchr( szCurrentDir, _T('\\') );
*lpInsertPos = _T('\0');

DuiSystem duiSystem(hInstance);

DefaultLogger loger;
loger.setLogFilename(CDuiStringT(szCurrentDir) + _T("\\duiengine.log"));
duiSystem.SetLogger(&loger);
duiSystem.logEvent(_T("demo started"));

// 加载ID名称对照表,该资源属于APP级别,所有皮肤应该共享该名字表,名字表总是从程序资源加载
duiSystem.InitName2ID(IDR_NAME2ID,_T("XML2"));

#ifdef __DUIFILE_RC
//从文件夹加载皮肤,指定皮肤位置
_tcscat( szCurrentDir, _T("\\..\\..\\..\\..\\coding\\demo\\WTLDemo\\skin") );
DuiResProviderFiles *pResFiles=new DuiResProviderFiles;
if(!pResFiles->Init(szCurrentDir))
{
DUIASSERT(0);
return 1;
}
duiSystem.SetResProvider(pResFiles);
#else
//从资源加载皮肤
duiSystem.SetResProvider(new DuiResProviderPE(hInstance));
#endif

//初始化DUI系统,原来的系统初始化方式依然可以使用。
BOOL result = duiSystem.Init(IDR_DUI_INIT);
duiSystem.SetMsgBoxTemplate(IDR_DUI_MSGBOX);

int uiResult = 0;
if (result)
{
uiResult = RunUI();
}

duiSystem.logEvent(_T("demo end"));
delete duiSystem.GetResProvider();

return uiResult;
}


int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpstrCmdLine*/, int /*nCmdShow*/)
{
HRESULT hRes = ::CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call instead to
// make the EXE free threaded. This means that calls come in on a random RPC thread.
// HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
ATLASSERT(SUCCEEDED(hRes));

// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
::DefWindowProc(NULL, 0, 0, 0L);

AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls

hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));

AtlAxWinInit();

int nRet = RunDuiEngine(hRes, hInstance);

_Module.Term();
::CoUninitialize();

return nRet;
}
  • 6.配置Pre Build Event,直接从DuiDemo复制过来就可以了,就是调用下residbuilder

上周的Build 2013上微软发布了Windows 8.1 Preview,微软对外公布了8.1的更新信息:

New or updated in Windows 8.1 Preview

我自己看了之后比较感兴趣的点有下面这些:

1. General updates

App bundle support

资源包,方便做多语言的支持,比现在散落在目录下规范很多

2. Diagnostics

Mixed-language debugging

终于可以同时调试Javascript和C++了,只能说现在的版本太搓了

Managed app debugging

动态修改变量的值,实用功能

Asynchronous debugging improvements

task任务的堆栈可以追溯到创建的地方,这个调试异步任务时非常实用

DOM Explorer

得到了加强

JavaScript Console

不用自己console.dir了

JavaScript Memory Profiler

现在的发现点还不够智能,需要验证下好不好用才知道

JavaScript UI Responsiveness

实用

3. JavaScript editor

小改动都比较实用

4. C++ development

C++ REST SDK

我们自己用不到,但是这也算是当下必备的基础库了

IDE productivity features

优化了编译速度,这个比较期待

Build-time performance enhancements

优化了并行编译,期待

5. HTML design tools

使用的优化,对开发影响不大

6. XAML design tools

使用的优化,对开发影响不大

7. Windows App Certification Kit 3.0

连这个小工具都做了并行优化,挺用心的嘛

New validation tests

File extensions and protocols

数量在规则上并没有什么变化,只是加了检查,网盘类应用应该比较关心这个

JavaScript background tasks

考虑省电的缘故,后台不给常驻了,但如果应用配合服务端的话,“常驻”比较难被检查出来吧?不知道微软是怎么检查的

Test reports

之前遇到过一个莫名其妙的启动性能Failed,给的report detail根本就看不出是什么原因导致的,希望这个版本能够有所改进。

8. 开发建议

从之前微软发布Windows 8 预览版的节操来看,Preview版本和RTM版本还是会有比较大的差异,所以开发者不要着急着全面跟进8.1的新特性;
先把这些特性都研究下,改改兼容性的Bug,能做到8.1正式发布后第一时间有新版本跟进也就可以了。

Good luck, have fun :)