Archive for the 'Programming Language' Category

How to exit a Java app gracefully

标题:如何优雅的退出Java程序
本文中信号拦截代码来自下面的博客:
http://twit88.com/blog/2007/09/27/do-a-graceful-shutdown-of-your-java-application-when-ctr-c-kill/

在UNIX下,通常我们结束一个进程,最常用的方式就是使用kill –9 <PID>,这个方法最简单,但是会带来一个问题,程序可能正在执行某些操作,而这些操作不能被突然中断,否则会造成一些资源的泄露,或者数据完整性的问题。

在UNIX下,信号可以被用来解决这个问题,我们应该比较熟悉kill这个命令,通常使用的命令kill –9 中的kill其实不是‘杀死’的意思,而是‘发送’。kill –9 就是把 SIGKILL(数字代码为9)的信号发送到目标进程。在Java中,可以捕获这些信号:

Runtime.getRuntime().addShutdownHook(…);

当我们执行Ctrl+C或者kill命令的时候,信号Handler会被通知到,这个Handler在一个新的线程中(并非主线程),我们可以在这个Handler里面处理资源释放等问题,注意这里的kill没有带参数,其实等同于kill -15,如果使用kill -9就会直接干掉目标进程,资源得不到回收。具体做法可参考上面的链接。

当Java捕捉到SIGTERM信号的时候,它会执行子线程中的shutdown(); 代码,理论上我们可以把资源清理工作放在这里,我们知道,这个信号监听的线程和主线程,分别在处在两个线程中,这里的代码执行完毕后,它并不管主线程里面的代码是否执行完毕,直接就把整个进程结束了。假设我们有一个长循环在主线程中,它可能会在循环没有执行完毕就殒命了。这个不是我们想要的,解决也很简单:在两个线程中增加同步机制就可以了。

尝试一:
设置一个标志位(notifiedMainThreadToExit = false),当收到SIGTERM信号后,在子线程中设置notifiedMainThreadToExit为true,然后sleep(Long.MAX_VALUE),主线程轮询这个标识位,一旦发现该变量为true,就退出循环,并且释放资源。

问题出现了:主线程的确退出了,可是子线程一直长眠,这个会导致整个进程都不会退出。后来我在主线程退出前,执行System.exit,问题依旧。理论上:System.exit执行后,它会结束该虚拟机中的所有线程,不知道为什么在这里行不通。

尝试二:
设置两个标识位(notifiedMainThreadToExit = false, notifiedSignalHandlerToExit = false),当子线程收到信号后,它通知主线程(set notifiedMainThreadToExit = true):“我收到SIGTERM信号了,你可以准备收工了”,然后,子线程并不马上退出,也不永久休眠,而是轮训等待主线程通知自己。当主线程接受到子线程的通知后,退出循环,然后通知子线程(set notifiedSignalHandlerToExit = true):“我已经准备好了,你也可以收工了”,这样两个线程都可以正常的退出。

结论:
尝试二是可行的,且两个线程间不会产生Race Condition,因为没有两个线程同时写的问题,所以很安全。这里可以下载例子。最后提醒,文章提到的kill,是不带参数的,不要用kill -9。

[ad]

基于文件系统的生产者和消费者问题

周末的时候和team讨论了下如何用最简单的方式,提高数据文件的单位时间传输吞吐量。下面是一个简单的应用场景:

一个目录(DIR1),有很多Producer向这个目录里面放文件,同时有很多的Consumer负责从这个目录里面消费这些文件,插入数据库或者做其他的操作,然后删除或者移走这些文件。

假设条件:

  • 一个文件中转目录DIR1,这个目录位于一个网络存储上
  • 一个生产者,每一秒钟向DIR1里面放一个文件
  • 若干个消费者,假设有8个,其实是8个不同的服务器,都可以访问DIR1,多台服务器可以起负载均衡的作用,任何一台或者几台出问题,整个数据流不会中断
  • 解析一个文件大约需要2-14秒
  • 最后一点:位于网络存储上的目录DIR1,我们认为它是不会出问题的,它不是这里的问题核心

这个场景很普遍,很多公司大概都会用到,尤其是那么比较老的系统(Legacy System),下面是两种方案:

方案一

Consumer循环扫描DIR1,一旦发现有文件,循环解析这些文件,这里有8台服务器,也就是说有8个Consumer一起这样做。代码如下:

	public void run() {
		System.out.println("Created consumer:" + threadName);

		while (true) {
			File file = new File(Constant.STAGING_FOLDER);
			File files[] = file.listFiles();
			for (int i = 0; i < files.length; ++i) {
				File f = files[i];
				parse(Constant.STAGING_FOLDER + "/" + f.getName());
			}

			Commons.sleep();
		}
	}

