基于xClient+Windows计划任务的断网重连程序实现(C#)

实现思路:

添加Windows计划任务,每隔一分钟执行一次编写的程序。在该程序中,首先判断网络是否正常。若正常,则关闭程序;若不正常,则重新打开xClient程序,并触发其“触发认证”按钮及隐藏窗口,而后关闭本程序。

1、在VS2008中,创建控制台应用程序。

2、Program.cs

[csharp]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;

namespace AlwaysOnline
{
class Program
{
[DllImport(“user32.dll”, EntryPoint = “FindWindow”, CharSet = CharSet.Auto)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport(“user32.dll”, EntryPoint = “FindWindowEx”, CharSet = CharSet.Auto)]
extern static IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport(“User32.dll”, EntryPoint = “SendMessage”)]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);

static void Main(string[] args)
{
if (!isNetConnected()) connect();
}

///

/// 打开xClient,触发认证,并隐藏窗口
///

private static void connect()
{
string path = “D:\\Program Files\\xClient\\xClient.exe”;
Process p = Process.Start(path);
if (p == null)
Console.WriteLine(“Warning:process may already exist”);

Console.WriteLine(“Finding main window handle”);
IntPtr mainWindows = FindMainWindowHandle(“xClient 802.1x 客户端”, 100, 25);
Console.WriteLine(“Handle to main window is ” + mainWindows);

//有名字控件句柄
Console.WriteLine(“Findding handle to button1”);
IntPtr butt = FindWindowEx(mainWindows, IntPtr.Zero, null, “触发认证”); // 找到按钮
IntPtr butt1 = FindWindowEx(mainWindows, IntPtr.Zero, null, “隐藏窗口”); // 找到按钮
if (butt == IntPtr.Zero)
throw new Exception(“Unable to find button1”);
else
Console.WriteLine(“Handle to button1 is ” + butt);
//SendMessage(mainWindows, 0X101, butt, null);
SendMessage(butt, 0x201, butt, null); // 左键按下
SendMessage(butt, 0x202, butt, null); // 左键弹起
SendMessage(butt1, 0x201, butt1, null); // 左键按下
SendMessage(butt1, 0x202, butt1, null); // 左键弹起

//没有名字或者重名控件
//Console.WriteLine(“Findding handle to listbox1”);
//IntPtr lb = FindWindowByIndex(mwh, 3);
//if (lb == IntPtr.Zero)
// throw new Exception(“Unable to find listbox1”);
//else
// Console.WriteLine(“Handle to listbox1 is ” + lb);
}

///

/// 网络是否已连接
///

///
private static bool isNetConnected()
{
Ping p = new Ping();
PingReply pr1 = p.Send(“8.8.8.8”, 5000);
return pr1.Status == IPStatus.Success;
}

//通过索引查找相应控件句柄
static IntPtr FindWindowByIndex(IntPtr hwndParent, int index)
{
if (index == 0)
{
return hwndParent;
}
else
{
int ct = 0;
IntPtr result = IntPtr.Zero;
do
{
result = FindWindowEx(hwndParent, result, null, null);
if (result != IntPtr.Zero)
{
++ct;
}
} while (ct < index && result != IntPtr.Zero); return result; } } //获得待测程序主窗体句柄 private static IntPtr FindMainWindowHandle(string caption, int delay, int maxTries) { IntPtr mwh = IntPtr.Zero; bool formFound = false; int attempts = 0; while (!formFound && attempts < maxTries) { if (mwh == IntPtr.Zero) { Console.WriteLine("Form not yet found"); Thread.Sleep(delay); ++attempts; mwh = FindWindow(null, caption); } else { Console.WriteLine("Form has been found"); formFound = true; } } if (mwh == IntPtr.Zero) throw new Exception("Could not find main window"); else return mwh; } } } [/csharp] 3、因为程序每分钟要触发一次,而控制台应用程序默认会显示黑色命令行窗口,因而为了在程序运行时不显示该窗口,参看本博客另外一篇文章《C# 控制台应用程序如何不弹出窗口》。

4、添加Windows计划任务

1) 将上一步生成的程序保存到某个位置,而后在控制面板中添加计划任务。

