精华内容
下载资源
问答
  • 1.使用粘滞通知通知是可见的,直到关闭/阅读) 2.使用默认行为(通知被自动解除) 安装扩展后,第一个选项将自动激活,这将覆盖默认行为。 这可以配置到首选项,看截图(点击用户 - >首选项 - >通知 - >可见性...
  • 而且这个传递步骤是颠倒的,例如我们关闭了某个程序,我们可能会认为是程序自己关闭通知系统的,其实不然,当用户点击关闭按钮的时候,Windows就会把一个叫做WM_CLOSE的消息传递给这个程序,程序接收到消息后就...
    一. 谁误导了浏览器
      今天是大年初二,王先生家中来了许多客人,把平时埋头于工作的王先生弄了个手忙脚乱,由于客人带来的几个小孩子嚷嚷着要出去上网,王先生只好把寝室里的电脑让给了这一群孩子玩,好容易到了晚上,客人散尽,王先生想在休息前上网浏览一下新闻,可是当他打开IE的时候,却发现它自动连接到一个莫名其妙的网站去了,而且收藏夹里也多了一些奇怪的网址,王先生担心是系统感染了病毒,赶紧输入一个在线杀毒工具的网址,结果IE打开的却是另一个不知所谓的网站。看着IE地址栏里准确无误的杀毒工具网址和下面那根本扯不上关系的内容,王先生真的手忙脚乱了……
      相信不少用户也遇到过与之类似的奇怪事情,究竟是谁,把我们的浏览器领到了一处陌生的地方呢?
    近年来,针对浏览器的攻击手段层出不穷,对浏览器的渗透攻击逐渐成为入侵者攻破用户层层防御的首要目标,在“钓鱼”(Publishing)危机尚未解除的时候,另一种攻击方式也在同时进行着,这就是“浏览器劫持”——故意误导浏览器行进路线的策划者。
      “浏览器劫持”(Browser Hijack)是一种不同于普通病毒木马感染途径的网络攻击手段,它的渗透途径很多,目前最常见的方式有通过BHO、DLL插件、Hook技术、Winsock LSP等载体达到对用户的浏览器进行篡改的目的。这些载体可以直接寄生于浏览器的模块里,成为浏览器的一部分,进而直接操纵浏览器的行为,轻者把用户带到自家门户网站,严重的则会在用户计算机中收集敏感信息,危及用户隐私安全。“浏览器劫持”的后果非常严重,用户只有在受到劫持后才会发现异常情况,但是这时候已经太迟了。目前,浏览器劫持已经成为Internet用户最大的威胁之一。


    二. BHO,你是助手还是敌人?
      为什么“浏览器劫持”能够如此猖狂呢?放眼众多论坛的求助贴,我们不时可以看到诸如“我的IE被主页被改了,我用杀毒工具扫了一遍都没发现病毒,我把主页改回自己的地址,可是一重启它又回来了!”、“我的系统一开机就跳出一个广告,我明明用了最新版的杀毒软件的啊!”等这类关于IE异常问题的求助,80%的提问者都表示纳闷,他们已经安装了杀毒软件,可是IE仍然被“黑”了,这又是为什么?
      其实这些都是典型的“浏览器劫持”现象,但是受害者不是已经安装了杀毒软件吗?为什么浏览器依然躲不过这只黑手?许多用户对这个领域都存在一种误区心理:浏览器劫持?我有最新的杀毒软件,我不怕!
    于是,当他们遭遇“浏览器劫持”时,惊讶了。
      要知道,杀毒软件自身也只是一种辅助工具,它不可能完全保护系统的安全,更何况,杀毒软件用户必须知道一个事实:“浏览器劫持”的攻击手段是可以通过被系统认可的“合法途径”来进行的!杀毒软件只能通过“特征码”的形式来判断程序是否合法,但这是建立在人为定义以后的,而实施“浏览器劫持”的程序可以有很多,防不胜防。
      为什么说“浏览器劫持”可以说是合法的呢?因为大部分浏览器劫持的发起者,都是通过一种被称为“BHO”(Browser Helper Object,浏览器辅助对象)的技术手段植入系统的。
      BHO是微软早在1999年推出的作为浏览器对第三方程序员开放交互接口的业界标准,它是一种可以让程序员使用简单代码进入浏览器领域的“交互接口”(INTERACTIVED Interface)。通过BHO接口,第三方程序员可以自己编写代码获取浏览器的一些行为(Action)和事件通知(Event),如“后退”、“前进”、“当前页面”等,甚至可以获取浏览器的各个组件信息,像菜单、工具栏、坐标等。由于BHO的交互特性,程序员还可以使用代码去控制浏览器的行为,比如常见的修改替换浏览器工具栏、在浏览器界面上添加自己的程序按钮等操作,而这些操作都被视为“合法”的,这就是一切罪恶根源的开始。
      BHO的出现帮助程序员更好的打造个性化浏览器或者为自己的程序实现了方便简洁的交互功能,可以说,如果没有BHO接口的诞生,我们今天就不能用一些工具实现个性化IE的功能了。从某一方面来看,BHO的确是各种缤纷网络互动功能的幕后功臣,但是一切事物都是有两面性的,这个恒古不变的真理同样对BHO有效,于是就有了今天让安全界头痛的“浏览器劫持”的攻击手段诞生。
      看看前面我提到的BHO接口特性,你想到了什么?BHO可以获知和实现浏览器的大部分事件和功能,也就是说,它可以利用少量的代码控制浏览器行为。程序员可以设计出一个BHO按钮以实现用户点击时通知浏览器跳转到某个页面完成交互功能,当然就可以进一步写出控制浏览器跳转到他想让用户去的页面,这就是最初的“浏览器劫持”的成因:BHO劫持。
      在描述BHO劫持之前,我们先要对BHO接口的启动做个简单介绍:符合BHO接口标准的程序代码被写为DLL动态链接库形式在注册表里注册为COM对象,还要在BHO接口的注册表入口处进行组件注册,以后每次IE启动时都会通过这里描述的注册信息调用加载这个DLL文件,而这个DLL文件就因此成为IE的一个模块(BHO组件),与IE共享一个运行周期,直到IE被关闭。
      IE启动时,会加载任何BHO组件,这些组件直接进入IE领域,而IE则成为它们的父进程和载体,从此IE的每一个事件都会通过IUnknown接口传递到BHO用以提供交互的IObjectWithSite接口里,这是BHO实现与IE交互的入口函数。
      BHO接收到IE接口传递来的参数后开始判断IE正在做什么,理论上BHO可以获取IE的大部分事件,然后根据程序员编写的代码,BHO持有对特定事件做出反应的决定权,例如一个可以实现“中文网址”的BHO,就是通过GetSite方法获取到IE当前打开的站点URL(或通过IURLSearchHook接口来获知),如果BHO发现获取到的URL和内置的判断条件匹配,该BHO就会启用SetSite方法强制IE跳转到程序员设定的页面去,这个过程就是利用about:blank篡改主页的“浏览器劫持”方法之一,它的实现原理其实很简单,程序员编写一个恶意BHO组件,当它获取到IE窗口的当前站点为“about:blank”时就强制IE内部跳转到指定的广告页面,于是闹出了不久之前沸沸扬扬的“IE空白页劫持事件”。
      了解了这种类似恶作剧的作案手段,要解决它就容易了,只要找到并删除这个隐藏在系统里的BHO程序即可。
      除了这类“广告软件”性质的BHO,还有一种利用IURLSearchHook接口实现的另一类更隐蔽的BHO,这种BHO从某些方面来说大概不算BHO,因为它并不是响应IUnknown,而是等待IE创建IURLSearchHook来启动。IURLSearchHook被浏览器用来转换一个未知的URL协议地址,当浏览器企图去打开一个未知协议的URL地址时,浏览器首先尝试从这个地址得到当前的协议,如果不成功,浏览器将寻找系统里所有注册为“URL Search Hook”(资源搜索钩子,USH)的对象并把这个IE不能理解的地址发送过去,如果某个USH对象“认识”这个地址,它就返回一个特定的标识告诉IE它知道怎么打开这个地址,然后IE就根据约定的方法调用它,最终打开这个地址。其实USH对象并不陌生,我们一些偷懒的用户就经常为了省事而不输入“http://”,但是IE最终还是能认出并打开某个地址,就是USH的功劳,但是这一点又被恶意程序员拿来磨刀了,通过创建自己的USH对象,恶意程序员能够命令IE在找不到一些网站时自动跳转到事先设置的站点里,如果这个站点带毒或者挂马,用户就完了。
      这类BHO的解决方法和前面一样,只是它比较隐蔽,除非用户经常偷懒,否则可能直到系统崩溃也不会知道自己已经感染了这种东西。也许你会说,只要用户的输入永远不会让IE无法识别,这种渗透不就白费了?但是事实不容乐观,我们无法得知BHO作者还会不会通过其他方法拦截IE,说不定每隔一段时间就让IE弹出一个广告呢?
      上面说了这么多BHO和IE合作搞破坏的事例,可能会给读者造成一种“BHO必须在IE传递数据后才能行动”的误解,然而事实并非如此,浏览器自身也是一个标准的可执行程序,而BHO只是借用这个程序进程启动的DLL,它并非API那种要用的时候就让你过来忙活,忙活完了就一脚踹开的奴隶形态DLL,前面说过了,BHO是一种在浏览器加载时一同启动的例程,它相当于一种自身运行逻辑不太明确的子进程(里面都是对IE事件的响应和操作代码),这个特性就造成了BHO DLL和API DLL本质的区别,BHO并不需要所有事件都必须依赖这个大家伙,它可以有自己决定的权利,只要适当的修改,就能用BHO实现类似DLL木马的功能,当然,这并不是说我们就能在IE眼皮下公然的肆无忌弹干坏事的,由于BHO自身是作为IE子进程启动的,它就必须受到一些限制,例如程序员不能在里面自己创建网络连接,这样会导致IE报错崩溃并供出你写的DLL,害怕BHO成为另一种后门的用户可以松口气了,要在BHO里实现Winsock大概只能在IE休息的时候才可以,但是会有哪个用户开着个开空IE什  么事情都不做呢?
      但这并不是说BHO就一定能无害了,虽然用它不能做到远程控制,但是别忘记,BHO能看到IE的所有东西,也就能任意的访问用户文件和注册表,在这个条件成立的前提下,入侵者可以编写代码查找用户隐私,然后在适当时候通过SetSite提交出去——谁叫现在Webmail这么流行呢?这就是为什么许多厂商发布诸如“中文网址”、“网络搜索”、“IE定制”、“IE监视”这些功能的BHO的同时都保证“不搜集用户隐私”的原因,只要你想要,BHO就能得到一切。
      有些人也许会想,既然BHO是微软浏览器的权利,那我不用IE了,我用Opera、Firefox不行?对于这点固然无可厚非,但是你用不用Windows?用不用共享软件?如果你用Windows,那么,你仍然可能处于被BHO接触到的世界,因为Windows本身就是和IE紧密结合的,这就把“IE进程”的范围给扩大了,细心的用户大概会发现,IE里能直接访问“我的电脑”,“我的电脑”窗口也能迅速变成IE,因为它们实质都是依赖于IE内核的,正因为这个原因,BHO可以在你打开一个文件夹时跟着偷偷启动。同时,现在的网络正处于一种“共享软件捆绑战略”大肆实施的时代,你再小心也不能避免某些共享软件固定捆绑了BHO的行为,安装后你才会发现文件夹上又多了个什么“助手”、“搜索”了。要想彻底逃开BHO的围困,大概只能放弃使用Windows了。


    三. Hook,你钩住浏览器了
      “ Life finds its way.”——《侏罗纪公园》
      正如《侏》里的这句话一样,入侵者也在不断寻找他们的新出路,虽然上面我说了这么多BHO的负面事例,但是真正的危机并不是只有BHO的,在一些使用BHO行不通的场合里,入侵者开始投掷他们的钩子。
      什么是钩子?让我们先看看它的官方定义:
      钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
      钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

      可能上面的官方定义对一部分读者理解有点困难,其实,钩子就像是一切程序的“先知”,一个实现了钩子的程序自身虽然也是普通程序,但是它总能在别的程序得到数据之前就已经知道了一切,这是为什么呢?对Windows系统有一定了解的读者应该知道,Windows系统是一个通过“信息处理机制”运作的系统,在这个系统里传递的数据都是通过“消息”(Message)的形式发送的,各个消息都遵循了官方的约定,否则就不能让系统产生回应。而且这个传递步骤是颠倒的,例如我们关闭了某个程序,我们可能会认为是程序自己关闭后通知系统的,其实不然,当用户点击关闭按钮的时候,Windows就会把一个叫做WM_CLOSE的消息传递给这个程序,程序接收到消息后就执行卸载自身例程的操作。理解了这点,就能知道钩子的原理了,所谓钩子程序,就是利用了系统提供的Hook API,让自己比每一个程序都提前接收到系统消息,然后做出处理,如果一个钩子拦截了系统给某个程序的WM_CLOSE消息,那么这个程序就会因为接收不到关闭消息而无法关闭自身。除了消息以外,钩子还可以拦截API,像我们都熟悉的屏幕翻译软件就是Hook了一些文本输出函数如TextOutA而达到了目的。
      Hook技术让编程人员可以轻松获取其他程序的一些有用数据或传递相关数据,像现在常见的一些游戏外挂,它们就是利用Hook技术钩住了游戏窗体,然后就可以识别游戏里面的行为和模拟发送按键鼠标消息,最终实现电脑自己玩游戏的功能。把这个技术应用到浏览器上面,就成了另一种控制浏览器行为的方法。
      钩子有两种,本地钩子(Local Hook)和全局钩子(Global Hook),本地钩子只在本进程里起作用,故不属于讨论范围;全局钩子代码必须以DLL形式编写,以便在钩子生效时被其它进程所加载调用,因此我们看到的大部分Hook程序都是DLL形式的。
      其实之前提到的BHO也可以视为一种针对IE的钩子,它钩的是IE的事件,这就是IE与BHO交互的起点,但是对于再复杂一点的操作,例如判断IE下载的是GIF图片还是JPEG图片,BHO无能为力,因为它仅仅知道IE的事件为DownloadBegin和DownloadComplete,对于具体内容,IE本身是不会告诉它的,否则IE岂不是要忙死了?至少我也没见过哪个领导还需要向秘书汇报中午吃了鸡肉还是鸭肉的吧,BHO可不是IE的老婆,或者说IE没有气管炎。
      所以,为了得到IE的更多数据,程序员开始钩IE了。与BHO不同,钩子不需要被动的等待IE事件,它直接和IE形成上司对下属的关系,这次轮到IE要做什么都得经过它批准了。Hook形式的控制不需要DLL文件必须与IE的注册表入口产生组件关系,它可以是一个独立的DLL,通过Rundll32.exe或自带的Loader EXE启动,而且由于它属于Hook形式, 在钩子有效的情况下会被系统自动插入其他程序的进程中,是不是有点像DLL木马呢?
      IE钩子程序载入进程后便能获知所有的消息类型、API和内容,一旦发现某个符合要求的消息,如IE执行了某个事件,或者用户输入了特定内容,钩子的处理代码就开始工作了,它先拦截系统发送给IE的消息,然后分析消息内容,根据不同消息内容作出修改后再发给IE,就完成了一次Hook篡改过程。用著名的3721实名搜索做例子,一些人会以为它是采用了BHO或者IURLSearchHook完成中文域名的识别跳转的,其实它是用了能够第一个得到Windows消息的Hook技术,这样一来就可以避免被其他的竞争对手抢先解析域名了:3721的主程序就是一个Hook DLL,它监视IE地址栏的消息,一旦用户输入的是中文,它便在其他BHO类插件工作之前拦截了这个消息,并调用自身代码完成中文域名到英文URL的转换工作,然后返回(也可能与自己的BHO DLL配合)一个让IE跳转到英文URL的消息,完成域名的翻译任务。
      IE钩子能帮助程序员用少量代码完成更多的IE交互工作,但是一旦这个钩子被用于犯罪,其后果也是严重的,恶意程序员可以写一个拦截IE输入的键盘钩子,达到窃取密码的作用,这样无论你是用HTTP明文协议还是SecurityHTTP加密协议都不能逃避密码被盗的下场了,因为它抓的是你在IE里的输入,后面的数据传输已经不重要了。


    四. Winsock LSP
      全称为“Windows Socket Layered Service Provider”(分层服务提供商),这是Winsock 2.0才有的功能,它需要Winsock支持服务提供商接口(Service Provider Interface,SPI)才能实现,SPI是一种不能独立工作的技术,它依赖于系统商已经存在的基本协议提供商,如TCP/IP协议等,在这些协议上派分出的子协议即为“分层协议”,如SSL等,它们必须通过一定的接口函数调用,LSP就是这些协议的接口。
      通过LSP,我们可以比分析基本协议更简单的得到我们想要的数据内容,如直接得到系统上运行的浏览器当前正在进行传输的地址和内容,不管这个浏览器是IE,还是Opera或Firefox,因为LSP是直接从Winsock获取信息的,即使不用微软生产的汽车,至少你这辆汽车一直是在微软建造的公路上跑的吧。
      LSP用在正途上可以方便程序员们编写监视系统网络通讯情况的Sniffer,可是现在常见的LSP都被用于浏览器劫持,使用户又多了个噩梦。


    五. 亡羊补牢,还是居安思危?
      也许大部分家庭用户都是在经历过一次入侵或中毒事件后才知道安全防范的重要性的,能亡羊补牢当然是好事,但是如果能对自己的要求提高一点,做到未雨绸缪岂不是更好?我们总是依赖于别人的技术,依赖于模式化的杀毒手段,但那些始终都是别人的东西,控制权不能掌握在自己手上,这并不是很好的事情,也许,该是暂时放弃游戏挂级、搜集明星电影,好好研读一下安全方面和系统原理书籍的时候了,否则在这个不安全的网络中,我们随时可能会迷失自己。
      可能有人会想,小金又在发感慨了。也许是的,因为清除“浏览器劫持”一般都需要手工进行,虽然现在已经有了多个检测浏览器劫持的工具如HijackThis、Browser Hijack Recover等软件面世,但是如果你抱着和以往使用杀毒工具那样“一开扫描就安枕无忧”想法的话,你会发现自己真的会迷失了,由于BHO的特殊性(别忘记,它是合法的),这些工具只会把系统的进程、BHO项目、启动项、LSP等需要有一定技术基础方能理解的东西显示给你,然后由你自己决定IE的明天,如果你不曾重视过安全技术,那么就会觉得这些工具如同另一种折磨你的病毒了。
      学,还是不学?这是个必须考虑的问题……

    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10294527/viewspace-124984/,如需转载,请注明出处,否则将追究法律责任。

    转载于:http://blog.itpub.net/10294527/viewspace-124984/

    展开全文
  • IE里的探索之定制浏览器好助手(下) ()[@more@].NET.com/tech/web/2001/02/08/58_1642.html">出处:http://www0.ccidnet.com/tech/web/20...
    IE里的探索之定制浏览器好助手(下) (转)[@more@]

    .NET.com/tech/web/2001/02/08/58_1642.html">出处:http://www0.ccidnet.com/tech/web/2001/02/08/58_1642.html

    IE里的探索之定制浏览器好助手(下)
    (作者:青苹果工作室编译 2001年02月08日 14:00)

    访问文档对象
      现在 BHO 引用了 Internet Explorer 的 WebBrowser 控件并已经连接到浏览器 以接收它产生的事件。在 Web 页面被完全下载并被正确地初始化之后,现在终于可以通过 dhtml 文档对象模型访问它了。WebBrowser 的 Document 属性返回一个指向文档对象的 IDispatch 接口的指针:

      CComPtr pDisp;

      HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);

      get_Document() 方法提供的只是一个指向接口的指针。我们需要确定在 IDispatch 指针后面确实是一个 HTML 文档对象。如果使用 Visual Basic,以下是等价的代码:

      Dim doc As object

      Set doc = WebBrowser1.Document

      If TypeName(doc)="HTMLDocument" Then

       ' Get the document content and display

      Else

       ' Disable the display dialog

      End If

      现在我们需要判断 get_Document() 返回的 IDispatch 指针的实质。Internet Explorer 不仅是一个 HTML 浏览器,还能处理任何 ActiveX 文档 ;即任何有作为 ActiveX 文档服务程序的应用程序支持的文档。这样一来,就不能保证查看的文档的确是一个 HTML 页面。

      有一个解决办法就是查看 URL 并检查 URL 的扩展名。但该如何处理 Active Server Pages (ASP) 或一个暗含指向 HTML 页面的 URL?如果你使用了像 about 或 res 这样的定制协议又该如何?

      我们决定采取另一种方式,它和上面的 Visual Basic 代码性质相同。这种想法就是,如果 IDispatch 指针确实指向一个 HTML 文档,对 IHTMLDocument2 接口的访问就能成功地返回。IHTMLDocument2 是综合了 DHTML 对象模型为 HTML 页面实现的所用功能的接口。以下代码片断说明如何进行这样的判断:

      CComPtr pDisp;

      HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);

      CComQIPtr spHTML;

      spHTML = pDisp;

      if (spHTML) {

       // 取得文档的内容并显示它

      }

      else {

       // 禁止代码窗口控件

      }

      如果访问 IHTMLDocument2 接口失败,spHTML 指针为 NULL。否则,我们就可以正常访问 DHTML 对象模型的方法和属性了。

      现在的问题是如何获得已显示的页面的源代码。幸好,基本的 DHTML 知识就足以做到这一点。由于 HTML 页面将它所有的内容包含在 标记中,DHTML 对象模型要求你首先获得指向 Body 对象的指针:

      CComPtr m_pBody;

      hr = spHTML->get_body(&m_pBody);

      奇特的是,DHTML 对象模型不让你知道在 之前的标记,例如 的原始内容。这些内容已经被处理并被保存到一系列属性中了,但你依然不能得到一个最初的 HTML 文件的原始内容。然而,现在 body 能告诉我们的就足够了。我们需要将 outerHTML 属性的内容读取到一个 BSTR 变量里以获得包含在 和 之间的 HTML 代码。

      BSTR bstrHTMLText;

      hr = m_pBody->get_outerHTML(&bstrHTMLText);

      现在,在代码窗口中显示文本的工作就是创建窗口、将字符串从 Unicode 转换为 ANSI,并如图 3 中所示设置编辑框。以下是完成这些工作的全部代码:

      HRESULT CViewsource::GetDocumentContent()

      {

       USES_CONVERSION;

      

       // 获得 WebBrowser 文档对象

       CComPtr pDisp;

       HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);

       if (FAILED(hr))

       return hr;

       // 验证我们得到了一个指向 IHTMLDocument2 接口的指针

       // 我们查询 IHTMLDocument2 接口 (通过灵巧指针)

       CComQIPtr spHTML;

       spHTML = pDisp;

       // 获得文档的源代码

       if (spHTML)

       {

       // 获得 BODY 对象

       hr = spHTML->get_body(&m_pBody);

       if (FAILED(hr))

           return hr;

       // 获得 HTML 文本

       BSTR bstrHTMLText;

       hr = m_pBody->get_outerHTML(&bstrHTMLText);

       if (FAILED(hr))

       return hr;

       // 将文本从 Unicode 转换为 ANSI

       LPTSTR psz = new TCHAR[SysStringLen(bstrHTMLText)];

       lstrcpy(psz, OLE2T(bstrHTMLText));

       // 允许修改文本

       HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT);

       EnableWindow(hwnd, true);

       hwnd = m_dlgCode.GetDlgItem(IDC_APPLY);

       EnableWindow(hwnd, true);

       // 设置代码窗口的文本

       m_dlgCode.SetDlgItemText(IDC_TEXT, psz);

       delete [] psz;

       }

       else // 文档不是 HTML 页面

       {

       m_dlgCode.SetDlgItemText(IDC_TEXT, "");

       HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT);

       EnableWindow(hwnd, false);

       hwnd = m_dlgCode.GetDlgItem(IDC_APPLY);

       EnableWindow(hwnd, false);

       }

       return S_OK;

      }

      由于我们运行这段代码以响应 DocumentComplete 通知,每个新页面都会迅速地自动处理。DHTML 对象模型允许你修改显现的页面的结构,但在你按 F5 键或浏览器的 Refresh 按钮刷新视图后,所有的修改会立即丢失。通过对 DOWNLOADComplete 事件进行处理你能同时刷新代码窗口。(注意 DownloadComplete 事件比 DocumentComplete 事件先到达) 这时,你应该忽略第一次下载页面时产生的 DownloadComplete 而只考虑刷新时产生的事件。一个简单的布尔成员例如 m_bDocumentCompleted 可以用来区分这两种情况。

    管理代码窗口
      用以显示当前页面的 HTML 源代码的代码窗口是 ATL 的另一个基本元素,一个可以在 ATL 对象向导的 Miscellaneous 页里找到的对话框窗口。我们重置这个窗口的尺寸以响应 WM_INITDIALOG 消息,并使此窗口占据桌面工作区,即屏幕的可用部分减掉任务栏可能占据位置最下面的部分。

      浏览器启动时此窗口可能出现也可能不出现。默认情况下它是出现的,但可以通过清除复选框 Show window at startup 禁止。如果你愿意也可以关掉它。随后,可以在任何时候按 F12 键将其召回。F12 由我们在 SetSite() 中安装的键盘挂钩捕获。

      启动设置完全按照 microsoft 指示保存在注册表里。读写注册表时我们没有使用 win32 函数,而是使用了新的 shell Lightweight api (shlwapi.dll),这样 可以避免打开和关闭相应的注册表项的麻烦:

      Dword dwType, dwVal;

      DWORD dwSize = sizeof(DWORD);

      SHGetValue(HKEY_CURRENT_USER, _T("SoftwareMSDNBHO"),

       _T("ShowWindowAtStartup"), &dwType, &dwVal, &dwSize);

      这个 DLL 是在 Internet Explorer 4.0 和 Active Desktop 中引入的,从 windows 98 开始成为标准的系统组件。这些函数比相应的 Win32 函数更直接,适合只进行一次读写时使用。

    注册助手对象
      BHO 是 com 服务程序,应该同时以 COM 服务程序和 BHO 注册。ATL 模板为你提供了完成第一项注册的注册脚本代码 (RGS) 。以下是完成 BHO 注册的 RGS 代码。(CLSID 是从例程序中得到的。)

      HKLM {

       SOFTWARE {

       Microsoft {

       Windows {

       CurrentVersion {

         Explorer {

          'Browser Helper Objects' {

          ForceRemove {1E1B2879-88FF-11D2-8D96-D7ACAC95951F}

      }}}}}}}

      注意 ForceRemove 子句使键在对象取消注册时被删除。

      在 Browser Helper Objects 键下是所有安装的助手对象。浏览器从不将这些放入缓存,所以安装并测试 BHO 是一个很快的过程。

    BHO小结
      本文中,我们介绍了浏览器助手对象,一种相对来说比较新的、在浏览器的地址空间内直接引入你的代码的有效方式。你所要做的就是编写一个 COM 服务程序以支持 IObjectWithSite 接口。这里,你的模块从所有预定目的来看都是浏览器机构中的一个组件。本文中我们建立的例程序还涉及到如 COM 事件、动态 HTML 对象模型以及 WebBrowser 编程接口等内容。我们认为它演示了 BHO 的功能,同时提供了一个创建你自己对象的实用平台。如果你需要知道浏览器正在显示什么,你一定需要熟悉事件并进一步了解 WebBrowser。现在你知道:预先警告是为了早做准备。作为结语,我们提醒你 BHO 对 Windows Explorer 非常有用,而且,通过 WebBrowser,它能由你的代码驱动


     


    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752019/viewspace-984576/,如需转载,请注明出处,否则将追究法律责任。

    转载于:http://blog.itpub.net/10752019/viewspace-984576/

    展开全文
  • 订票助手

    千次阅读 2013-01-15 13:36:45
    12306订票助手 本站工具分类用户后台关于 ">后花园首页订票助手下载助手使用教程完整攻略免责声明常见问题助手常见问题捐助作者安装说明讨论区 12306.cn 可以网上购火车票了,但由于景德镇的...
     
     
    

    12306.cn 可以网上购火车票了,但由于景德镇的『国情』决定了春节时分它竟然比电话购票都不靠谱。但是,毕竟它是最方便的,所以在意识到其实这不仅是个技术活、更是一个体力活之后,终于决定博彩众家之长搞这么一个一条龙的轮子……

     

    【郑重提醒】

    12306.CN订票助手是由【木鱼】开发的『免费软件』,并没有授权任何人以任何形式在任何渠道上进行销售(如淘宝等),助手本身的使用也不收任何费用!目前助手只接受捐助(请参考捐助页面),如果您是从淘宝或任何第三方渠道购买的,请申请退款并向作者举报!

    同时强烈鄙视任何未经授权便拿来为自己谋利的无良卖家!

    简介[导航]

    现在放出来也许有点晚了,但是对于赶年前一两天以及回程票的同学还是有点用的,所以还是放出来吧 :-)

     

    不是原创,借鉴了不少别人的思想和思路,但代码基本上都是重写的。目前实现的功能有:

    • 记录登录的用户名和密码,在打开登录页面后自动填写;
    • 自动登录,遇到人过多或繁忙的时候自动重试,直到登录成功(有点儿抢线的味道);
    • 自动记录查询信息,一次查询线路后下次再查询自动填入;
    • 自动刷新查询,当没有需要的车次时,自动重新刷新;
    • 支持 Firefox + Scriptish 或 Chrome 以及 Maxhon3(3.0.4+开始支持)
    • 在 Chrome下,查票和登录有右下角提示和声音提示;Firefox下暂不支持声音提示,但有桌面弹窗提示。
    • 自动提交订单,直到订单成功(新功能!1.1版中加入!
    • 查询失败时自动刷新新功能!1.3版中加入!
    • 预定失败时自动重新预定新功能!1.3版中加入!
    • 禁用查询缓存新功能!1.3版中加入!
    • 查询界面参数和设置自动保存新功能!2.0版中加入!
    • 支持过滤无法预定车次新功能!2.0版中加入!
    • 支持过滤无需要席别车次新功能!2.0版中加入!
    • 现在系统已禁止验证码自动跳过,所以当出现验证码错误时,系统将会自动刷新验证码并自动定位到验证码输入框中并请求输入验证码,输入满四位的时候系统将会自动重新提交。新功能!2.0版中加入!
    • 支持过滤不需要车次 (3.0中新增!)
    • 支持出现指定车次时,自动进入预定的功能(3.0中新增!)
    • 支持自定义查询的时间段(3.0中新增!)
    • 新增Maxthon3的扩展支持!(3.0.4新增!)
    • 支持没有票可用时自动在指定的日期范围中轮换查询
    运行需求[导航]

    您必须准备以下条件之一:

     

    1. Firefox,最好比较新的版本(6+),并安装 Scriptish 扩展;
    2. Chrome(4以上版本)
    3. Maxthon3(遨游3),请至官方扩展区安装,地址参见下面的下载

     

    注意,如果您以前有安装过类似的脚本(如自动登录、自动查询等等),请先移除它们。

    功能介绍[导航]

    在登录页面,本脚本会加入密码记录功能和自动登录功能:

     

    记录密码后,下次登录直接就会填入,并自动定位到验证码段。需要提醒的是,密码是明文记录在本地存储中的,所以如果不是你自己的电脑,建议不要记录。

    请按照顺序填写,依次填写登录名、密码和验证码,三者都输入完成时,助手将会自动尝试登录,右侧会显示相关进度信息。如果验证码填写有误,助手会给出提示,并自动定位到验证码框中、输入刷新验证码。 

     

     

    如果登录成功,进入查询页面后,在查询下面有加一条选项,选项意思简单明了,不需要解释的吧 :-)

    当没有查询到符合要求的车次时,会自动重试。由于系统按钮有5秒钟的限制,所以不允许设置在6秒内重试。

     

     

    如果查到了,会停止刷新,高亮行,并在桌面右下角弹出提示。在谷歌浏览器下,会有音乐提示。Firefox下的提示框如下图:

     

    谷歌浏览器的提示框如下图:

     

    在车次列表,鼠标移动到车次上方时,右侧的提示信息中有两个新增的按钮:

     

     

    『加入黑名单』是指将指定车次加入黑名单,不管是否有车票都不再显示,适合于那些打死你也不坐的车次……

    『自动预定本车次』是指将指定的车次加入自动预定列表,如果指定车次的指定席别可用,那么将会自动进入预定页面。

     

    在3.0版本开始,车次列表下方加入了新的辅助工具部分。

     

    『增加自定义车票时间段』用于在查询的时间范围中增加自定义的时间范围。假如对系统设置的时间段不满意,可以使用此功能来增加符合自己要求的时间段。点击后,依次输入开始的小时数、结束的小时并选择是否记忆后,即可看到时间段下出现自定义的时间段(这里定义的是早上9点到下午4点):

    『车次黑名单』和『自动预定』分别对应前面的加入黑名单和加入自动预定,这里显示已设置的车次,并可以管理。

    『如果自动预定成功。。。。。』这个选项用于控制如果使用自动预定且成功,是否在进入订票页面后弹窗提示并播放音乐(Firefox下音乐不可用)。

    『自定义音乐地址』供您自定义提示的音乐,请确认填写正确,必要时可以使用『测试』来测试是否可以播放。

     

    进入订票页面后,全部填写完成时,直接点击『自动提交』,助手将自动帮您提交订单,并在成功后自动转到订单查询页面。出现错误时,会给出提示,并提示您操作。

    Chrome下安装[导航]

    在Chrome(谷歌浏览器)下安装你可能会不成功,并看到这样的提示:

    只可添加来自Chrome网上应用店的扩展程序、应用和用户脚本

    Apps, extensions, and user scripts cannot be added from this website.



    出现这种错误的原因是因为最新版的谷歌浏览器更改了安全设置,扩展、应用和用户脚本只能从Google Store里面安装。要解决这个问题,请打开浏览器的扩展管理界面:


    将脚本从下载栏直接拖放到界面中即可:



    如果拖进去后出现源码,那么最有可能的是因为你重复下载助手,导致后面的脚本文件名被修改。请删除所有已下载的文件,并重新开始安装。

    小贴士 & 开源[导航]

    请注意喔

     

    1. Chrome下的桌面提示仅在浏览器正常模式下才起效(也就是说你要是把浏览器最小化了,提示看不到的),不知道是不是Chrome都是这样,但我测试的结果是这样……所以一定要注意声音啊
    2. Firefox下的桌面提示是可以在桌面上弹出来的。我这里用的是Scriptish,没有测试GreaseMonkey的兼容性……木有时间啊
    3. 很多人说又要用IE去付款,还不是要反复登录的体力活吗?建议用银联在线支付,关联银行卡后用手机即可在Firefox或Chrome下直接付款(值得注意的是,Firefox6以上的版本它的安全控件暂时不支持,可以用我制作的安全控件补丁,点击下载)。

     

    开源

    本脚本助手已开源(@GitHub)。如果您有兴趣自己定制自己的版本并获得最新的内容,或提交自己的修改,请至项目主页

    项目主页位于:https://github.com/iccfish/12306_ticket_helper

    下载[导航]

    如果您不会安装,请务必参考 安装说明 页面。

    如果您遇到任何无法使用的问题(包括谷歌浏览器、Firefox、猎豹浏览器、遨游3或遨游4等 上无法使用、弹启用公告提示、打不开网页等),请务必参考助手常见问题页,都有解决方案:http://www.fishlee.net/soft/44/faq.html

     

    如果您使用的是谷歌浏览器,你还可以在谷歌的应用程序商店中安装本扩展:https://chrome.google.com/webstore/detail/idjgmabfihmhmojipdkcackbihbdceno

     

    推荐使用Firefox+脚本,这样您获得的助手更新将会是最及时的!

     

    如果你不会安装或者总是摆不平,请下载由作者打包的Firefox集成版:http://pan.baidu.com/share/link?shareid=190342&uk=2097456672已内置必须的Scriptish扩展和4.0.8版助手。请注意不要下载任何第三方人打包的绿色版,作者不保证其中的内容的安全性!

     

    请选择您需要的版本(请下载『通用版本,其它两个仅作兼容性更新保留!):

     

    火狐/谷歌浏览器脚本版

    已有 787924 次下载

    更新时间:2013-01-15 13:27
    下载大小:152.18 KB
    只建议Firefox安装本版本!
    谷歌浏览器建议安装商店版,可完全自动更新,地址见上方;或安装扩展版,也可自动更新,下载点在下面。

    安装如果出现问题,请参考导航中的几个常见问题。
    傲游3扩展

    已有 69448 次下载

    更新时间:2013-01-15 13:27
    下载大小:162.8 KB
    由于官方发布和更新扩展均需要审核,因此无法做到随时更新。如果您想要测试到最新版或随时获得最新版本,请安装此版本。此版本和官方发布的版本等效。
    CRX扩展

    已有 117688 次下载

    更新时间:2013-01-15 13:27
    下载大小:56.75 KB
    建议谷歌浏览器/猎豹浏览器/360极速浏览器安装此版本。这里的版本比谷歌商店发布的版本会更新一点。提示不能安装时请打开扩展管理,将扩展包拖进去。扩展版具有相同的更新功能。
    淘宝浏览器专版

    已有 34664 次下载

    更新时间:2013-01-15 13:27
    下载大小:54.95 KB
    淘宝浏览器专版扩展,非淘宝浏览器请勿下载。
    搜狗高速浏览器扩展

    已有 31758 次下载

    更新时间:2013-01-15 13:27
    下载大小:50.15 KB
    搜狗高速浏览器扩展包。安装后可在工具栏上显示快速订票按钮。请在极速模式下使用助手(非极速模式会有提示)。
    注意,搜狗浏览器4.0经测试不够稳定,通知权限无法保存每次都会弹出,且更新插件时表现异常行为,因此强烈不建议使用!
    猎豹专版

    已有 68107 次下载

    更新时间:2013-01-15 13:28
    下载大小:54.95 KB
    猎豹专版

     

     

     

    4.1.1 版本: 4.1.1,更新时间: 2013/1/15
    • 修正提示验证码必须输入的错误(紧急)
    4.1.0 版本: 4.1.0,更新时间: 2013/1/15
    • 启用全新的检查更新方式,检查更加稳定可靠
    • 修正针对部分提交时,针对无座不会缩减乘客的BUG
    • 对自动提交提示颜色启用全新的颜色提示,红色显示需要填写区域,绿色显示已填写区域
    • 修复自动预订时可能导致重复提交的潜在BUG
    • 手动输入车次时,尝试对输入错误的内容进行自动修正(如含逗号时自动转换为正则)

    4.0.8 版本: 4.0.8,更新时间: 2013/1/13
    • 允许在查询普通票时,也为学生提交学生票
    • 调整加速刷新为正点刷新,启用后59分内自动暂停刷新,以减轻缓存影响(测试)
    4.0.7 版本: 4.0.7,更新时间: 2013/1/12
    • 更改联系人选择,部分提交时,将会按照勾选的顺序优先订票
    • 更改验证码地址,解决部分情况下不显示的问题(HTTP访问订票网站时)

    4.0.6 版本: 4.0.6,更新时间: 2013/1/11
    • 添加部分提交功能,当余票数不足时,自动按顺序部分提交订单
    4.0.5 版本: 4.0.5,更新时间: 2013/1/11
    • 增加出行模式功能,能快速保存黑白名单及自动预定等设置,快速恢复;
    • 修复当联系人证件号重复时,自动提交无法使用的BUG
    • 增加自动预定模式选择(车次优先还是席别优先);
    • 修正进入预定页后,再点击余票查询会自动跳回的BUG;

    4.0.3 版本: 4.0.3,更新时间: 2013/1/11
    • 修改有席别优选存在时的优选顺序,调整为先席别再车次优选
    4.0.2 版本: 4.0.2,更新时间: 2013/1/10
    • 查询界面增加IE登录按钮;
    • 修复主动打开设置中的IE登录时没有代码的小问题;
    • 部分功能BUG修正和优化
    4.0.1 版本: 4.0.1,更新时间: 2013/1/10
    • 解决预定页只能提交一次的限制,出现后台错误时自动刷新预定;
    • 其它细节修改;
    4.0.0 版本: 4.0.0,更新时间: 2013/1/9
    • 全新的自动提交订单功能,允许你在查询界面预先填写验证码并全自动提交
    • 添加车次过滤白名单,在白名单中的车次将不会被过滤
    • 修改黑名单和自动预定列表为席别优先级一样的选择模式
    • 修改添加名单逻辑,现在自动预定和黑名单直接点击车次即可,不需要弹出层
    • 修改席别选择;增加全部席别按钮;当不优选席别时,改为选中所有席别
    • 重新加入被铁道部移除的上下铺选择下拉框,当然,加着玩儿,有效果更好,没效果……咱大肚能容那啥……
    • 席别优先选择中加入硬座的无座
    • 添加对在支付页面点击取消订单后跳回的查询页面的功能支持
    • 增加清除已保存配置的功能按键
    • 更新部分页面展示内容及效果, 增加部分选项提示,调整页面高度尽量避免太多滚动条
    • 修正预定界面上的『显示帮助』的配置保存问题(真是个古老的BUG……)。
    • 有童鞋提醒我一周年了……一看记录果然2012年1月9日发布的1.0版……一周年之际发布4.0版,感谢各位的支持和鼓励,希望每一位同学都能顺利地回家,2013都能收获自己想要的,不管是高雅的爱情还是庸俗的钱财 :-)
    3.9.0 版本: 3.9.0,更新时间: 2013/1/7
    • 优化自动预定流程,提供全新的席别优先判定,允许优先选择级别
    • 为Firefox启用全新的自动更新流程,检测升级更及时准确方便
    • 增强谷歌浏览器下的版本升级检测,提高稳定性
    • 修正服务器时间和本地时间差的负数显示问题
    • 修改部分废话,精简文字量,捎带卖卖萌(卖萌无罪……)

    3.8.5 版本: 3.8.5,更新时间: 2013/1/6
    • 登录页面增加起售日期提示和查询
    • 增加保持在线功能(即使不刷新只是挂着也会每隔一分钟提交一次请求防止掉线)
    • 新增服务器时间显示以及本地时间和服务器差额时间显示
    • 修正注册后无法刷新整页(框架时)
    • 修正谷歌浏览器下安装脚本版本时显示版本号不正确
    3.8.2 版本: 3.8.2,更新时间: 2013/1/6
    • 增加对取消订单后跳转的页面的自动的支持
    • 预先选择席别后增加自动调整查询结果中的席别过滤选项
    • 音乐地址同时提供HTTP和HTTPS的(但是好像依然不稳定,可以提供HTTPS协议音乐地址的同学请联系作者)
    • 其它相关逻辑及提示上的修正

    3.8.0 版本: 3.8.0,更新时间: 2013/1/3
    • 改进站点过滤,修正部分情况下未能过滤的BUG(不可预订车次),可以发到站分开过滤
    • 修正在自动预定列表和黑名单中存在“|”符号时,保存后会被替换为换行的BUG
    • 支付页面增加获得在IE中打开的代码(感谢xphelper提交的代码)
    • 其它部分细节修改

    3.7.2 版本: 3.7.2,更新时间: 2013/1/1
    • 修正前版本中部分配置保存不了的BUG
    • 修改过滤站点不匹配行为,直接隐藏对应车次
    • 调整交换站点按钮样式,避免在Firefox下换行

    3.7.1 版本: 3.7.1,更新时间: 2013/1/1
    • 修正默认将车次类型全部取消的BUG
    3.7.0 版本: 3.7.0,更新时间: 2013/1/1
    • 祝各位新年快乐
    • 增加保存查的车次类型功能
    • 增加过滤发到站或始发站不完全匹配的车次的功能
    • 更新预售时间查询链接

    3.6.7 版本: 3.6.7,更新时间: 2012/12/30
    • 修改自动预定逻辑, 现在预定当有多个车次有效时, 将会按照列表的顺序进行优先判定, 而不再是查询结果中的顺序
    • 修正取消自动预定时依然会自动预定的BUG
    • 修正通過查詢介面設置的自動選中上下鋪可能未能在预定界面设置的BUG
    • 修正取消自动刷新无效的BUG(这个BUG是在3.6.5修正版中修复的)
    3.6.5 版本: 3.6.5,更新时间: 2012/12/29
    • 修正在3.6.4版本中导致的学生票刷新BUG
    3.6.4 版本: 3.6.4,更新时间: 2012/12/29
    • 修正因铁道部改签页面程序问题导致的无法自动刷新(检测和刷新流程重写了)
    • 增加对查票结果中星号(*)的处理
    • 取消记录部分请求(因为有同学担心隐私泄漏)
    • 对遨游3/4做兼容性改进(取消点击日期下拉时会弹重复安装的提示)

    3.6.2 版本: 3.6.2,更新时间: 2012/12/29
    • 跟进最近修改,解决提交时显示非法提交的错误 (感谢 cutefelix 比作者还快的手 =。=)
    • 增加预先选择上下铺功能,显示预定界面的上下铺选择(但作者无法保证一定起效)
    • 其它细节调整

    3.6.1 版本: 3.6.1,更新时间: 2012/12/18
    • 支持预先选择席别并自动选中
    • 修正部分情况下可能导致的自动选择乘客失败的问题
    • 其它细节调整
    3.6.0 版本: 3.6.0,更新时间: 2012/12/11
    • 修正自动预定功能失效的问题(2012.12.11铁道部最新修改)
    • 修正过滤不可预定车次功能失效的问题(2012.12.11铁道部最新修改)
    • 修正自动刷新查询功能混乱的问题(2012.12.11铁道部最新修改)
    • 修正自动预选乘客功能失效的问题(2012.12.11铁道部最新修改)
    • 修正因铁道部的弹出框出错而导致重新查询的问题(2012.12.11铁道部最新修改)
    • * -----------------------------------------------------------
    • 提示:如果您发现车次的提示信息无法显示,这是铁道部的问题,请不要怪责老衲 =。=
    • * -----------------------------------------------------------
    • (新功能陆续开发中,为避免封杀咱不透露细节 #^_^#)

    3.5.7 版本: 3.5.7,更新时间: 2012/12/11
    • 修正登录成功但没有能判断出的错误(2012.12.11铁道部最新修改)
    3.5.6 版本: 3.5.6,更新时间: 2012/12/5
    • 修复有过滤车次时不会自动刷新的问题
    3.5.5 版本: 3.5.5,更新时间: 2012/12/4
    • 修复自动预定中不能使用正则表达式的BUG- 取消订票时间手动修改功能(喔……被铁道部枪毙了 ╮(╯▽╰)╭)
    • 增加订票最小张数限制
    • 修改刷新余票为即时刷新 (感谢 cutefelix 的提交)
    • 自动选择人员增加对护照等其它类型证件的支持
    • 增加请求日志功能(左上角显示日志按钮,出现异常的操作时请点击此按钮并复制日志)
    • (其它细节更改)
    3.5.0 版本: 3.5.0,更新时间: 2012/9/24
    • 在预定页面即可选择要自动填入订单的联系人,并自动定位到验证码框中 (感谢yangg提交的自动选择上次联系人)
    • 自动预定和黑名单添加开关,可自定义是否启用
    • 黑名单和自动预定允许手动添加
    • 黑名单和自动预定允许使用正则表达式进行匹配(正则表达式哦亲!)
    • 登录界面,取消勾选保存密码时,自动清空已保存的密码

    • 警告!谷歌商店中由 www.6pmhaitao.com 发布的订票助手扩展为盗用本助手并加入恶意脚本后打包的,请大家不要安装!

    3.4.3 版本: 3.4.3,更新时间: 2012/9/21
    • 增加谷歌的扩展包CRX类型的助手!(如果安装此类型,切记卸载脚本版的助手!)

    • 修正订单提交排队逻辑,解决当被强行退出登录时出现的错误并反复重新请求
    • 排队提示中增加当前排队人数,并将显示逻辑同步到铁道部最新更改
    • 增加对未完成订单页面支持,显示时间和排队更及时完整,订单成功失败均有声音提示
    • 增加两首用于提示的音乐(蓝精灵、超级玛丽),添加订票失败的悲歌(忽然之间 by 莫文蔚)……
    • 修改选项对话框显示位置,防止显示在可视区域之外
    • 修改登录逻辑,防止多次重试
    • 修复各查询页面提示刷新却失效的问题
    3.4.0 版本: 3.4.0,更新时间: 2012/9/19
    • 订票提交页面添加实时队列数显示!
    • 修正对 https://www.12306.cn/otsweb/ 的支持 【感谢(K.T.S)的提交!】
    • 修正对【改签】页面自动刷新的支持
    • 修正在提交订单页面出现错误后,重试之间没有时间间隔的BUG
    • 恢复老版本的音乐地址,因为新版本的音乐地址经常有人放不出来(仅WEBKIT内核,Firefox对HTTP地址拒绝播放)
    • 增加排队时间过久的警告
    3.3.6 版本: 3.3.6,更新时间: 2012/9/18
    • 修正预定操作提示系统忙时无法自动重试的BUG
    3.3.5 版本: 3.3.5,更新时间: 2012/9/18
    • 允许禁用自动登录改用手动登录
    • 增加对 https://www.12306.cn/otsweb/ 网址的支持
    • 修改自动更新逻辑,允许在更新前查看更新内容以决定是否更新,并允许屏蔽指定版本的更新推送
    • 登录页添加快速链接区, 并添加重新注册快速链接
    • 其它细节修改
    3.3.3-3.3.4 版本: 3.3.4,更新时间: 2012/9/17
    • 修改授权模式BUG,增加多账户模式。
    3.3.2 版本: 3.3.2,更新时间: 2012/9/17
    • 移除自动提交排队时右下角的提示,因为排队太蛋疼,所以看得很多人也比较蛋疼 ╮(╯▽╰)╭
    • 添加提交预定、未完成订单页面的出错自动重载功能
    • 添加排队订票成功后,转到支付页时音乐提醒的功能
    • 修正访问人数过多时,自动重新提交表单没有时间间隔的BUG

    3.3.1 版本: 3.3.1,更新时间: 2012/9/17
    • 修正点击我的12306却跳转到查询的BUG
    • 注册系统升级,新的序列号注册更简单,新增绑定12306帐户而没有注册时间限制的序列号

    3.3.0 版本: 3.3.0,更新时间: 2012/9/16
    • 增加Firefox中的Storage设置检测
    • 增加基础设置对话框,允许设置登录重试时间间隔
    • 为防未经作者授权的出售, 加入注册系统

    3.2.9 版本: 3.2.9,更新时间: 2012/9/14
    • 彻底修正登录人多时自动重试过快的问题
    • 修改默认音乐地址,避免浏览器安全设置导致没有音乐;
    3.2.8 版本: 3.2.8,更新时间: 2012/9/14
    • 修改登录页面重试时间为2秒以免速度过快被封
    • 添加403错误页自动重试顺便卖卖萌 o(︶︿︶)o, 买不着票只能自娱自乐了
    3.2.7 版本: 3.2.7,更新时间: 2012/9/13
    • 修正改签页面中验证码被帮助隐藏功能隐藏的BUG
    • 出现重复提交错误时,自动刷新页面验证TOKEN并重新提交
    • 修改对-4状态码的处理动作
    3.2.6 版本: 3.2.6,更新时间: 2012/9/12
    • 修正服务器出错时以高速进行重试的BUG
    • 修正当获得登录随机码失败时,以高速重复获得随机码的BUG
    • 添加默认禁止查询缓存的功能
    • 修正当自动轮查的日期中第一个日期选择时,无法自动切换的BUG
    • 添加隐藏当查询失败时系统弹出来的提示失败对话框

    3.2.4 版本: 3.2.4,更新时间: 2012/9/11
    • 修改自动提交逻辑,未知状态代码时自动跳转到未支付订单页面
    • 修改自动预定逻辑,在进入订票页面提示系统忙时将可以自动重新提交
    • 修改自动刷新逻辑

    3.2.3 版本: 3.2.3,更新时间: 2012/9/10
    • 修改自动订票逻辑, 实际优先订票车次以加入列表的顺序而定
    • 修复 Chrome 下订票提示在启用自动订票时不消失的问题
    3.2.1 版本: 3.2.1,更新时间: 2012/9/10
    • 修正提交订单时的逻辑
    3.2.0 版本: 3.2.0,更新时间: 2012/9/10
    • 添加日期快捷操作
    • 添加无票时在指定日期范围内循环查询的功能
    • 重写自动提交订单逻辑到最新版
    • 移除登录界面上无关紧要的弹窗提示
    • 修正登录界面成功的提示不会消失的问题
    • 修正登录脚本的兼容性
    • 去除CSCript兼容检测代码,因为发现检测还是会出编译错误,没用 o(︶︿︶)o
    • 修改刷新查询出错逻辑,改为延迟查询而不是立刻刷新(与自动刷新使用同样时间间隔)
    • 修正当查询出错时以高频率刷新的问题;
    • 其它细节更新
    3.0.6 版本: 3.0.6,更新时间: 2012/8/23
    • 添加输入验证码后自动开始提交的选项
    • 修正遨游3更新链接提示的地址
    • 修改检查更新所使用的地址,避免出现不安全脚本不让更新的问题

    3.0.5 版本: 3.0.5,更新时间: 2012/8/23
    • 修改登录成功提示,变成右下角弹窗提示
    • [遨游3] 扩展兼容性改进,允许直接访问 www.12306.cn 时的运行
    • 修正自定义时间里,24不被认为是正确时间的错误
    3.0.4 版本: 3.0.4,更新时间: 2012/8/23
    • 增加遨游3的扩展格式支持;
    • x 修改提示打开全屏订票的弹框为链接,避免频繁提示造成困扰
    3.0.2 版本: 3.0.2,更新时间: 2012/8/22
    • 修正在Firefox下查询有票时桌面提示不出现的错误
    3.0.1 版本: 3.0.1,更新时间: 2012/8/21
    • 修复勾选“仅座票”、“仅卧铺”过滤无效的BUG
    3.0.0 版本: 3.0.0,更新时间: 2012/8/19
    • 增加新开专门订票网页窗口的提示;
    • 席别过滤的勾选可以记录到设置中;
    • 修改列表标题文字,避免换行;
    • 增加过滤车次功能;
    • 增加出现指定车次时,自动进入预定的功能;
    • 增加自定义提示音乐地址功能;
    • 增加进入预定页面后,自动播放提示音乐功能(针对自动预定)
    • 增加自定义提示音乐地址功能
    • 修复进入订票页面时,没有点击自动提交时输入验证码也会提交的BUG
    2.2.0 版本: 2.2.0,更新时间: 2012/5/30
    • 修正自动登录无法登录的问题;
    • 修正了时间显示的只有个位数的问题(虽然不是BUG但比较难看。。。)
    • 修正了遇到帐户锁定或密码输入错误时依然不断重试的问题;
    • 修正了验证码输入错误时不会自动刷新验证码的问题;
    • 其它细节更新

    2.0.0.0 版本: 2.0.0.0,更新时间: 2012/1/19
    • 登录界面重新调整
    • 增加查询界面参数和设置保存功能
    • 增加过滤无法预定车次功能
    • 增加过滤无需要席别车次功能
    • 修复数个BUG
    • 现在系统已禁止验证码自动跳过,所以当出现验证码错误时,系统将会自动刷新验证码并自动定位到验证码输入框中并请求输入验证码,输入满四位的时候系统将会自动重新提交。

    1.4 版本: 1.4,更新时间: 2012/1/16
    • 将Chrome和Firefox两个版本分支完全合并,兼容处理
    • Firefox下支持声音提示
    • 修改了提示声音
    • 提示窗口均设置默认值自动关闭
    • 修正点击查询后参数保存的问题
    • 修改了参数保存位置,不再保存在Cookies中
    • 加入改签页面的自动提交
    • 增加脚本更新提示功能
    1.3.2 版本: 1.3.2,更新时间: 2012/1/11
    • 修复了一些BUG
    • 增加了自动提交订单时的休息时间的设置
    1.3 版本: 1.3,更新时间: 2012/1/10
    • 新增查询失败时自动刷新的功能
    • 新增预定失败时自动重新预定的功能
    • 新增禁用查询缓存的功能
    • 其它细节更改
    1.1.3 版本: 1.1.3,更新时间: 2012/1/10
    • 增加改签页面的自动刷新支持
    1.1.2.1 版本: 1.1.2.1,更新时间: 2012/1/9
    • 更改登录判断逻辑,避免提示登录成功却又跳回登录页面的问题;
    • 其它细微更改
    1.1.1 版本: 1.1.1,更新时间: 2012/1/9
    • 增加取消自动提交订单的功能
    1.1 版本: 1.1,更新时间: 2012/1/9
    • 增加订单自动提交功能
    发布 版本: 1.0,更新时间: 2012/1/9
    • 第一次发布。
    展开全文
  • iOS推送通知

    2015-02-10 17:45:05
    自:...很好很详细的推送通知流程, 本人就按照这个实现. Learn how to add Push Notifications into your iPhone app 在iOS系统中,

    转自:http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12

    很好很详细的推送通知流程, 本人就按照这个实现.


    Learn how to add Push Notifications into your iPhone app!

    Learn how to add Push Notifications into your iPhone app

    在iOS系统中,在后台运行的程序能够进行的操作是非常有限的。这种限制是为了节省手机电池。
    但是,如果你需要在用户没有使用你的程序的情况下给他们推送消息该怎么办呢?

    打个比方,用户收到了一个来自推特的信息,他喜欢的球队在比赛中取胜,或者他的晚饭准备好了。因为现在用户并没有在使用我们的程序,程序本身无法听取这些事件。

    幸运的是,苹果系统对此已有了解决办法。你并不需要让程序不停地听取事件或者在后台跑运算。你只需要编写一个服务器组件来完成这个任务。

    在一个特定的事件发生时,那个服务器的组件就可以给我们的程序发一个推送信息!推送信息可以做如下三件事:

    • 显示一条信息
    • 播放一小段提示音乐
    • 在程序的小图标边上放置一个数量标志

    你可以随意组合这些选项;比如你可以播放提示音并放置数量标志,而不显示任何信息。

    在这个有两部分的教程中,你会用苹果推送服务器(APNS)来完成一个有消息推送功能的简单的程序。

    在第一部分,你会学习如何接收推送的信息。

    这篇教程针对的是中级或者高级的iOS开发者。如果你对iOS还处于入门阶段,你应该先看看这个网站上一些初级教程. 并且,我也建议你可以先看看下面这两篇教程(或者有类似的经验):

    那就让我们开始吧!

    文章概略

    为你的app加入信息推送是需要付出很多努力的。这个任务有很多个部分。下面是一个概要:

    Apple Push Notification Services (APNS) Overview

    1. 程序启用消息推送功能。用户必须确认他希望接受这些推送信息。
    2. 那个程序接收一个“设备标记码”。你可以把这个设备标记码理解为推送信息的地址。
    3. 那个程序将这个设备标记码发送到服务器。
    4. 每当任何关于你的程序的事件发生时,那个服务器会将信息发送到苹果的推送信息服务(APNS)。
    5. APNS 将这个信息再推送到用户的设备上

    用户的设备收到这个信息时,会有提示窗口,播放提示音或者更新app的数量标志。用户可以在提示窗口中直接开启这个app。我们的app从这里接过推送信息的内容并能自定义处理这个信息的逻辑。

    有人会问,iOS4中已经有了本地提示以及多重任务执行,那推送信息提示还有用吗?答案:“当然啦”!

    本地提示仅能用于定时的事件。无限制的后台运算也仅限用于网络通话,导航和背景音乐类的app。如果在程序已经进入后台运行还想给用户信息提示,那我们仍然需要使用推送信息提示。

    在这个教程中,我会详细解释推送信息提示是怎么实现的,以及如何在你的app中使用它。需要学的东西很多,让我们现在开始吧!

    推送信息提示准备工作

    在你的app中加入推送信息提示,你需要:

    一台 iPhone 或者 iPad。 你需要真实的设备因为推送信息提示在模拟器中不能用。

    你必须是注册的iOS开发者。 每个要加入推送信息的app的需要一个新的App ID,provisioning profile 和SSL证书。你可以在iOS Provisioning Portal来完成这些所需操作。

    如果你想跟着这个教程中的例子,那你就需要创建新的provisioning profile和SSL 证书;你不能用我的。因为获取正确的证书很重要,我会一步步解释如何获取这个证书。

    一个连接到网上的服务器。 推送信息是由这个服务器发送出来的。开发期间你可以用你的苹果电脑作为服务器(我们在这个教程中就会这样做),但是在app上线后,你至少需要一个VPS(虚拟私人服务器)。

    一个简单的共享的服务器账号是不够的。你需要能够在服务器上跑后台线程,安装SSL证书以及对外在特定端口建立TLS链接。

    大多数共享服务器的提供者是不会让你这么做的。所以,我强烈建议你用 Linode类型的服务器.

    解析消息推送服务/h2>

    你的服务器负责创建被推送的消息,所以我们应该来了解一下这个服务器是怎么做到的。

    一个推送消息会包含设备标记码,信息负载和一些其他的字节。那个信息负载就是我们要发到设备上的推送消息。

    你的服务器提供的这个信息负载应该是JSON字典的格式。一个简单的推送信息负载应该是这样的:

    {
    	"aps":
    	{
    		"alert": "Hello, world!",
    		"sound": "default"
    	}
    }
    

    如果你不了解JSON,你只需要知道用“{}”符号分割出来的一个代码块代表了一个键与值对应的字典(和NSDictionary相似)。

    我们的信息就是这样的一个字典。这个字典里至少要有一个物件,“aps”。在这里,“aps”本身又是一个字典。“aps”包含了“alert”和“sound”两个键。当设备收到这个消息时,程序应该显示一个弹出消息:“Hello,world!”并且发出标准的提示音。

    你还可以在“aps”字典中加入其他物件来设置那个消息。比如:

    {
    	"aps":
    	{
    		"alert":
    		{
    			"action-loc-key": "Open",
    			"body": "Hello, world!"
    		},
    		"badge": 2
    	}
    }
    

    注意“alert”本身也变成了一个字典。弹出信息栏的查看按钮的标签会变换成“action-lock-key”的值。“badge”键所对应的数字值会成为程序图标的数量标记。这个消息不会发出提示音。

    这个JSON格式的信息负载还有很多可以设置的选项。你可以改变提示音的声音,提供翻译过的标签,你也可以加入自定义的键值对。如果有兴趣深入了解,你可以看看苹果公司提供的 本地和推送信息编程指南.

    推送信息应该很简洁;那个信息负载不应该超过256个字节。这样一般有足够的空间传送一个SMS信息或者一个推特消息。正确的服务器不会浪费宝贵的负载空间了传送换行符和空格,所以你的服务其应该发送这样的信息:

    {"aps":{"alert":"Hello, world!","sound":"default"}}
    

    这个对于人来说比较难读懂,但是却更节省空间。苹果APNS不会接受超过256个字节的推送消息。

    推送消息的易错点

    推送消息不可靠!

    推送消息不可靠!

    太不可靠了! 就算在APNS服务器接收了信息的情况下,推送消息也不一定会成功地传到用户设备上。

    你的服务器在发射推送消息到APNS后,没有任何办法可以获取消息的状态。消息实际被推送的时间也不一定,可能几秒钟,也可能要半个小时。

    并且,用户的iPhone并不是随时都能接收推送信息。他们可能在一个无法接收苹果推送服务的无线网络中,原因可能是相应的网络端口被封闭了;或者用户的手机关机了。

    苹果推送服务会在手机重新上线后试着重试没有传递的信息,但是这是有时限的。时限一过,我们就永远失去那个信息提示了!

    在看到苹果推送服务的账单后

    在看到苹果推送服务的账单后

    好贵啊! 如果有很多用户,或者所推送的信息需要从其他地方不断获取,那这个服务的费用可能会很高。

    打个比方,如果你控制了RSS源。因为你会清楚知道什么时候会有新的RSS,你可以轻松直接地在适当的时候给用户推送信息。
    但是,如果你的程序允许用户输入自定义的RSS网址怎么办?在这种情况下你需要有自己的一套方案来探测是否有心的RSS信息。

    事实上,你的服务器需要不停地访问这些地址来探测新的RSS信息。如果你有很多用户,那你就需要不断增加新的服务器来探测和发送信息。这很快就会变得非常昂贵。

    理论问题讨论完了,那我们就开始实践吧。 但是在做有趣的事情–编程,之前, 我们还需要在iOS开发者门户网站上完成一些无聊的设置。

    Provisioning Profiles以及各类证书

    苹果推送服务需要一个证书!

    苹果推送服务需要一个证书!

    为了使你的程序能使用推送服务,我们需要建立一个特别用来进行这个服务的provisioning profile。另外,你的服务器还需要通过SSL证书来和苹果的推送服务器建立联系。

    provisioning profile和SSL证书是一一对应的,并且只有在有一个有效的App ID的情况下才能用。这是为了保证你的服务器只能将推送信息发到这一个特定的程序中,而不能发到其他任何程序。

    值得注意的是,一个程序在开发时和发布时需要用不同的provisioning profile。同时也有两种服务器证书:

    • 开发时. 如果你的程序是在Debug模式下运行,并且使用的是开发阶段的provisioning profile(Code Signing Identity属性的值是”iPhone Developer”),那你的服务器必须使用开发阶段的证书。
    • 生产时. 程序如果已经在苹果商店上发布(Code Signing Identity属性的值是”iPhone Distribution”),那服务器必须使用生产阶段的证书。如果这两个被弄混了,推送的提示信息就无法到达你的程序。

    在这个叫教程里,我们将只会使用开发时的profile和证书。

    生成Certificate Signing Request(证书申请)

    还记得你如何在iOS开发者网站上注册iOS开发计划并获取开发证书的吗?我们下面要做的和那个类似。但我还是建议你一步一步跟着我来完成这个操作。因为大多数问题都出在证书上。

    电子证书都是基于公共和私有密钥加密的。你并不需要知道这个加密的过程,但你需要知道那个证书必须和一个私有密钥一起用才会有效。

    那个证书是这组密钥的公共部分。所以把这部分随便给别人是没有问题的,在你用SSL来通讯时就需要这么做。但是那个私有密钥却需要保密。这个秘密不能让任何别人知道。如果你没有这个密钥,那证书就失效了。

    在你申请一个电子证书时,你需要提交一份Certificate Signing Request(证书申请),缩写为CSR。在你生成CSR时,会生成一个新的私有密钥并被放到你电脑的keychain中。然后你把这个CSR发到一个证书的认证网站(在我们的情况下,这个网站就是苹果的开发者门户网站)。这个网站会用CSR生成相应的SSL证书。

    在你的Mac上打开Keychain Access程序(在Applications/Utilities子目录下),然后在菜单中选择 Request a Certificate from a Certificate Authority…,意思是从证书权威获取证书。

    Requesting a certificate with Keychain Access

    如果你在菜单里找不到需要的选项,或者选项的标签是“Request a Certificate from a Certificate Authority with key”, 那你就需要先下载并且安装WWDR Intermediate Certificate。 还需要注意的是,在Keychain Access主窗口中,千万不要选择任何的私有密钥。

    然后你应该看到类似下面的窗口:

    用Keychain Access来申请证书

    输入你的邮箱。我曾听有人说你应该用和你申请iOS开发者计划时一样的邮箱,但是应该任何邮箱都是可以的。

    在Common Name(公用名)一栏输入”PushChat”. 你可以输入任何名字作为公用名,但最好选择一些有代表性的名字。这样我们以后才能很快的找到这个私有密钥。

    Saved to disk(保存到硬盘)边上打钩。将它保存为”PushChat.certSigningRequest”。

    如果你现在来到Keychain Access的Keys部分,你应该看到一个新的,你刚刚生成的私有密钥。右键点击并将它汇出。

    用keychain access汇出私有密钥

    将汇出文件命名为“PushChatKey.p12”然后输入一个加密码。

    为了方便,我的加密码就是“pushchat”。但为了更好地保护这个p12文件,你应该选择一些更难猜到的加密码。私有密钥必须是秘密的,不是吗?但是你一定要记得住这个加密码,否则私有密钥还是不能用。

    创建App ID和获取SSL证书

    登陆iOS置备门户网站.

    首先,我们需要创建一个新的App ID。每一个用推送信息服务的app都需要一个特有的ID,因为只有这样,被推送的信息提示才能被发送到正确的程序上。(在这里,你不能使用通配符ID。)

    在边上的目录栏中选择“App IDs”,然后点击“New App ID”按钮。

    生成新的App ID

    我填写内容如下:

    • Description: PushChat
    • Bundle Seed ID: Generate New
    • Bundle Identifier: com.hollance.PushChat

    你最好是选择一个你自己的Bundle Identifier,而不是用我的,例如“com.你的网站.PushChat”。你之后在Xcode中还要使用这个Bundle Identifier。

    很快,我们会生成一个SSL证书。你的服务器就是用这个证书来和苹果的推送服务器建立安全连接。这个证书和你的App ID绑定在一起,这样你的服务器就只能给这一个app发送推送信息提示了。

    在你创建好App ID后,你应该看到类似如下的列表:

    List of App IDs in the iOS Provisioning Portal

    在“Apple Push Notification service”(苹果推送信息服务)一栏里,有两个橘黄色的标志分别写着“Configurable for Development”(开发设置)和“Configurable for Production”(发布生产设置)。这意味着我们的App ID已经可以用来进行推送了,只是还需要我们做相应的设置。点击那个写着“Configure”的链接来打开设置App ID的界面。

    Configuring your App ID in the iOS Provisioning Portal

    “Enable for Apple Push Notification service”(启用苹果推送服务)旁打钩然后点击对应“Development Push SSL Certificate”的Configure 按钮。苹果推送服务SSL证书助手界面就出现了:

    Uploading your CSR with the SSL Assistant

    它首先要你生成一个证书签署请求。我们已经完成这一步了。点击Continue。在下一步你需要上传已经生成的证书签署请求(CSR)。选择那个CSR文件,然后点击Generate

    Generating a Certificate with the SSL Assistant

    生成SSL证书需要几分钟时间,完成后点击Continue

    Downloading a certificate with the SSL assistant

    点击“Download” 来下载证书 – 名字应该叫 “aps_developer_identity.cer”. 点击 “Done” 来关闭助手界面并回到设置App ID的界面。

    Screenshot after the SSL Assistant is Complete

    如你所见,我们现在有一个有效的证书了,可以开始给App推送证书了。如果需要,你可以在刚才的地方重新下载证书。开发证书的有效期是3个月。

    当你的App准备可以正式使用时,重复上述步骤来获取生产时用的证书。步骤是一样的。

    注意: 生产时用的证书有效期是1年。但你需要提前更新它以确保你的App的推送服务不会中断。

    创建PEM文件

    现在我们总共有三个文件:

    • CSR
    • 私有密钥(PushChatKey.p12)
    • SSL证书(aps_developer_identity.cer)

    将这三个文件好好保存起来。你可以选择不再用那个CSR文件了。但是在你更新证书时,你可以使用同一个CSR或者获取一个新的。如果你获取新的CSR,你同时会生成一个新的私有密钥。如果使用同一个CSR,那就只有SSL证书需要改变了。

    我们需要将证书和私有密钥转换成另外一个更方便的格式。我们在服务器的推送部分会用PHP来写,所以我们现在可以把证书和私有密钥合并成一个PEM格式的文件。

    PEM文件是怎么运作的并不重要(说实话我也不知道),但是这样我们能更容易地在PHP里使用这个证书。如果你准备用其他语言来写这个服务器部分,那下面的步骤可能会不配套。

    我们将使用命令行OpengSSL的工具来完成这项任务。打开一个Terminal并输入以下指令。

    “cd”到你下载证书,密钥文件的文件夹,对于我是桌面文件夹:

    $ cd /Users/matthijs/Desktop
    

    将那个.cer文件转换成.pem文件:

    $ openssl x509 -in aps_developer_identity.cer -inform der 
        -out PushChatCert.pem
    

    将那个密钥.p12文件转换成.pem文件:

    $ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12
    Enter Import Password: <输入你导出密钥时用的那个密码>
    MAC verified OK
    Enter PEM pass phrase: <输入一个新的密码>
    Verifying - Enter PEM pass phrase: <重复密码>
    

    你首先需要输入.p12文件的密码,这样openssl才能读取这个文件。然后你需要你个新的密码来对pem文件进行加密。在这个教程中我用的还是“pushchat”。但你自己应该选择一个更加保险的密码。

    注意:如果你不输入PEM的密码,openssl不会给你任何的错误信息。但是生成的.pem文件里就不会有那个密钥。

    最后,将那个证书和密钥合并为一个文件:

    $ cat PushChatCert.pem PushChatKey.pem > ck.pem
    

    我们应该测试一下这个证书是否能用。执行如下指令:

    $ telnet gateway.sandbox.push.apple.com 2195
    Trying 17.172.232.226...
    Connected to gateway.sandbox.push-apple.com.akadns.net.
    Escape character is '^]'.
    

    我们试着与APNS服务器建立一个一般的,没有加密的连接。如果你看到类似上面的回复,那说明你的Mac能连上APNS。按Ctrl+C切断连接。如果你得到一个错误信息,那你应该确保你的防火墙允许对外2195端口的连接。

    让我们再次试着连接。这次,我们会使用那个SSL证书和密钥来建立一个加密连接:

    $ openssl s_client -connect gateway.sandbox.push.apple.com:2195 
        -cert PushChatCert.pem -key PushChatKey.pem
    Enter pass phrase for PushChatKey.pem: 
    

    你应该看到一大窜回复内容。那是openssl的运行信息。

    如果连接成功建立,你应该可以键入几个字符,然后当你点回车时,服务器就会和你断开连接。如果建立连接过程出现问题,openssl会给出错误信息,但你可能需要在那一大窜信息中找出错误信息。

    值得注意的是,APNS其实有两个不同的服务器:那个沙盒服务器使用来测试的。还有一个正式的服务器是在你的程序投入生产后使用的。我们上面用的是测试用的服务器。因为我们的证书是开发时才能用的。

    生成Provisioning Profile

    我们还需要用Provisioning门户网页来生成一个新的Provisioning Profile。在左边的目录栏中点击“Provisioning”,选择“Development”来生成一个新的profile。
    Creating a Provisioning Profile in the iOS Provisioning Portal

    填写表格如下:

    • Profile Name: PushChat Development
    • Certificates: 在你的证书旁打钩
    • App ID: PushChat
    • Devices: 选择你的设备

    我们需要一个新的profile因为每个有推送信息的app都需要和它App ID相对应的一个profile。

    点击Submit,你的profile就会被生成。那个profile的状态一开始会是“Pending”。刷新页面直到它的状态变成“Active”,这时你就可以下载那个文件了(给它取名为PushChat_Development.mobileprovision)。

    双击那个文件或者将这个文件拖到Xcode的图标上,这样就能将这个profile添加到Xcode中了。

    在你将程序投入生产时,你需要重复上述步骤来生成Ad Hoc或者distribution profile。

    一个非常基础的APP

    到现在为止我们做的准备工作都比较枯燥,但却是必要的。我们详细的学习了生成各种证书的过程因为我们不会经常坐这件事,但是推送信息没有这些就无法运作。

    通过连接到沙盒服务器,我们确认了证书是可用的。现在我们要测试一下我们是否确实可以推送提示信息!

    启动Xcode并建立一个新的项目。 选择View-based Application作为这个项目的模版然后点击进入下一步。

    Creating a View-Based Application with Xcode 4

    我是这样填写各个空格的:

    • Product Name: PushChat
    • Company Identifier: com.hollance
    • Device Family: iPhone

    那个Product Name(产品名字)和Company Identifier(公司代码)合在一起就是Bundle ID。我的是“com.hollance.PushChat”。 你应该选择和你在苹果的Provisioning Portal上注册的App ID相对应的产品名字和公司代码(com.yourname.PushChat)。

    完成创建项目并打开PushChatAppDelegate.m文件。将didFinishLaunchingWithOptions改为如下:

    [objc]  view plain copy
    1. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">-</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">BOOL</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIApplication <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application didFinishLaunchingWithOptions<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:none"><span style="font-family:inherit;color:#40080;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">NSDictionary</span></a> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>launchOptions  
    2. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">{</span>  
    3.     self.window.rootViewController <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">=</span> self.viewController;  
    4.     <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span>self.window makeKeyAndVisible<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span>;  
    5.    
    6.     <span style="font-family:inherit;color:#1174a;margin:0px; padding:0px; border:0px; outline:0px; vertical-align:baseline;"><em>// 让手机知道我们想接收推送信息提示。</em></span>  
    7.     <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span>UIApplication sharedApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span> registerForRemoteNotificationTypes<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span>  
    8.         <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span>;  
    9.    
    10.     <span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">return</span> <span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">YES</span>;  
    11. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">}</span>  

    我们调用registerForRemoteNotificationTypes方法来告诉OS我们的程序想接收推送信息。

    编译并运行你的程序。你应该在真正的苹果设备上运行因为模拟器不支持推送信息提示。Xcode应该会自动选择那个新的provisioning profile。如果你得到一个关于“code sign”的错误,那你需要确保已经选择了正确的profile。 你可以在“build settings”中的“Code Sign”设置部分对其修改。

    当启动你的程序时,系统应该会弹出一个选择菜单来询问用户是否允许当前程序推送提示信息。

    A Simple iPhone App Requesting Permission to Deliver Notifications

    你的程序只会向用户询问一次然后记住用户的选择。用户只有选择“OK”之后你的程序才能接收推送提示信息。用户也可以在iPhone系统设置菜单中更改推送信息的设置。

    Viewing Push Notification Permissions in iPhone Settings

    你app的名称会出现在手机的提示设置界面里。用户可以在这里设置是否允许你的app推送信息提示,显示数量图标以及发出提示音。

    A single app's Push Notification Settings

    你的app也可以通过如下的代码来探测用户启用了哪一种提示方式:

    [objc]  view plain copy
    1. UIRemoteNotificationType enabledTypes <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">=</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span>UIApplication sharedApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span> enabledRemoteNotificationTypes<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span>;  

    要让我们的app开始接收推送提示,我们还需要将下面的代码加入PushChatAppDelegate.m文件中:

    [objc]  view plain copy
    1. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">-</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">void</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application didRegisterForRemoteNotificationsWithDeviceToken<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:none"><span style="font-family:inherit;color:#40080;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">NSData</span></a><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>deviceToken  
    2. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">{</span>  
    3.     NSLog<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#1174a;margin:0px; padding:0px; border:0px; outline:0px; vertical-align:baseline;"><em>@</em></span><span style="font-family:inherit;color:#bf1d1a;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">"My token is: %@"</span>, deviceToken<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>;  
    4. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">}</span>  
    5.    
    6. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">-</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">void</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application didFailToRegisterForRemoteNotificationsWithError<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:none"><span style="font-family:inherit;color:#40080;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">NSError</span></a><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>error  
    7. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">{</span>  
    8.     NSLog<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#1174a;margin:0px; padding:0px; border:0px; outline:0px; vertical-align:baseline;"><em>@</em></span><span style="font-family:inherit;color:#bf1d1a;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">"Failed to get token, error: %@"</span>, error<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>;  
    9. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">}</span>  

    当你的app注册远程推送提示时,它会获取一个设备代码(device toke)。这是一个32字节的独特数字来辨别用户的设备。你可以把这个代码看做是用户设备的地址。APNS用这个地址来推送信息提示。

    在你用来测试的苹果设备上运行你的app。你应该在Xcode的控制台窗口中看到:

    My token is: 
    <740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad>
    

    那个代码是一个NSDatat对象,拥有某种二进制数据结构。我们不应该对它进行任何修改。我们只用知道它有32个字节,可以用64个十六进制的字符表示。我们会直接用这种表示方式(去掉那括弧和中间的空格)。

    如果你在模拟器里运行你的程序,didFailToRegisterForRemoteNotificationsWithError:方法应该会被调用因为模拟器不支持推送信息提示。

    我们的app就暂时写完了。但要真的开始推送信息,我们还需要完成最后的一步!

    推送第一个信息提示

    我曾说过,你需要有一个服务器来把信息推送到你的app上。但为了测试,我们还不需要一个服务器。你可以用下面这个简单的PHP脚本来和APNS建立连接并将信息推送到指定的设备上。你可以直接在你的Mac上运行这个脚本。

    下载这个叫“SimplePush”代码包 并解压缩. 你需要对simplepush.php文件进行如下修改.

    [php]  view plain copy
    1. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-family:inherit; vertical-align:baseline; color:rgb(102,102,102)"><em>// 把你的设备代码放在这里(不要空格): </em></span>  
    2. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,136)">$deviceToken</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">=</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,255)">'0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78'</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">;</span>  
    3.    
    4. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-family:inherit; vertical-align:baseline; color:rgb(102,102,102)"><em>// 把你密钥的密码放在这里:</em></span>  
    5. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,136)">$passphrase</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">=</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,255)">'pushchat'</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">;</span>  
    6.    
    7. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-family:inherit; vertical-align:baseline; color:rgb(102,102,102)"><em>// 把你的提示信息放在这里:</em></span>  
    8. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,136)">$message</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">=</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,255)">'My first push notification!'</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">;</span>  

    你应该把设备代码从app里拷贝粘贴到$deviceToken变量。请确保你去掉空格和括弧。把$passphrase的值设为你密钥的密码, $message的值设为你要推送的信息字符串。

    把你的ck.pem文件拷贝到SimplePush文件夹中。记住,ck.pem包含你的证书和密钥。

    打开终端并打入:

    $ php simplepush.php
    

    如果一切顺利,那个脚本应该回应:

    Connected to APNS
    Message successfully delivered
    

    然后几秒之后,你应该收到app的第一个推送信息提示:

    Simple app receiving a Push Notification

    注意,如果你的app是开着的,你就看不到推送的信息。这是因为我们还没有写代码在app里接收推送的信息。你应该关掉app然后重试。

    如果simplepush.php脚本因错误退出,那你因该确保你的PEM文件是正确生成的,并且确保你可以连接到苹果的沙盒服务器(请看上面的详细步骤)。

    我们并不用急着知道这个脚本到底做了什么,因为这是这个教程的下一部分的内容。在下一部分,我们将学习如何编写一个真正的推送服务器。

    展开全文
  • 微信多开助手 for Mac 中文版

    千次阅读 2019-01-21 16:00:36
    微信多开助手 for Mac 中文版 拥有多个微信账号的朋友是不是想要能够在一个平台上登录多个账号呢?微信多开工具for Mac就一款能够让微信双开和多开的小工具。小编为大家带来了最新的微信双开版 Mac版下载,有需要的...
  • MFC实现串口助手

    2021-05-13 23:31:49
    MFC串口助手实现 实现了串口发送接收等基本功能,还需完善。
  • MFC串口助手实现

    千次阅读 2017-10-23 14:32:59
    学习了MFC测试看着别人的例程写了一个串口调试助手, 分享一下我的学习过程,我调用了一个定时器,一个MSCOMM控件,  我实现了自动搜索可打开串口,串口波特率,奇偶校验,各种配置串口功能的实现,没有编辑16位...
  • iOS注册APNs通知

    千次阅读 2016-04-22 16:00:36
    1、什么是通知 消息通知分本地通知和远程推送通知,是没有运行在前台的应用程序可以让它们的用户获得相关消息通知的方式。消息通知可能是一条消息,即将发生的日历事件,或远程服务器的新数据。当被操作系统显示...
  • 我的微信 本章将介绍微信的各种有趣的玩法,用微信玩石头剪刀布、投骰子和发表情等等,让微信真正走入我们的生活,给我们带来欢乐。 5.1 聊天 作为一款手机通信软件,聊天是微信的基础功能,但是作为网络的...
  • Qt实现飞秋拦截助手—Mac地址扫描器 前言 准备好了就开干,利用业余时间,开始着手写 飞秋拦截助手,按照Qt:Qt实现飞秋拦截助手—介绍 中的4步骤来走。4步骤中,Mac地址扫描器是第一步,因为网络传输中 最底层协议 ...
  • 通知

    2012-11-24 12:54:26
    例如,如果你正在为一个医疗等级卡编写设备驱动,你可能需要注册自身接受死亡通知,这样,当内核恐慌发生时,你可以关闭医疗电子。 (2)网络设备通知。当一个网络接口卡启动和关闭的时候被发送。 (3)CPU...
  • Apple推送通知服务教程

    千次阅读 2012-10-14 01:04:45
    Apple推送通知服务教程  生成APP ID和SSL证书 登录iOS Provisioning Portal页面 首先,我们将要新建一个App ID. 每一个推送APP都需要一个唯一的对应的App ID,推送的消息将被送达到这个ID对应的APP应用中...
  • 桌面提醒助手谷歌浏览器插件

    千次阅读 2018-01-16 14:35:05
    最近总是有一些每天需要... // 这个字段将用在安装对话框,扩展管理界面,和store里面,弹出通知的标题 "name": "桌面助手", // 扩展的版本用一个到4个数字来表示,中间用点隔开,必须在0到65535之间,非零数字不能0开
  • 当您开启设备定位功能,下载或使用我们开发的应用程序(例如有礼包APP),我们可能会读取您的位置(大多数移动设备将允许您关闭定位服务,具体方法建议您联系您的移动设备的服务商或生产商)。 b) 您的设备信息。...
  • Wince 串口通讯助手

    2011-05-23 15:35:00
    (SAVE.txt时间保存信息没有成二进制类型保存,额!``建议``还是不要用额``有bug) 发送数据代码 void CSerialPortDlg::OnBnClickedbtnsend() { // TODO: 在此添加控件通知处理程序代码 int ...
  • 通知会短暂地出现在屏幕的右上角或者一直显示在右上角直到您将其关闭。 查看通知 在 Mac 上,将指针移到通知上,然后执行以下任一项操作: 展开或折叠通知叠放:如果 App 的通知已分组,则多个通知会叠放。若要...
  • ios app推送通知详细教程二

    千次阅读 2014-06-08 17:54:12
     按钮,会弹出“Apple推送通知服务SSL证书助手”的页面。 第一件事让你生成一个证书签名的请求,这一步我们已经做过了,所以点击 Continue。 下一步是上传CSR文件。选择在前面步骤中我们已生成好的CSR...
  • 算是地址更新吧这个和之前发的一样大灰苹果微信双开之前也发过一个苹果多开的需要的可以公众号搜来看看稳定苹果防封不闪退高级防封微商助手私密定制 超级防封 转发大视频 24小时后台通知自动抢 兼容iphone 11PRO主要...
  • 更新日期:2021年9月24日 本政策将帮助您了解以下内容... 本人(王建勇)作为"手机程序化操作助手"的开发者,深知用户信息对您的重要性,并会尽全力保护您的用户信息安全可靠。我们致力于维持您对我们的信任,恪守以下...
  • 某些应用程序具有内置更新程序,可以自动下载和安装更新,其他应用程序可以检查更新并至少通知您,还有一些应用程序只需依靠您来检查更新版本。 安全软件(如防病毒应用程序)对于保持更新尤为重要。大多数都内置...
  • 12306火车购票 助手.js

    2012-09-22 17:11:00
    // ==UserScript==// @name 12306.CN 订票助手 For Firefox&Chrome// @namespace http://www.u-tide.com/fish/// @author iFish@FishLee.net <ifish@fishlee.net> http://www.f...
  • 显示和使用"Office 助手"注意 若要执行以下操作,"Microsoft Office 助手"必须打开且可见。若要打开或显示"Office 助手",请按 Alt+H 以打开"帮助"菜单,然后按 O。"Office 助手"可见后,请按 F1 以显示"助手"气球。...
  • 除了通勤路上听,你也可以在睡前听节目来助眠,和音乐App一样拥有睡眠定时功能,设定好自动关闭节目的时间。 02. Apple Music Apple Music,是苹果推出的音乐App,虽然App的名称带着一个Apple,但它也可以在Android...
  • 如何配置iOS推送通知

    2014-02-21 12:00:11
    配置推送通知比较麻烦,需要iOS开发者帐号,然后配置一个Apple ID、配置概要文件和SSL证书。   1 证书签名请求 首先需要证书签名请求,这需要在Mac OS X系统中完成,通过在系统中选择“实用...
  • 1. 项目简介 灵感来源于学校的 每日健康日报,要求使用微信小程序进行每日健康打卡。所以此项目的功能类似于 QQ群机器人,或者是 每日闹钟。...当然,用户可以自定义每日通知的时间,也可以随时开启或关闭每日通知

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,272
精华内容 1,308
关键字:

关闭转转通知助手