看起来很简单,可是上面的代码效率非常的差,多个Consumer有很大的几率拿到相同的文件,当某个Consumer尝试去解析一个文件时,却发现这个文件已经被别的Consumer解析过了,并且文件也都删除或者移走了。这样浪费的很多的CPU时间。

可以用下面的方案来替代:

方案二

	public void run() {
		System.out.println("Created consumer:" + threadName);

		while (true) {
			File file = new File(Constant.STAGING_FOLDER);
			File files[] = file.listFiles();

			int nCapacity = files.length > Constant.CAPACITY ? Constant.CAPACITY
					: files.length;
			System.out.println(this.threadName + " found " + nCapacity
					+ " files");

			for (int i = 0; i < nCapacity; ++i) {
				File f = files[i];
				f.renameTo(new File(Constant.TMP_FOLDER + "/" + f.getName()));
			}

			for (int i = 0; i < nCapacity; ++i) {
				parse(Constant.TMP_FOLDER + "/" + files[i].getName());
			}

			Commons.sleep();
		}
	}

它和方案一的不同之处在于:它每次扫描完目录后,最多只取前若干个文件,这里是10个。并且,它不急于去处理文件,而是把文件马上移动到一个临时工作目录,其他的的操作都是相同的。

对于这个方案,有个附加条件:这个临时工作目录tmp,一定要和staging目录在同一个文件系统(filesystem),这样的话,mv操作就只是修改一下inode,几乎瞬间完成。

比较(Benchmarking)

为了测试两中方案的效率差别,我写了一个模拟程序(http://googlestop.com/download/SimConsumer.7z),它有7个class:

  1. App.java - 程序入口
  2. Commons.java - 共享的函数
  3. Constant.java – 配置参数
  4. Producer.java - 生产者,每隔一秒向目录staging里丢一个文件
  5. AbstractConsumer.java – 抽象消费者,定义消费者的一些基本属性和行为
  6. Consumer1.java - 具体消费者,实现方案一
  7. Consumer2.java - 具体消费者,实现方案二

在App.java中,你可以指定调用Consumer1还是Consumer2。

对于前者(Consumer1),staging目录下的文件数目不停的增长,并且如log显示,有很多冲突:一个Consumer准备处理的文件已经被其他的Consumer处理完了,造成了很多无效的操作,由于消费速度更不上生产速度,DIR1被撑爆只是时间的问题。

对于后者(Consumer2),staging目录下的文件几乎马上就会被移动到tmp目录下,大部分时间,文件数都为0。而tmp目录下,在程序稳定后大概保存在20多个文件左右,保持一个动态的平衡。用这种方式,你也会看到很多冲突,但是只会发生在程序刚开始,原因是,刚开始的时候,8个线程几乎是同时去访问staging目录,势必拿到很多相同的文件,待到稳定后,就很少有冲突发生了。

这两种方案都是最基本的,没有借助于第三方工具完成的,成本是最低的,其实还有一些其他的方案,可能会借助一些服务来实现,比如消息分发、数据库等。有时间的话,我继续补充。

[ad]

NoSuchMethodError of BeanUtils.copyProperty(due to wrong access level)

为了节省开发时间,今天打算做一个数据集合类,可以直接将java中的ResultSet,或者其他Collection派生类的内容copy到该集合中,然后加入自定义的一些方法,比如支持直接导出Excel、CSV、KDF、Image、HTML等。这里借助Apache Commons BeanUtils的和反射(Reflection),将数据库中的一行记录保存为一个对象,然后插入数据集中,结果老是报错如下:

USING CONVERTER org.apache.commons.beanutils.converters.IntegerConverter@1befab0
java.lang.reflect.InvocationTargetException: Cannot set id
at org.apache.commons.beanutils.BeanUtilsBean.copyProperty(BeanUtilsBean.java:449)
at org.apache.commons.beanutils.BeanUtils.copyProperty(BeanUtils.java:129)

… …
Caused by: java.lang.NoSuchMethodException: Property ‘id’ has no setter method
at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:1746)
at org.apache.commons.beanutils.BeanUtilsBean.copyProperty(BeanUtilsBean.java:447)

代码段如下:

private void dumpResultSet(ResultSet rs, Class clazz) throws Exception {
	ResultSetMetaData metaData = (ResultSetMetaData) rs.getMetaData();
	int colCnt = metaData.getColumnCount();
	Field[] fields = clazz.getDeclaredFields();

	while (rs.next()) {
		Object newInstance = clazz.newInstance();

		for (int i = 1; i <= colCnt; i++) {
			try {
				Object value = rs.getObject(i);
				for (int j = 0; j < fields.length; j++) {
					Field f = fields[j];
					if (f.getName().equalsIgnoreCase(
							metaData.getColumnName(i).replaceAll("_", ""))) {

						log.info("f.getName:" + f.getName());

						BeanUtils.copyProperty(newInstance, f.getName(),
								value);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		list.add(newInstance);
	}
}

The root cause: public is required for class UserInfo, or else you’ll get the ‘NoSuchMethodError‘ exception, DO NOT ignore the access level

public class UserInfo {
	private int id;
	private String user;
	private String password;

	public UserInfo() {
	}

	public String getUser() {
		return user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}

Eclipse一直提示:NoSuchMethodError,可是UserInfo里面明明有对应的setter方法,最后,才鬼使神差的发现,只要将UserInfo整个类声明为public就可以了。我一直都把注意力放到是否把setter和getter设置为public,却忽略了Bean的访问级别,希望碰到类似问题的朋友可以注意一下。

[ad]

A XFire error and solution

when i tried to deploy my web service with XFire, i got the following error message:

Exception in thread "main" org.codehaus.xfire.annotations.AnnotationException: Service class cannot be abstract: com.webserviceproject.xifre…

the root cause is mis-matched dependency libs, for my case, i’ve imported xfire-annotation-1.2.6.jar and xfire-annotation-1.1.1.jar at the same time. coz i used the wrong pom.xml as below:

        <dependency>
            <groupId>org.codehaus.xfire</groupId>
            <artifactId>xfire-jaxb2</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.xfire</groupId>
            <artifactId>xfire-spring</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.xfire</groupId>
            <artifactId>xfire-java5</artifactId>
            <version>1.1.1</version>
        </dependency>
       
      <dependency>
            <groupId>org.codehaus.xfire</groupId>
            <artifactId>xfire-all</artifactId>
            <version>1.2.6</version>
        </dependency>

see, actually i’ve imported XFire lib twice(1.1.1 & 1.2.6), that’s the reason why the Java complains, we just need the dependency in green.

good luck.

ASP.net 发布的小问题

Exception Details: System.Data.SqlClient.SqlException: Failed to update database “C:InetpubwwwrootSSRAPP_DATAASPNETDB.MDF” because the database is read-only.

上面的例子通常发生在publish一个新开发的web application的时候,解决方法很简单:即修改App_Data这个目录的Security属性,加入用户”NETWORK SERVICE”,并设置该用户可以对这个目录有Write权限。

[ad]

C# LDAP Wrapper

Before you try to run the following code, please download Novel LDAP lib:

http://forge.novell.com/modules/xfcontent/downloads.php/ldapcsharp/ldapcsharp/CsharpLDAP-v2.1.10/

using System;
using System.Collections.Generic;
using System.Text;
using Novell.Directory.Ldap;

namespace LDAPUtility
{
    class LDAPUtil
    {
        private string ldapHost = "ssuzdc3";
        private int ldapPort = 389;
        LdapConnection ldapConn = null;

        private void Connect()
        {
            try
            {
                // Creating an LdapConnection instance
                ldapConn = new LdapConnection();

                // Connect function will create a socket connection to the server
                ldapConn.Connect(ldapHost, ldapPort);

                // Bind function with null user dn and password value will perform anonymous bind
                // to LDAP server
                ldapConn.Bind(null, null);
            }
            catch (Exception e)
            {
                // failed to connect to server
            }
        }

        private void Disconnect()
        {
            ldapConn.Disconnect();
            ldapConn = null;
        }

        public string GetSupervisor(string id)
        {
            Connect();

            string boss = "";
            try
            {
                LdapSearchResults lsc = ldapConn.Search("OU=Users,OU=Suzhou,DC=charry,DC=org",
                LdapConnection.SCOPE_ONE,
                "sAMAccountName=" + id,
                null,
                false);

                while (lsc.hasMore())
                {
                    LdapEntry nextEntry = null;
                    try
                    {
                        nextEntry = lsc.next();
                    }
                    catch (LdapException e)
                    {
                        // Exception is thrown, go for next entry
                        continue;
                    }

                    LdapAttribute attribute = nextEntry.getAttribute("manager");
                    boss = attribute.StringValue;
                }
            }
            catch (Exception e)
            {
                // exception
            }

            Disconnect();

            return GetAMAcountName(boss);
        }

        private string GetFullName(string id)
        {
            // CN=Wang, Charry,OU=Users,OU=Suzhou,DC=charry,DC=org
            int end = id.IndexOf("OU=");

            id = id.Substring(3, end - 4);
            id = id.Replace("\", "");

            return id;
        }

        // convert distinguished name to AMAcountName
        public string GetAMAcountName(string id)
        {
            id = GetFullName(id);
            Connect();

            string tmp = "";
            try
            {
                LdapSearchResults lsc = ldapConn.Search("OU=Users,OU=Suzhou,DC=charry,DC=org",
                LdapConnection.SCOPE_ONE,
                "displayName=" + id,
                null,
                false);

                while (lsc.hasMore())
                {
                    LdapEntry nextEntry = null;
                    try
                    {
                        nextEntry = lsc.next();
                    }
                    catch (LdapException e)
                    {
                        // Exception is thrown, go for next entry
                        continue;
                    }

                    LdapAttribute attribute = nextEntry.getAttribute("sAMAccountName");
                    tmp = attribute.StringValue;
                }
            }
            catch (Exception e)
            {
                // exception
            }

            Disconnect();

            return tmp;
        }

        public string GetDisplayName(string id)
        {
            Connect();
            string name = "";
            // get fullname

            try
            {
                LdapSearchResults lsc = ldapConn.Search("OU=Users,OU=Suzhou,DC=charry,DC=org",
                LdapConnection.SCOPE_ONE,
                "sAMAccountName=" + id,
                null,
                false);

                while (lsc.hasMore())
                {
                    LdapEntry nextEntry = null;
                    try
                    {
                        nextEntry = lsc.next();
                    }
                    catch (LdapException e)
                    {
                        // Exception is thrown, go for next entry
                        continue;
                    }

                    
                    LdapAttribute attribute = nextEntry.getAttribute("displayName");
                    name = attribute.StringValue;
                }
            }
            catch (Exception e)
            {
                // exception
            }

            Disconnect();

            return name;
        }

        public string GetEmail(string id)
        {
            Connect();
            string email = "";
            try
            {
                LdapSearchResults lsc = ldapConn.Search("OU=Users,OU=Suzhou,DC=charry,DC=org",
                LdapConnection.SCOPE_ONE,
                "sAMAccountName=" + id,
                null,
                false);

                while (lsc.hasMore())
                {
                    LdapEntry nextEntry = null;
                    try
                    {
                        nextEntry = lsc.next();
                    }
                    catch (LdapException e)
                    {
                        // Exception is thrown, go for next entry
                        continue;
                    }

                    LdapAttribute attribute = nextEntry.getAttribute("mail");
                    email = attribute.StringValue;
                }
            }
            catch (Exception e)
            {
                // exception
            }

            Disconnect();

            return email;
        }

        public static void test()
        {
            string ldapHost = "ssuzdc3";
            int ldapPort = 389;

            try
            {
                // Creating an LdapConnection instance
                LdapConnection ldapConn = new LdapConnection();

                // Connect function will create a socket connection to the server
                ldapConn.Connect(ldapHost, ldapPort);

                // Bind function with null user dn and password value will perform anonymous bind
                // to LDAP server
                ldapConn.Bind(null, null);

                // Searches in the Marketing container and return all child entries just below this
                // container i.e. Single level search
                LdapSearchResults lsc = ldapConn.Search("OU=Users,OU=Suzhou,DC=charry,DC=org",
                LdapConnection.SCOPE_ONE,
                "sAMAccountName=qinick",
                null,
                false);

                while (lsc.hasMore())
                {
                    LdapEntry nextEntry = null;
                    try
                    {
                        nextEntry = lsc.next();
                    }
                    catch (LdapException e)
                    {
                        Console.WriteLine("Error: " + e.LdapErrorMessage);
                        // Exception is thrown, go for next entry
                        continue;
                    }

                    Console.WriteLine("n" + nextEntry.DN);
                    LdapAttributeSet attributeSet = nextEntry.getAttributeSet();
                    System.Collections.IEnumerator ienum = attributeSet.GetEnumerator();
                    while (ienum.MoveNext())
                    {
                        LdapAttribute attribute = (LdapAttribute)ienum.Current;
                        string attributeName = attribute.Name;
                        string attributeVal = attribute.StringValue;
                        Console.WriteLine(attributeName + "value:" + attributeVal);
                    }
                }
                ldapConn.Disconnect();

            }
            catch (Exception e)
            {
                string x = e.Message;
            }

            Console.Read();
        }
    }
}

[ad]

Problem with JFreechart

一个基于Tomcat的程序里面用到了JFreeChart,偶尔用浏览器访问时,PC端安装的XManager的XServer会自动的打开,然后Tomcat就莫名其妙的挂掉。在没有安装XManager的PC上通常就不会出这种情况。查看一下log,如下:

Can’t connect to X11 window server using ‘:0.0′ as the value of the DISPLAY variable.

搜之,此乃awt的bug。加入以下参数启动即可:

-Djava.awt.handless=true

如果是Tomcat,把上面的参数加到环境变量:CATALINA_OTPS里即可。

[ad]

Upload the 3rd party artifact to local Archiva

Maven is a powerful build tool, more and more developers migrate to it, so do I. Below is the command for deploy the 3rd artifact to your local Archiva:

mvn deploy:deploy-file -DrepositoryId=internal -Durl=http://ssuzsws02:8080/archiva/repository/internal -DgroupId=com.amd.sws -DartifactId=mysql-connector-java -Dversion=3.0.17 -Dpackaging=jar -Dfile=test.jar

but before you issue this command, you should add the following lines to your local settings.xml

<servers>
  <server>
   <id>internal</id>
   <username>admin</username>
   <password>mypassword</password>
  </server>
</servers>

FYI.: when you upload your jar file to Archiva, you can’t see it immediately, please be patient and wait several minutes, the artifact list will be synced soon.

Hibernate, SocketTimeOutException错误

项目中用到Hibernate,部署的时候发现,过了一段时间后,Hibernate就不能正常工作了,时间很有规律,通常是在部署后的若干个小时。错误日志如下:

** BEGIN NESTED EXCEPTION **

java.net.SocketTimeoutException
MESSAGE: Read timed out

STACKTRACE:

java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(Unknown Source)
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:1539)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:1930)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1168)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1279)
    at com.mysql.jdbc.MysqlIO.sqlQuery(MysqlIO.java:1225)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:2278)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:2237)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:2218)
    at com.mysql.jdbc.Connection.commit(Connection.java:1155)
    at org.hibernate.transaction.JDBCTransaction.commitAndResetAutoCommit(JDBCTransaction.java:139)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:115)
    at com.amd.BizB.test(BizB.java:27)
    at org.apache.jsp.index_jsp._jspService(index_jsp.java:109)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:393)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Unknown Source)