2) 在计划任务中,选择生成的程序,并选择“每天”执行,起始时间设置为“00:00”;

3) 创建该计划任务完成后,右键该任务选择属性,在高级选项中,做如下设置

面向对象的一些新理解

很多人对面向对象都非常苦恼。

面向对象,说简单也简单,几乎每个编程人员都知道如何new一个对象,如何调用对象提供的方法,如何自己编写一个简单对象。

但大部分人都知道,面向对象应该不仅如此。

几乎没“亲自”使用过继承,几乎没亲自定义过接口…

为什么?

其实,原因不是对这些知识的不了解,而是“恐惧心理”在作祟。

害怕用的不对,害怕用的不规范…

其实,无需将面向对象看成是“束缚”,而是要将其看作“工具”——编程语言提供给我们的工具。

面向对象的使用,没有所谓的好坏。所谓的设计模式也仅仅是“经验之谈”,莫要成为枷锁。

最后,给大家的忠告:

在你觉得需要继承的时候,就去继承;在你觉得需要使用接口的时候,就去使用接口…实践出真知!

如何在网页中启动本地应用程序

说到单点登录,往往是和Portal(门户)是离不开的。通常企业中会有许多应用,WEB的或CS的。而做Portal的时候往往是做成WEB的。这时候,用户登录Portal后,如何从Portal启动本地的CS程序,就成为需要解决的问题。
不知道大家是如何解决的,我的做法是,自己实现一个协议(就象迅雷/电驴/网络蚂蚁那样),在Portal上实现一个形如
[html]
协议名称://应用名称/作业?action=动作&param1=参数1&param2=参数2…
[/html]
这样的例子可能是(我们协议名是用公司简称,这里我就用foo):
[html]
<a href=”foo://erp/order?action=query&owner=hydonlee”>我的订单</a>
[/html]
通过这样的设计,让浏览器象处理http协议的链接一样,把请示发送给我们的应用。
那如何让浏览器将这个链接发送给我们的协议处理器呢?这就需要向系统中注册一下(Windows下),注册表如下:

[csharp]
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\foo]
@=”URL: foo Application 协议”
“URL Protocol”=””

[HKEY_CLASSES_ROOT\foo\DefaultIcon]
@=”d:\\workspace\\fooPortal\\bin\\client\\foo.protocolhandler.exe,1″

[HKEY_CLASSES_ROOT\foo\shell]

[HKEY_CLASSES_ROOT\foo\shell\open]

[HKEY_CLASSES_ROOT\foo\shell\open\command]
@=”\”d:\\workspace\\fooPortal\\bin\\client\\foo.protocolhandler.exe\” \”%1\””

[/csharp]
怎么样?是不是很简单?其实将这个加入注册表之后,你可以开始->运行,输入:foo://test/ 回车,来测试你的协议处理器了!windows系统会把网址作为参数提供给命令行。
注册协议处理器的C#代码如下,我是写在协议处理器的类中的:

[csharp]
private void FooProtocolRegister() {
// copyright(c) hydonlee, 转载请注明原址
RegistryKey cr = Registry.ClassesRoot;

RegistryKey Fookey = cr.CreateSubKey(“Foo”);

//添加Foo键
Fookey.SetValue(“”, “URL: Foo Application 协议”);
Fookey.SetValue(“URL Protocol”, “”);

//添加DefaultIcon
RegistryKey iconKey = Fookey.CreateSubKey(“DefaultIcon”);
iconKey.SetValue(“”, string.Format(“{0},1”, Application.ExecutablePath.ToLowerInvariant()));

//添加Shell Key
RegistryKey shellKey = Fookey.CreateSubKey(“shell”);
RegistryKey openKey = shellKey.CreateSubKey(“open”);
RegistryKey commandKey = openKey.CreateSubKey(“command”);
commandKey.SetValue(“”, string.Format(“\”{0}\” \”%1\””, Application.ExecutablePath.ToLowerInvariant()));

Fookey.Close();
}

[/csharp]
这样,由浏览器的链接,已经传递到我们本地的应用中了,剩下的事情就比较简单了。协议处理器分析这个地址,呼叫相应的作业插件,并将参数传入。
简单来说就是:通过协议地址模型,Portal生成链接->浏览器发起请求->协议处理器分派–>各应用插件启动作业

Zend Framework利用Zend_Cache生成页面缓存(页面静态化)

One of the things I’m always looking for is ways to improve performance with the applications I write. While a few applications are write-heavy, most are read-heavy: that is, reading the database is the predominant behavior (for example, this WordPress blog reads the database far more often than it writes to the database). Additionally, Zend Framework is (comparatively) slow at handling requests, offering a throughput of about 67 requests per second on my machine, while loading static pages came in at a whopping 750 requests per second.*

So, given this performance difference, how do we improve the performance of Zend Framework while still retaining its functionality and ease-of-use? Well, we employ caching, of course!

But not just any caching. One of the beauties of a read-heavy website, especially one that doesn’t change all that often, is that we have the ability to cache entire pages and serve them directly using our web server. In Zend Framework 1.10.0, Zend_Cache_Frontend_Capture and Zend_Cache_Backend_Static were introduced, giving us the ability to take entire pages produced by Zend Framework and cache them. This means that we get the ability to use Zend Framework and all of its framework-y goodness, while still having the ability to enjoy the performance of static HTML pages served by our webserver. Excellent.

When devising my proof of concept, however, I found that implementing these components is more difficult than it looks. This is in part because the documentation is lacking, and also in part that the documentation in some spots is wrong. But after a week of searching and a journey that consisted of reading a Jira ticket, filing one of my own, dealing with imperfect documentation, asking questions in #zftalk on Freenode, bugging Matthew Weier O’Phinney to the point where I’m sure he made a voodoo doll of me, bugging Pádraic Brady about the cache, and good old-fashioned trial and error, I’ve mastered the implementation of static whole-page caching in Zend Framework, and here is a tutorial of how to do it yourself.

Standard Disclaimer
This tutorial implements code found in the latest version of Zend Framework at the time it was written. That means Zend Framework 1.10.3. Future releases of Zend Framework may, from time to time, change the behavior of components discussed here. Any changes should be reviewed in the documentation.

Additionally, where caching is concerned, it’s never a good idea to cache authenticated pages. It’s also never a good idea to cache data that changes a lot. Finally, it’s never a good idea to cache pages that change based on inputs, like pages that you access via POST or PUT requests.

Getting Started
First things first: let’s talk a little bit about Zend Framework’s caching model and how Zend_Cache_Frontend_Capture and Zend_Cache_Backend_Static are different.

With most Zend caches, you can implement them using the factory() method – in fact, the documentation warns against doing it any other way. So, to implement a frontend file cache using an APC backend, you can do the following:
[php]
Zend_Cache::factory(‘File’, ‘APC’, $frontOps, $backOps);
[/php]
With the implementation of the Zend_Cache_Manager in 1.10, you can register your cache with the manager, and then access it directly from your controllers. However, if you try to implement the Zend_Cache_Frontend_Capture or Zend_Cache_Backend_Static caches in this fashion, it blows up entirely, and will ruin your day. This is because these caches (collectively known as the Static Cache) are designed to serve files directly from the webserver once the file is cached; this means two things in particular: first, the static cache’s ID is the request URI (which in turn is turned into hexadecimal to comply with Zend_Cache’s rules on IDs), and second, because in order to capture the data, the cache uses output buffering.

Therefore, implementation of the static cache is done through the application.ini file, as a resource plugin. Developers wishing to implement this cache must include the following lines in their application.ini files:
[php]
; Custom Caches (Adjustments To Default CacheManager)

resources.cacheManager.page.backend.options.public_dir = APPLICATION_PATH “/../public/cached”
resources.cacheManager.pagetag.backend.options.cache_dir = APPLICATION_PATH “/../data/cache/tags”
[/php]
This means that all of the cached data is stored in the public/cached directory; all the cache’s tags are stored in data/cache/tags. These paths are by no means the only paths, but you must specify a path for both in order for the cache to work properly.