最后发现是由于配置文件Hibernate.cfg.xml没有写好,少加了若干的项(注意黑体部分)

<property name=”connection.autocommit”>true</property>

<property name=”connection.url”>jdbc:mysql://ssuzsws01:3306/foo?autoReconnect=true</property>

加了这些选项后,就正常了,希望对遇到类似问题的朋友有帮助。

为什么不用SEH

前些时候,在论坛上看到一个朋友说SEH怎么怎么不好,一定不要用它。其实存在即合理。就像GOTO,不能因为它破坏了程序的流程,就不用它,适当的使用,还是可以事半功倍的。

大家知道SEH是Windows操作系统提供的一种异常处理机制,它和C++无关。在Compiler编译的时候,就把这个机制加入了我们的程序中。在VC下可以用__try, __finally, __except, __leave等关键字来标识。由于SEH可以捕获硬件异常(Hardware Exception)和软件异常(Software Exception),它比C++的异常机制能捕获更多的异常,所以有朋友不喜欢这点,认为它掩盖了错误。其实这种说法是也是合情合理的,毕竟掩盖错误不是最好的解决方案,找出问题的所在才是我们应该做的。可是在现实中,我们不可能找到所有的bug,或者由于时间的关系,来不及修补这个bug,不如先用SEH挡一挡,何尝不可。

就像我之前的一个项目,程序在一个地方偶尔会Crash掉,而且这个地方如果不能正常执行丝毫不影响整个程序的运作,不会对用户造成损失,在找出问题真正的原因之前,我们完全可以用SEH捕获异常。

 下面的例子也是一个SEH优势的体现

BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult)
{
    __try
    {
        *pResult = dividend / divisor;
    }
    __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ?
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        return FALSE;
    }
    return TRUE;
}

SafeDiv用来做除法操作,它的返回值指出函数是否执行成功。pResult指向最终的结果,如果不用SEH,这类Hardware Exception会导致程序Crash,这里引入了SEH,我们在发现有除零错误的时候,让函数返回FALSE,调用处通过检查函数的返回值就可以判断除法运算是否成功,没必要就因为一个除零错误导致程序Crash掉。

由于SEH是Windows操作系统特有的机制,所以它不适合用在那些跨平台的代码,这种情况不用也罢,既然SEH是一个Windows的一个很好的异常处理机制,我们虽不能滥用它,适当的、合理的使用还是值得推荐的。

P.S.
For details about SEH, check this: http://www.google.com/search?hl=en&q=Programming+Applications+for+Microsoft+Windows

Page 1 of 3123

Switch to our mobile site