Why do you need to specify a separate tags directory? Due to the fact that files are served directly off the web server, rather than through PHP, the tags are stored separately in another cache. This defaults to a file cache, and you must specify another location for the files to be stored. The static cache utilizes an internal cache which is transparent to you in every other way.

There is one additional INI setting we must employ. In order to operate properly, the static cache employs output buffering and captures that output, writing it to disk and then serving it to the end user. Zend Framework also employs output buffering, which if not turned off, will interfere with the static cache. This was a hangup for me, since it’s not mentioned anywhere, and was something I discovered quite by accident. In order to turn off Zend Framework’s standard output buffering, we need to include the following INI directive:
[php]
resources.frontController.params.disableOutputBuffering = true
[/php]
This directs the front controller to turn off output buffering, which allows the static cache to handle it.

The last thing we need to do is create the directories where the cache will store its files, and make them owned by the web server user. While the static cache will create its own directory to store the cached static files (if it doesn’t exist), the file cache will throw an exception.

At this point, the file cache is ready to go. It’s configured, we’ve created the directories, we’ve turned off output buffering, and we’re not ready to get into caching files.

Caching Output

Zend Framework now has a built in cache helper which we’ll use to cache our static content. This needs to be done in the init() method (from my tests), and should list all the actions on the page you want to cache. Your controller should look similar to this:
[php]
< ?php class IndexController extends Zend_Controller_Action { public function init() { $this->_helper->cache(array(‘index’), array(‘indexaction’));
$this->_helper->cache(array(‘viewpage’), array(‘viewpageaction’));
}

public function indexAction()
{
}

public function viewpageAction()
{
}

public function logoutAction()
{
}
}
[/php]
The argument list for the cache plugin is simple: first, an array of the actions we’re caching, followed by an array of the tags associated with those actions. I’ve listed index and viewpage separately, with different tags, but you can tag multiple actions with the same tags, or break it out as I have. As you develop your application, you’ll want to be careful to not cache actions that are being executed on a POST request, which you can do by using the request object’s isPost() method. Also, in this example, logoutAction() is never cached; this is because we obviously don’t want to cache the results of a log out; we actually want PHP to unset the user’s identity.

Occasionally you may wish to invalidate the cache and remove old files. To do so, you search by tag. For this example, let’s purge the “indexaction” tagged files from the cache:
[php]
$this->_helper->getHelper(‘Cache’)
->removePagesTagged(array(‘indexaction’));
[/php]
The “indexaction” tagged pages will be invalidated and re-cached on the next request.

Directing Apache To Serve Cached Files
The whole point of this process is to serve the cached files at a significant performance improvement, so now we need to make some edits to our .htaccess file’s rewrite rules. The documentation’s rules are slightly incorrect, so let’s devise our own scheme.

My read-heavy sites are usually fairly simple, serving static HTML files rather than XML, OPML, or JSON. Therefore, I need only have a rule for HTML. Additionally, I want to make sure the web server only serves cached files on GET requests, so I’ll include a rewrite condition to help with that.
[php]
RewriteCond %{REQUEST_METHOD} GET
RewriteCond %{DOCUMENT_ROOT}/cached/index.html -f
RewriteRule ^/*$ cached/index.html [L]

RewriteCond %{REQUEST_METHOD} GET
RewriteCond %{DOCUMENT_ROOT}/cached/%{REQUEST_URI}\.html -f
RewriteRule .* cached/%{REQUEST_URI}\.html [L]

RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ – [NC,L]
RewriteRule ^.*$ index.php [NC,L]
[/php]
These rules do a few things: first, if the request goes to the index controller with no arguments (www.example.com/) then it tries to load the index.html cached file. Second, if there are arguments it tries to load the cached file based on the request URI. Finally, for anyone used to using Zend Framework, the last five lines are the same five lines we start with in our Zend Framework default project; these we leave alone to ensure that we run the application if there is nothing in our cache to serve.

Final Notes On Caching
Now that we’ve gotten the cache set up and verified that it works, we can develop our application. However, we obviously want to avoid caching during development, so we can turn caching off by adding the following to our [development : production] section of application.ini:
[php]
resources.cacheManager.page.backend.options.disable_caching = true
[/php]
And that’s it! We can now develop our application with full page caching, getting the performance of a static web server and the flexibility of Zend Framework in the same package.

Good luck!

* The benchmarks cited were performed in the following way: I used Apache Bench, with 3000 requests (none concurrently), to test a stock Zend Framework project, and a flat file of the stock index.php of a Zend Framework project. Both times the files were named with the PHP extension. I did not benchmark the performance of any Zend components. The tests were executed on the same server as the webserver. Apache was restarted after each test. As usual, the standard disclaimers apply and the point of these benchmarks is simply to illustrate a well-known fact: flat HTML files serve faster than parsed PHP code.

原文链接:http://www.brandonsavage.net/caching-for-efficiency-with-zend-framework/

Zend Framework路由器设置方法

路由是个过程,在这个过程中它取出URI的端点(跟着基本URL的URI的那部分)并把它分解成参数来决定哪个模块、哪个控制器和控制器中的哪个动作应该接受请求。模块、控制器、动作和其它参数被打包到Zend_Controller_Request_Http对象,接着这个对象由Zend_Controller_Dispatcher_Standard来处理。路由只发生一次:当请求最初被接收和第一个控制器被派遣之前。

一般地,更新配置文件比修改代码更方便。这个可能通过addConfig()方法来做。基本上,你创建一个Zend_Config-compatible配置,并在你的代码中读入然后传递给RewriteRouter。

本例中,使用INI文件进行路由器配置。下面为具体代码。

[php]
/*
* Bootstrap.php
*/

protected function _initRouter()
{
$router = Zend_Controller_Front::getInstance()->getRouter();
$config = new Zend_Config_Ini(APPLICATION_PATH.’/configs/route.ini’, ‘production’);
$router->addConfig($config, ‘routes’);
}
[/php]

[shell]
[production]
routes.archive.type = “Zend_Controller_Router_Route_Regex”
routes.archive.route = “archive/(\d+)”
routes.archive.defaults.controller = “archive”
routes.archive.defaults.action = “show”
routes.archive.map.1 = “year”
routes.archive.reverse = “archive/%d”
[/shell]

Zend Framework提供的路由器功能十分强大,设置起来非常灵活。对于路由器的各种用法,在Zend Framework参考手册中有非常详细的介绍,大家可以参看:
《标准路由器 – Zend Framework Manual》

Zend Form中如何将英文错误提示设置为中文错误提示(Zend_Form国际化)

可查看Zend Framework的官方参考手册:Internationalization of Zend_Form

Zend_Form表单验证的错误提示均默认为英文,下面介绍如何将英文错误提示设置为中文。

[php]
// application/Bootstrap.php

protected function _initForm()
{
$translate = new Zend_Translate(
array(
‘adapter’ => ‘csv’,
‘content’ => APPLICATION_PATH.’/configs/translation/error-message.csv’,
‘locale’ => ‘zh_CN’,
‘delimiter’ => ‘,’
)
);
Zend_Form::setDefaultTranslator($translate);
}
[/php]

[php]
// application/configs/translation/error-message.csv

“Value is required and can’t be empty”,”此项不可以为空”
“‘%value%’ has not only alphabetic and digit characters”,”‘%value%’不是字母或数字”
“‘%value%’ is an empty string”,”此项不可以为空”
“‘%value%’ has not only alphabetic characters”,”‘%value%’包含非英文字母的字符”
“isValid”,”错误”
“‘%value%’ must contain between 13 and 19 digits”,”‘%value%’必须是一个13到19位之间的数字”
“Luhn algorithm (mod-10 checksum) failed on ‘%value%'”,”Luhn algorithm (mod-10 checksum) failed on ‘%value%'”
“‘%value%’ is not between ‘%min%’ and ‘%max%’, inclusively”,”‘%value%’ 不在 ‘%min%’ 和 ‘%max%’之间”
“‘%value%’ is not strictly between ‘%min%’ and ‘%max%'”,”‘%value%’ 必须大于 ‘%min%’ ,小于 ‘%max%'”
“‘%value%’ is not of the format YYYY-MM-DD”,”‘%value%’ 必须是类似 YYYY-MM-DD 这样的日期格式”
“‘%value%’ does not appear to be a valid date”,”‘%value%’ 是一个错误的日期”
“‘%value%’ does not fit given date format”,”‘%value%’ 日期格式错误”
“‘%value%’ contains not only digit characters”,”‘%value%’ 不是数字”
“‘%value%’ is not a valid email address in the basic format [email=local-part@hostname]local-part@hostname[/email]”,”这不是一个电子邮件地址”
“‘%hostname%’ is not a valid hostname for email address ‘%value%'”,”这不是一个电子邮件地址”
“‘%hostname%’ does not appear to have a valid MX record for the email address ‘%value%'”,”这不是一个电子邮件地址”
“‘%localPart%’ not matched against dot-atom format”,”这不是一个电子邮件地址”
“‘%localPart%’ not matched against quoted-string format”,”这不是一个电子邮件地址”
“‘%localPart%’ is not a valid local part for email address ‘%value%'”,”这不是一个电子邮件地址”
“‘%value%’ exceeds the allowed length”,”‘%value%’不是一个电子邮件地址”
“‘%value%’ does not appear to be a float”,”‘%value%’不是一个浮点数”
“‘%value%’ is not greater than ‘%min%'”,”输入值必须大于 ‘%min%'”
“‘%value%’ has not only hexadecimal digit characters”,”输入值不是一个十六进制字符串”
“Tokens do not match”,”表单已过期,请重新提交”
“No token was provided to match against”,”表单已过期,请重新提交”
“‘%value%’ was not found in the haystack”,”‘%value%’不在可选范围之内”
“‘%value%’ does not appear to be an integer”,”‘%value%’ 不是一个整数”
“‘%value%’ does not appear to be a valid IP address”,”‘%value%’ 不是一个合法的IP地址”
“‘%value%’ is not less than ‘%max%'”,”输入值必须小于 ‘%max%'”
“‘%value%’ does not match against pattern ‘%pattern%'”,”‘%value%’ 不符合输入规则 ‘%pattern%'”
“‘%value%’ is less than %min% characters long”,”‘%value%’ 字符长度小于 %min% 位”
“‘%value%’ is greater than %max% characters long”,”‘%value%’ 字符长度超过 %max% 位”
“Captcha value is wrong”,”验证码错误”
“The two given tokens do not match”,”请勿直接刷新,表单已过期,请重新提交”
[/php]

WORD翻到某一页后卡住并且无法保存的解决办法

  用Word打开一份文件后,只要一翻页就死机,换到别的电脑上也出现这种情况。

  这一般是文件中出现系统不能识别的乱码导致的死机现象。解决方法如下:

  (1)尝试用高版本的Word打开。比如,如果你使用的是Word2007,那么使用Word2010试试。

  (2)您可以用Word重新打开这个文件,不要进行任何编辑操作,用鼠标左键单击菜单中的文件另存为.,在弹出对话框中的保存类型组合框下拉列表中选纯文本,再单击保存按钮。这种操作虽然会将文件原有的格式信息丢失,但文字部分将完全得以保留。

  (3)用Word打开刚保存的纯文本文件,找到文字中的乱码,将其删除,并重新设置文档格式即可。

在移动设备浏览器上利用Bootstrap禁用网页缩放

在移动设备浏览器上,通过为viewport meta标签添加user-scalable=no可以禁用其缩放(zooming)功能。这样禁用缩放功能后,用户只能滚动屏幕,就能让你的网站看上去更像原生应用的感觉。注意,这种方式我们并不推荐所有网站使用,还是要看你自己的情况而定!

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>