<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8742242208674094237</id><updated>2024-09-04T03:38:13.253-07:00</updated><title type='text'>Programming and Computer tips</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default?alt=atom'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default?alt=atom&amp;start-index=26&amp;max-results=25'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>71</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-7795659901949156053</id><published>2009-02-13T18:53:00.000-08:00</published><updated>2009-02-13T18:57:32.682-08:00</updated><title type='text'>EJBs and Servlets</title><content type='html'>EJBs and Servlets&lt;br /&gt;Question:&lt;br /&gt;Both Enterprise JavaBeans and Servlets are server side components. What are the major differences between them? Specifically, what is the difference between a Session Bean and a Servlet?&lt;br /&gt;&lt;br /&gt;Answer:&lt;br /&gt;Enterprise JavaBeans are components meant to encapsulate business logic. They do not handle presentation and have a precise set of restrictions they must obey. An EJB may not manage threads, access files with the java.io package, have a GUI, accept socket connections, or load native libraries. These restrictions are imposed because EJBs execute inside an EJB container, roughly analogous to the way servlets execute in a servlet container. Where servlets can be used to perform many arbitrary tasks, including the generation of HTML pages, EJBs are used almost entirely as business objects. A session bean represents a client for a session or transaction and an entity bean represents a persistent business object that is usually stored in a database. Unlike servlets, a single session bean may exist per client. A single servlet may serve many clients. A session bean usually mediates client access to entity beans, shielding the client from the details of managing state.&lt;br /&gt;&lt;br /&gt;A very common model is for a servlet to act as a client of a session bean. In this multi-tier model, a user talks to a servlet or JavaServer Page through a Web browser. The servlet manages the session, keeping track of a session bean for each of its clients and forwarding transaction requests to the session bean. The session bean may then access any number of entity beans to do things like lookup customer account data. The session bean executes its business logic and returns results to the servlet which then formats a result for the Web browser.&lt;br /&gt;&lt;br /&gt;One of the ideas behind EJBs is to componentize server-side business logic and move it closer to the database. EJBs and servlets will typically not run on the same server. A key thing to remember is that both EJBs and servlets must execute inside of a container. For servlets, this is usually a Web server interfaced with a servlet engine. For EJBs, this is an EJB server such as the reference implementation provided with the Java 2 Enterprise Edition SDK.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/7795659901949156053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/7795659901949156053' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7795659901949156053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7795659901949156053'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2009/02/ejbs-and-servlets.html' title='EJBs and Servlets'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-9183222530086566438</id><published>2008-12-06T16:38:00.000-08:00</published><updated>2008-12-06T16:41:46.590-08:00</updated><title type='text'>What to do if Remote JMS Client can not get factory and throw classcastexception?</title><content type='html'>What to do if Remote JMS Client can not get factory and throw classcastexception?&lt;br /&gt;&lt;br /&gt;First: Add the following property to initialcontext like:&lt;br /&gt;&lt;br /&gt;     props.put(&quot;java.naming.factory.initial&quot;, &quot;org.jnp.interfaces.NamingContextFactory&quot;);&lt;br /&gt;     props.put(&quot;java.naming.provider.url&quot;, &quot;jnp://localhost:1099&quot;);&lt;br /&gt;     props.put(&quot;java.naming.factory.url.pkgs&quot;, &quot;&lt;span style=&quot;font-weight: bold;&quot;&gt;org.jboss.naming:org.jnp.interfaces&lt;/span&gt;&quot;);&lt;br /&gt;&lt;br /&gt;   ctx = new InitialContext(props);&lt;br /&gt;&lt;br /&gt;Second: Check the version of jar file: jboss-messaging-client. It has to have the same version with jms in jboss.&lt;br /&gt;&lt;br /&gt;That&#39;s all</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/9183222530086566438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/9183222530086566438' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/9183222530086566438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/9183222530086566438'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/12/what-to-do-if-remote-jms-client-can-not.html' title='What to do if Remote JMS Client can not get factory and throw classcastexception?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-643836815046883680</id><published>2008-09-25T17:14:00.000-07:00</published><updated>2008-09-25T17:18:04.713-07:00</updated><title type='text'>JMS on JBoss using EJB3 MDB</title><content type='html'>Introduction &lt;div class=&quot;section&quot;&gt; This simple JMS application has you submit a form which sends a message to a JBoss queue. A Message-Driven Bean (MDB) will be listening on that queue destination and receives the mssage and processes it. What makes this lesson different than this one  is that this MDB example runs in an EJB3 container and the MDB is exposed using a simple annotation. To appreciate this simple example, it will really help to view the &#39;old way&#39; of having to do things in the link above. With the EJB3 approach there is a lot less configuration. We don not need to configure the jboss-web.xml, destinations-service.xml, ejb-jar.xml, or the jboss.xml. &lt;/div&gt;  &lt;div class=&quot;sectionHeading&quot;&gt;The Setup&lt;/div&gt;  &lt;b&gt;Download the source&lt;/b&gt;  Download the source above and unzip it into your project directory of choice. For eclipse users the source comes with an eclipse project so you can just import that project file and be good to go. To build using ant, type &#39;ant all&#39; or if you want to build and deploy to jboss type &#39;ant deploy&#39; to build and deploy the ear to JBoss.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java5&lt;/b&gt; The Java5 JDK needs to be set as your environment. JAVA_HOME env variable needs to be defined pointing to yth the JDK home directory.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JBoss 4.2+&lt;/b&gt; - Download JBoss 4.2 here &lt;a href=&quot;http://labs.jboss.com/portal/jbossas/download&quot;&gt;http://labs.jboss.com/portal/jbossas/download&lt;/a&gt;. (This should also run on 4.0.5 if it&#39;s patched to support EJB3 using the JEMS installer.)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Running the example&lt;/b&gt; If you&#39;ve built the project usingg ant (ant deploy), the ear will be deployed to jboss and you simply go to your jboss home dir/bin and start jboss with run.sh (Nix flavors) or run.bat (Windows). If you want to just see the app run you can download the ear above and put the ear into your jboss/default/deploy directory. Once jboss is started, go the following url: http://localhost:8080/rr-jms&lt;br /&gt;&lt;br /&gt;   &lt;div class=&quot;sectionHeading&quot;&gt;LongProcessMessageBean&lt;/div&gt; &lt;div class=&quot;section&quot;&gt;This is our Message Driven Bean. Notice the EJB3 annotations. The annotation tells the container to deploy this as an Message Driven Bean and it also creates the queue based off our annotation. We are also using Resource injection with the @Resource annotation which injects our MessageDrivenContext message which we use if their was some business logic problem and we need to do a rollback in the catch block ( context.setRollbackOnly() .) All this MDB does is mimic calling some business process (doLongProcess). You&#39;ll see in your jboss console the println statements when this gets called. (Of course in real life you&#39;d be using Logging and not stupid println statements.) &lt;pre class=&quot;decorated&quot;&gt;&lt;code&gt;&lt;br /&gt;import javax.annotation.Resource;&lt;br /&gt;import javax.ejb.ActivationConfigProperty;&lt;br /&gt;import javax.ejb.MessageDriven;&lt;br /&gt;import javax.ejb.MessageDrivenContext;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;import javax.jms.MessageListener;&lt;br /&gt;import javax.jms.ObjectMessage;&lt;br /&gt;&lt;br /&gt;import net.learntechnology.dto.ProcessDTO;&lt;br /&gt;import net.learntechnology.util.LongProcessService;&lt;br /&gt;import net.learntechnology.util.ProcessResult;&lt;br /&gt;&lt;br /&gt;@MessageDriven(name=&quot;LongProcessMessageBean&quot;, activationConfig = {&lt;br /&gt;   @ActivationConfigProperty(propertyName=&quot;destinationType&quot;, propertyValue=&quot;javax.jms.Queue&quot;),&lt;br /&gt;   @ActivationConfigProperty(propertyName=&quot;destination&quot;, propertyValue=&quot;queue/examples/OurSampleQueue&quot;)&lt;br /&gt;&lt;br /&gt;})&lt;br /&gt;public class LongProcessMessageBean implements MessageListener {&lt;br /&gt;&lt;br /&gt;   @Resource&lt;br /&gt;   private MessageDrivenContext context;&lt;br /&gt;&lt;br /&gt;   public void onMessage(Message message) {&lt;br /&gt;       String name = null;&lt;br /&gt;       try {&lt;br /&gt;           if (message instanceof ObjectMessage) {&lt;br /&gt;               ObjectMessage objMessage = (ObjectMessage) message;&lt;br /&gt;               Object obj = objMessage.getObject();&lt;br /&gt;               if (obj instanceof ProcessDTO) {&lt;br /&gt;                   name = ((ProcessDTO)obj).getName();&lt;br /&gt;                   System.out.println(&quot;****************************************************&quot;);&lt;br /&gt;                   System.out.println(&quot;LongProcessMessageBean.onMessage(): Received message. NAME: &quot;+name);&lt;br /&gt;                   System.out.println(&quot;****************************************************&quot;);&lt;br /&gt;                   System.out.println(&quot;Now calling LongProcessService.doLongProcess&quot;);&lt;br /&gt;&lt;br /&gt;                   ProcessResult result = LongProcessService.doLongProcess((ProcessDTO)obj);&lt;br /&gt;                } else {&lt;br /&gt;                   System.err.println(&quot;Expecting ProcessDTO in Message&quot;);&lt;br /&gt;               }&lt;br /&gt;           } else {&lt;br /&gt;               System.err.println(&quot;Expecting Object Message&quot;);&lt;br /&gt;           }&lt;br /&gt;           System.out.println(&quot;*******************************************&quot;);&lt;br /&gt;           System.out.println(&quot;Leaving LongProcessMessageBean.onMessage(). NAME: &quot;+name );&lt;br /&gt;           System.out.println(&quot;*******************************************&quot;);&lt;br /&gt;       } catch (Throwable t) {&lt;br /&gt;           t.printStackTrace();&lt;br /&gt;           context.setRollbackOnly();&lt;br /&gt;       }&lt;br /&gt;   }    &lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;div class=&quot;sectionHeading&quot;&gt;index.jsp form&lt;/div&gt; &lt;div class=&quot;section&quot;&gt; A simple JSP page. All it does is submit some text to our Servlet (AppServlet).   &lt;pre class=&quot;decorated&quot;&gt;&lt;form name=&quot;appForm&quot; action=&quot;StartLongProcess&quot;&gt;&lt;br /&gt;   Your name: lt;input type=&quot;text&quot; name=&quot;name&quot; size=&quot;40&quot;&gt;&lt;br /&gt;&lt;br /&gt;   &lt;br/&gt;&lt;br /&gt;&lt;br /&gt;   &lt;input type=&quot;submit&quot; value=&quot;Kick-off The Long Process&quot;&gt;&lt;br /&gt;&lt;/form&gt;&lt;br /&gt;&lt;/pre&gt; &lt;/div&gt;  &lt;div class=&quot;sectionHeading&quot;&gt;AppServlet&lt;/div&gt; &lt;div class=&quot;section&quot;&gt;Our AppServlet takes the request of text submitted from index.jsp and passes it to our JmsProducer. Our JmsProducer will need to know the jndi names of the connection factory and the queue name, so we are defining them here as well and passing them to our JmsProducer class. &lt;pre class=&quot;decorated&quot;&gt;&lt;code&gt;&lt;br /&gt;public class AppServlet extends HttpServlet {&lt;br /&gt;   private static final String QUEUE_CONNECTION_FACTORY = &quot;ConnectionFactory&quot;;&lt;br /&gt;   private static final String LONG_PROCESS_QUEUE = &quot;queue/examples/OurSampleQueue&quot;;&lt;br /&gt;&lt;br /&gt;   public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {&lt;br /&gt;       doPost(request, response);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {&lt;br /&gt;       System.out.println(&quot;in doPost of AppServlet..&quot;);&lt;br /&gt;       String name = request.getParameter(&quot;name&quot;);&lt;br /&gt;       System.out.println(&quot;name: &quot;+name );&lt;br /&gt;       ProcessDTO processDTO = new ProcessDTO();&lt;br /&gt;       processDTO.setName(name);&lt;br /&gt;       JmsProducer.sendMessage(processDTO, QUEUE_CONNECTION_FACTORY, LONG_PROCESS_QUEUE);&lt;br /&gt;       request.setAttribute(&quot;started&quot;,true);&lt;br /&gt;       RequestDispatcher dispatcher =  getServletContext().getRequestDispatcher(&quot;/index.jsp&quot;);&lt;br /&gt;       dispatcher.forward(request, response);&lt;br /&gt;   }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;  &lt;div class=&quot;sectionHeading&quot;&gt;JmsProducer&lt;/div&gt; &lt;div class=&quot;section&quot;&gt;This is our JMS client code that sends the message to the queue. We pushed the Connection lookup and the Destination lookup into another class called &#39;ServiceLocator&#39; which is shown next. &lt;pre class=&quot;decorated&quot;&gt;&lt;code&gt;&lt;br /&gt;public class JmsProducer {&lt;br /&gt;   private JmsProducer() {}&lt;br /&gt;   public static void sendMessage(Serializable payload, String connectionFactoryJndiName,&lt;br /&gt;       String destinationJndiName) throws JmsProducerException {&lt;br /&gt;       try {&lt;br /&gt;           ConnectionFactory connectionFactory = null;&lt;br /&gt;           Connection connection = null;&lt;br /&gt;           Session session = null;&lt;br /&gt;           Destination destination = null;&lt;br /&gt;           MessageProducer messageProducer = null;&lt;br /&gt;           ObjectMessage message = null;&lt;br /&gt;           System.out.println(&quot;In sendMessage of JmsProducter,&lt;br /&gt;               getting ConnectionFactory for jndi name: &quot;+connectionFactoryJndiName );&lt;br /&gt;           connectionFactory = ServiceLocator.getJmsConnectionFactory(&lt;br /&gt;                                                    connectionFactoryJndiName);&lt;br /&gt;&lt;br /&gt;           connection = connectionFactory.createConnection();&lt;br /&gt;           session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);&lt;br /&gt;           destination = ServiceLocator.getJmsDestination(destinationJndiName);&lt;br /&gt;           messageProducer = session.createProducer(destination);&lt;br /&gt;           message = session.createObjectMessage(payload);&lt;br /&gt;           messageProducer.send(message);&lt;br /&gt;           System.out.println(&quot;Message sent to messageProducer&quot;);&lt;br /&gt;           messageProducer.close();&lt;br /&gt;           session.close();&lt;br /&gt;           connection.close();&lt;br /&gt;       } catch (JMSException je) {&lt;br /&gt;           throw new JmsProducerException(je);&lt;br /&gt;       } catch (ServiceLocatorException sle) {&lt;br /&gt;           throw new JmsProducerException(sle);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt; &lt;div class=&quot;sectionHeading&quot;&gt;ServiceLocator&lt;/div&gt; &lt;div class=&quot;section&quot;&gt; This is a simple utility class used to look up our Connection and Destination via a JNDI lookup. In real life we might use a cache as well to store these lookups. &lt;pre class=&quot;decorated&quot;&gt;&lt;code&gt;&lt;br /&gt;public class ServiceLocator {&lt;br /&gt;  private ServiceLocator() {}&lt;br /&gt;    public static ConnectionFactory getJmsConnectionFactory(String jmsConnectionFactoryJndiName)&lt;br /&gt;            throws ServiceLocatorException {&lt;br /&gt;       ConnectionFactory jmsConnectionFactory = null;&lt;br /&gt;       try {&lt;br /&gt;           Context ctx = new InitialContext();&lt;br /&gt;           jmsConnectionFactory = (ConnectionFactory) ctx.lookup(jmsConnectionFactoryJndiName);&lt;br /&gt;       } catch (ClassCastException cce) {&lt;br /&gt;           throw new ServiceLocatorException(cce);&lt;br /&gt;       } catch (NamingException ne) {&lt;br /&gt;           throw new ServiceLocatorException(ne);&lt;br /&gt;       }&lt;br /&gt;       return jmsConnectionFactory;&lt;br /&gt;   }&lt;br /&gt;   public static Destination getJmsDestination(String jmsDestinationJndiName)&lt;br /&gt;           throws ServiceLocatorException {&lt;br /&gt;       Destination jmsDestination = null;&lt;br /&gt;       try {&lt;br /&gt;           Context ctx = new InitialContext();&lt;br /&gt;           jmsDestination = (Destination) ctx.lookup(jmsDestinationJndiName);&lt;br /&gt;       } catch (ClassCastException cce) {&lt;br /&gt;           throw new ServiceLocatorException(cce);&lt;br /&gt;       } catch (NamingException ne) {&lt;br /&gt;           throw new ServiceLocatorException(ne);&lt;br /&gt;       }&lt;br /&gt;       return jmsDestination;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;You might be wondering how to set the IntialContext with the proper host name to do its lookups. The best place to do that is in a jndi.properties file (which this app uses). Our jndi.properties file looks like this: &lt;pre class=&quot;decorated&quot;&gt;&lt;code&gt;&lt;br /&gt;#jndi.properties&lt;br /&gt;java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory&lt;br /&gt;java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces&lt;br /&gt;java.naming.provider.url=localhost:1099&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;/div&gt;  &lt;div class=&quot;sectionHeading&quot;&gt;application.xml&lt;/div&gt; &lt;div class=&quot;section&quot;&gt; Necessary for our ear deployment to jboss. &lt;pre class=&quot;decorated&quot;&gt;&lt;code&gt;&lt;br /&gt;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;br /&gt;&lt;application xmlns=&quot;http://java.sun.com/xml/ns/j2ee&quot;&lt;br /&gt;            xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;br /&gt;            xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/j2ee&lt;br /&gt;                                http://java.sun.com/xml/ns/j2ee/application_1_4.xsd&quot;&lt;br /&gt;            version=&quot;1.4&quot;&gt;&lt;br /&gt;   &lt;display-name&gt;rr-jms-ejb3 Example EAR&lt;/display-name&gt;&lt;br /&gt;  &lt;br /&gt;       &lt;module&gt;&lt;br /&gt;           &lt;web&gt;&lt;br /&gt;               &lt;web-uri&gt;rr-jms-webapp.war&lt;/web-uri&gt;&lt;br /&gt;               &lt;context-root&gt;rr-jms&lt;/context-root&gt;&lt;br /&gt;           &lt;/web&gt;&lt;br /&gt;       &lt;/module&gt;&lt;br /&gt;       &lt;module&gt;&lt;br /&gt;           &lt;java&gt;conf.jar&lt;/java&gt;&lt;br /&gt;       &lt;/module&gt;&lt;br /&gt;       &lt;module&gt;&lt;br /&gt;           &lt;java&gt;common.jar&lt;/java&gt;&lt;br /&gt;       &lt;/module&gt;&lt;br /&gt;  &lt;br /&gt;       &lt;module&gt;&lt;br /&gt;           &lt;ejb&gt;rr-jms-ejb.jar&lt;/ejb&gt;&lt;br /&gt;       &lt;/module&gt;&lt;br /&gt;&lt;/application&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/643836815046883680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/643836815046883680' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/643836815046883680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/643836815046883680'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/09/jms-on-jboss-using-ejb3-mdb.html' title='JMS on JBoss using EJB3 MDB'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-5055083409009671361</id><published>2008-08-03T07:16:00.000-07:00</published><updated>2008-08-03T07:20:51.304-07:00</updated><title type='text'>PHP - How to parse RSS?</title><content type='html'>It is very easy to parse RSS and print out all information of RSS as following&lt;br /&gt;All we need is calling function simplexml_load_string&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;$rss = &#39;&#39;;&lt;br /&gt;$url = &quot;http://www.dantri.com.vn/Sukien.rss&quot;;&lt;br /&gt;$rss_file = file_get_contents($url);&lt;br /&gt;if(!$feed = simplexml_load_string($rss_file)){&lt;br /&gt; die(&quot;Cannot load RSS Feed. This application supports RSS 1.0 and 2.0&quot;);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// print out the title of the feed&lt;br /&gt;echo &#39;&lt;p&gt;&#39;;&lt;br /&gt;echo $feed-&gt;channel-&gt;title;&lt;br /&gt;echo &#39;&lt;/p&gt;&#39;;&lt;br /&gt;// check for RSS Version&lt;br /&gt;$items = ($feed[&#39;version&#39;] != &#39;&#39;) ? $feed-&gt;channel-&gt;item : $feed-&gt;item;&lt;br /&gt;// PRINT OUT ITEMS&lt;br /&gt;/*---------------------------------------------------------------------------------------*/&lt;br /&gt;foreach($items as $item)&lt;br /&gt;{&lt;br /&gt; echo $item-&gt;description.&#39;&lt;BR&gt;&lt;BR&gt;&#39;;&lt;br /&gt;}&lt;br /&gt;/*---------------------------------------------------------------------------------------*/&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/5055083409009671361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/5055083409009671361' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5055083409009671361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5055083409009671361'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/08/php-how-to-parse-rss.html' title='PHP - How to parse RSS?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-8010188764927261336</id><published>2008-05-22T21:17:00.000-07:00</published><updated>2008-05-22T21:26:16.778-07:00</updated><title type='text'>How to log sql statement stored in PreparedStatement</title><content type='html'>&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;Take the following program.&lt;br /&gt;&lt;br /&gt;import java.net.URL;&lt;br /&gt;import java.sql.*;&lt;br /&gt;&lt;br /&gt;class JDBCapp  {&lt;br /&gt;  static MyConnection theConn;&lt;br /&gt;&lt;br /&gt;  public static void main (String args[]) {&lt;br /&gt;    new JDBCapp().doit();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  public void doit() {&lt;br /&gt;    theConn = new MyConnection();&lt;br /&gt;    theConn.connect(&quot;EAS Demo DB V3&quot;, &quot;dba&quot;, &quot;sql&quot;);&lt;br /&gt;&lt;br /&gt;PreparedStatement prepstmt;&lt;br /&gt;try {&lt;br /&gt;  prepstmt = theConn.dbConn.prepareStatement&lt;br /&gt;   (&quot;SELECT emp_id FROM employee&quot; );&lt;br /&gt;  prepstmt.execute();&lt;br /&gt;  prepstmt.close();&lt;br /&gt;  }&lt;br /&gt;catch (Exception e) { e.printStackTrace(); }&lt;br /&gt;  theConn.disconnect();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class MyConnection {&lt;br /&gt;  Connection dbConn = null;&lt;br /&gt;  void connect(String db, String user, String passw) {&lt;br /&gt;    try {&lt;br /&gt;      Driver d = &lt;br /&gt;       (Driver)Class.forName(&quot;sun.jdbc.odbc.JdbcOdbcDriver&quot;).newInstance();&lt;br /&gt;      String URL = &quot;jdbc:odbc:&quot; + db;&lt;br /&gt;      dbConn = DriverManager.getConnection(URL, user, passw);&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;      java.io.PrintWriter w =&lt;br /&gt;        new java.io.PrintWriter&lt;br /&gt;           (new java.io.OutputStreamWriter(System.out));&lt;br /&gt;      DriverManager.setLogWriter(w);&lt;/span&gt;&lt;br /&gt;      }&lt;br /&gt;    catch (Exception e) {&lt;br /&gt;      e.printStackTrace();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  void disconnect() {&lt;br /&gt;    try {&lt;br /&gt;      dbConn.close();&lt;br /&gt;      }&lt;br /&gt;    catch (Exception e) {&lt;br /&gt;      e.printStackTrace();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If we use BasicDataSource to getConnection instead of DriverManager,&lt;br /&gt;use BasicDataSource .setLogWriter.&lt;br /&gt;&lt;br /&gt;In addition, to log these sql strings to log4j, we will redirect these messages from printwriter to log4j. Use the following code to do this.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;LoggerWriter&quot;&gt;http://www.cenqua.com/clover/eg/jboss/report/org/jboss/logging/util/LoggerWriter.html&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/8010188764927261336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/8010188764927261336' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/8010188764927261336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/8010188764927261336'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/05/how-to-log-sql-statement-stored-in.html' title='How to log sql statement stored in PreparedStatement'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-5077506206131470641</id><published>2008-05-21T01:35:00.000-07:00</published><updated>2008-05-21T01:36:36.418-07:00</updated><title type='text'>Show lock on table in oracle and unlock them</title><content type='html'>Show lock&lt;br /&gt;=========&lt;br /&gt;select O.object_name,&lt;br /&gt;O.object_type,&lt;br /&gt;S.sid,&lt;br /&gt;S.serial#,&lt;br /&gt;L.oracle_username,&lt;br /&gt;L.os_user_name,&lt;br /&gt;L.locked_mode&lt;br /&gt;from dba_objects O,&lt;br /&gt;v$locked_object L,&lt;br /&gt;v$session S&lt;br /&gt;where O.object_id = L.object_id&lt;br /&gt;and S.sid = L.session_id&lt;br /&gt;=============&lt;br /&gt;&lt;br /&gt;Unlock&lt;br /&gt;ALTER SYSTEM KILL SESSION &#39;sid, serialid&#39;;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/5077506206131470641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/5077506206131470641' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5077506206131470641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5077506206131470641'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/05/show-lock-on-table-in-oracle-and-unlock.html' title='Show lock on table in oracle and unlock them'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-5801621463944143255</id><published>2008-05-05T23:12:00.000-07:00</published><updated>2008-05-05T23:18:44.059-07:00</updated><title type='text'>Angry with entrecard</title><content type='html'>I am very angry with entrecard system. They considers my site was spam blog. &lt;br /&gt;They said that because entrecard is their system and they want to do what they want.&lt;br /&gt;It is not acceptable. I will make my own system which can be better than entrecard. If we have more competition, i think they will know where they are in internet.&lt;br /&gt;They delete my site but all their ads is still in my site. What do you think about them? They are all bullshit.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/5801621463944143255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/5801621463944143255' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5801621463944143255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5801621463944143255'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/05/angry-with-entrecard.html' title='Angry with entrecard'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-3308969082248913194</id><published>2008-04-12T07:07:00.000-07:00</published><updated>2008-04-12T07:14:43.897-07:00</updated><title type='text'>How to use org.apache.commons.httpclient  and excute method via proxy</title><content type='html'>I try to find out how to use httpclient with proxy and do not want to change java system property. But it seems that we can not find much document about it.&lt;br /&gt;Finally, i have find out the solution:&lt;br /&gt;&lt;br /&gt;First: Create httpclient&lt;br /&gt;HttpClient client = new HttpClient();&lt;br /&gt;&lt;br /&gt;set proxy&lt;br /&gt;client.getHostConfiguration().setProxy(proxyhost, proxyPort);&lt;br /&gt;&lt;br /&gt;//create authenticate &lt;br /&gt;AuthScope auth = new AuthScope(proxyhost,proxyPort, AuthScope.ANY_REALM);&lt;br /&gt;Credentials cred = new UsernamePasswordCredentials(&quot;&quot;,&quot;&quot;);   &lt;br /&gt;   client.getState().setProxyCredentials(auth, cred);&lt;br /&gt;=&gt; now we can connect to internet through proxy.&lt;br /&gt;&lt;br /&gt;If you want more information, please post comment. I will answer.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/3308969082248913194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/3308969082248913194' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/3308969082248913194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/3308969082248913194'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/04/how-to-use-orgapachecommonshttpclient.html' title='How to use org.apache.commons.httpclient  and excute method via proxy'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-1819021389441059822</id><published>2008-03-08T18:41:00.000-08:00</published><updated>2008-03-08T19:32:36.007-08:00</updated><title type='text'>Privacy Policy</title><content type='html'>Privacy Policy&lt;br /&gt;&lt;br /&gt;The privacy of our visitors to http://thegioitinhoccuatoi.blogspot.com is important to us.&lt;br /&gt;&lt;br /&gt;At http://thegioitinhoccuatoi.blogspot.com, we recognize that privacy of your personal information is important. Here is information on what types of personal information we receive and collect when you use visit http://thegioitinhoccuatoi.blogspot.com, and how we safeguard your information. We never sell your personal information to third parties.&lt;br /&gt;&lt;br /&gt;Log Files&lt;br /&gt;&lt;br /&gt;As with most other websites, we collect and use the data contained in log files. The information in the log files include your IP (internet protocal) address, your ISP (internet service provider, such as AOL or Shaw Cable), the browser you used to visit our site (such as Internet Explorer or Firefox), the time you visited our site and which pages you visited throughout our site.&lt;br /&gt;&lt;br /&gt;Cookies and Web Beacons&lt;br /&gt;&lt;br /&gt;We do use cookies to store information, such as your personal preferences when you visit our site. This could include only showing you a popup once in your visit, or the ability to login to some of our features, such as forums.&lt;br /&gt;&lt;br /&gt;We also use third party advertisements on http://thegioitinhoccuatoi.blogspot.com to support our site. Some of these advertisers may use technology such as cookies and web beacons when they advertise on our site, which will also send these advertisers (such as Google through the Google AdSense program) information including your IP address, your ISP , the browser you used to visit our site, and in some cases, whether you have Flash installed. This is generally used for geotargeting purposes (showing New York real estate ads to someone in New York, for example) or showing certain ads based on specific sites visited (such as showing cooking ads to someone who frequents cooking sites).&lt;br /&gt;&lt;br /&gt;You can chose to disable or selectively turn off our cookies or third-party cookies in your browser settings, or by managing preferences in programs such as Norton Internet Security. However, this can affect how you are able to interact with our site as well as other websites. This could include the inability to login to services or programs, such as logging into forums or accounts.&lt;br /&gt;Contact Information&lt;br /&gt;&lt;br /&gt;If users have any questions or suggestions regarding our privacy policy, please contact me through my contact form or send me an email to: testoldfriend(at) yahoo (dot) com.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/1819021389441059822/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/1819021389441059822' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/1819021389441059822'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/1819021389441059822'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/03/privacy-policy.html' title='Privacy Policy'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-4902488188490355682</id><published>2008-03-07T22:20:00.000-08:00</published><updated>2008-03-07T23:31:23.068-08:00</updated><title type='text'>Hibernate, JBoss and auto-increment field</title><content type='html'>&lt;p style=&quot;color: rgb(51, 255, 51);&quot;&gt;&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;&lt;b&gt;The &lt;generator&gt; element&lt;/generator&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot;color: rgb(255, 255, 255);&quot;&gt;&lt;span style=&quot;color: rgb(51, 255, 51);&quot;&gt;This is the optional element under &lt;id&gt; element. The &lt;generator&gt;&lt;param&gt; element is used to specify the class name to be used to generate the primary key for new record while saving a new record. The  element is used to pass the parameter (s) to the  class. Here is the example of generator element from our first application:&lt;br /&gt;&lt;/generator&gt;&lt;/id&gt;&lt;/span&gt;&lt;b style=&quot;color: rgb(51, 255, 51);&quot;&gt;&lt;span style=&quot;color: rgb(0, 0, 255);&quot;&gt;&lt;generator class=&quot;assigned&quot;&gt;&lt;/generator&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 0, 128);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: rgb(0, 0, 128);&quot;&gt;&lt;span style=&quot;color: rgb(51, 255, 51);&quot;&gt;In this case &lt;/span&gt;&lt;generator&gt;&lt;span style=&quot;color: rgb(51, 255, 51);&quot;&gt; element do not generate the primary key and it is required to set the primary key value before calling save() method.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/generator&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;color: rgb(51, 255, 51);&quot;&gt;&lt;span style=&quot;color: rgb(0, 0, 128);&quot;&gt;Here are the list of some commonly used generators in hibernate:&lt;/span&gt;   &lt;/p&gt;        &lt;table bgcolor=&quot;#ffffcc&quot; border=&quot;1&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; width=&quot;80%&quot;&gt;&lt;tbody&gt;&lt;tr&gt;       &lt;td bgcolor=&quot;#ffff99&quot; width=&quot;16%&quot;&gt;&lt;b&gt;Generator&lt;/b&gt;&lt;/td&gt;       &lt;td bgcolor=&quot;#ffff99&quot; width=&quot;84%&quot;&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;increment&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;It generates identifiers of type long, short or int that         are unique only when no other process is inserting data into the same         table. It should not the used in the clustered environment.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;identity&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;It supports identity columns in DB2, MySql, MS SQL Server,         Sybase and HypersonicSQL. The returned identifier is of type long, short         or int.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;sequence&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;The sequence generator uses a sequence in DB2, PostgreSQL,         Oracle, SAP DB, McKoi or a generator in Interbase. The returned         identifier is of type long, short or int&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;hilo&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;The hilo generator uses a hi/lo algorithm to efficiently         generate identifiers of type long, short or int, given a table and         column (by default hibernate_unique_key and next_hi respectively) as a         source of hi values. The hi/lo algorithm generates identifiers that are         unique only for a particular database. Do not use this generator with         connections enlisted with JTA or with a user-supplied connection.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;seqhilo&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;The seqhilo generator uses a hi/lo algorithm to         efficiently generate identifiers of type long, short or int, given a         named database sequence.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;uuid&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;The uuid generator uses a 128-bit UUID algorithm to         generate identifiers of type string, unique within a network (the IP         address is used). The UUID is encoded as a string of hexadecimal digits         of length 32.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;guid&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;It uses a database-generated GUID string on MS SQL Server         and MySQL.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;native&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;It picks identity, sequence or hilo depending upon the         capabilities of the underlying database.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;assigned&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;lets the application to assign an identifier to the object         before save() is called. This is the default strategy if no         &lt;generator&gt; element is specified.&lt;/generator&gt;&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;select&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;retrieves a primary key assigned by a database trigger by         selecting the row by some unique key and retrieving the primary key         value.&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;       &lt;td width=&quot;16%&quot;&gt;foreign&lt;/td&gt;       &lt;td width=&quot;84%&quot;&gt;uses the identifier of another associated object. Usually         used in conjunction with a &lt;one-to-one&gt; primary key association.&lt;/one-to-one&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Therefore,&lt;br /&gt;If your application is EJB, WS which create multiple threads, you should not use class=increment. Instead, you should use class=sequence or native. Or you will&lt;br /&gt;encounter the problem: unique constrain violation.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/4902488188490355682/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/4902488188490355682' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/4902488188490355682'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/4902488188490355682'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/03/hibernate-jboss-and-auto-increment.html' title='Hibernate, JBoss and auto-increment field'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-488202963428033981</id><published>2008-02-15T23:27:00.000-08:00</published><updated>2008-02-15T23:36:33.432-08:00</updated><title type='text'>Java: how to find the absolute path with white space to the resource ?</title><content type='html'>For example: our program contains the resource A and class MyClass&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    src&lt;br /&gt;       main&lt;br /&gt;          java&lt;br /&gt;             MyClass&lt;br /&gt;       resource&lt;br /&gt;          A                 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When we package our program, A will be at root. &lt;br /&gt;To get the absolute path to resource(for example when we want to do unit test&lt;br /&gt;and load those resource for test), we will call &lt;br /&gt;String path = MyClass.getClass.GetResource(&quot;/A&quot;).toURI().getPath();&lt;br /&gt;And now we have the absolute path to resource.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/488202963428033981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/488202963428033981' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/488202963428033981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/488202963428033981'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/02/java-how-to-find-absolute-path-with.html' title='Java: how to find the absolute path with white space to the resource ?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-1249948737978717307</id><published>2008-02-15T22:46:00.000-08:00</published><updated>2008-02-15T23:26:18.647-08:00</updated><title type='text'>JMock and how to mock static function and final class?</title><content type='html'>Unit test is one of the &quot;must&quot; task when we develop software. One of the most technology we usually used is mock. As i already said in one of my previous post,&lt;br /&gt;mock is the powerful technology but they have some limitation. This limitation is depend on which type of mock object( JMock, AMock, NMock ...) or the environment.&lt;br /&gt;For example, if you want to test some DAO class which directly access to DBMS, you can not create mock for all JDBC class to do unit test. And in this case, we also can not do unit test because the test depend on the environment(we have to set up an DBMS just for do unit test). Therefore, we have to chose the most suitable way to test: System test, module test, review code ... &lt;br /&gt;JMock like others mock object, has it own limitation. It can not mock final class, static method. It is because of its implementation. For final class, JMock can not inherit the class to create new mock class ==&gt; can not mock this class.&lt;br /&gt;With static method, because static method can not be overriden, so JMock has no way to create mock object.&lt;br /&gt;&lt;br /&gt;So in this case, we have to think about design and test first before code&lt;br /&gt;1. Case: our method use final class.&lt;br /&gt;&lt;br /&gt;We will use pattern template method to separate code which contain instances of final class.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class OurClass {&lt;br /&gt;    function myFunc() {&lt;br /&gt;        my code&lt;br /&gt;        ......&lt;br /&gt;        URL url = new URL(href); //Can not mock URL because of final class&lt;br /&gt;        url.openstream(...)&lt;br /&gt;        ....&lt;br /&gt;        my code&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;Our mission is test this function.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this case, we create new class calls MockHelperTest and a factory MockHelperFactory&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;IHelperFactory = interface{&lt;br /&gt;    IHelperTest getMockHelper;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;MockHelperTest = class implements IHelperTest{&lt;br /&gt;        &lt;br /&gt;    void funcHelper() {&lt;br /&gt;      ///Prepare data , result and code for mock&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and we change our class to:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class OurClass {&lt;br /&gt;    public IHelperFactory factory = null; //factory can be set by unit test&lt;br /&gt;    &lt;br /&gt;    //If we test this function, we will &lt;br /&gt;    //pass mock factory to this class and &lt;br /&gt;    //function will call mock function &lt;br /&gt;    //instead of real code.&lt;br /&gt;    //If we want to to do real code, &lt;br /&gt;    //just pass null factory, and it will&lt;br /&gt;    //create real factory and then myFunc &lt;br /&gt;    //will call funcMockHelper which call &lt;br /&gt;    //real code:         &lt;br /&gt;    //URL url = new URL(href); //Can not mock URL because of final class&lt;br /&gt;    //url.openstream(...)&lt;br /&gt;&lt;br /&gt;    OurClass(IHelperFactory  mockFactory) {&lt;br /&gt;         if mockFactory == null ) {&lt;br /&gt;              factory = new RealFactory();&lt;br /&gt;         } else {&lt;br /&gt;              factory = mockFactory;&lt;br /&gt;         }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function myFunc() {&lt;br /&gt;        &lt;br /&gt;        my code&lt;br /&gt;        ......&lt;br /&gt;        IHelperTest helper = factory.funcHelper().getMockHelper();        &lt;br /&gt;        helper.funcMockHelper();&lt;br /&gt;        ....&lt;br /&gt;        my code&lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And now, we can easily replace the code which can not mock which the mock code.&lt;br /&gt;The same pattern for static function.&lt;br /&gt;&lt;br /&gt;Conclusion: &lt;span style=&quot;font-weight:bold;&quot;&gt;Think about test + design first and then code.&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/1249948737978717307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/1249948737978717307' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/1249948737978717307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/1249948737978717307'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/02/jmock-and-how-to-mock-static-function.html' title='JMock and how to mock static function and final class?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-8521920809235174904</id><published>2008-02-03T09:31:00.000-08:00</published><updated>2008-02-03T10:10:22.620-08:00</updated><title type='text'>NUnit === How to and FAQ</title><content type='html'>NUnit goal:&lt;br /&gt;&lt;br /&gt;+ NUnit is a unit-testing framework for all .Net languages. Initially ported from  JUnit, the current production release, version 2.4, is the fifth major release of this xUnit based unit testing tool for Microsoft .NET. It is written entirely in C# and has been completely redesigned to take advantage of many .NET language features, for example custom attributes and other reflection related capabilities. NUnit brings xUnit to all .NET languages&lt;br /&gt;Homepage: &lt;a href=&quot;http://www.nunit.org/index.php&quot;&gt;http://www.nunit.org/index.php&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;+ Why do we use NUnit?&lt;br /&gt;Because it makes us do unit test very easy and very efficiently&lt;br /&gt;&lt;br /&gt;+ How to use NUnit:&lt;br /&gt;&lt;br /&gt;1.We have to import library:&lt;br /&gt; using System;&lt;br /&gt; using NUnit.Framework;&lt;br /&gt;2. Declare[TestFixture] attribute to class&lt;br /&gt;3. Declare [SetUp] attribute for setup function. This function will be called before any call to tested function&lt;br /&gt;4. Run NUnit test GUI to test assemblies&lt;br /&gt;&lt;br /&gt;+ Some functions need for test:&lt;br /&gt;Assert.AreEqual: raise exception if two object are not the same&lt;br /&gt;Assert.IsTrue: raise exception if condition is false&lt;br /&gt;Assert.IsFalse: raise exception if condition is true&lt;br /&gt;Assert.Fail: Raise exception to notify test case fail.&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;+ How do I implement a test case for a thrown exception?&lt;br /&gt;Catch the exception and if it isn&#39;t thrown call the Fail method. Fail signals the failure of a test case. Here is an example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    public void TestArgumentOutOfRangeException() {&lt;br /&gt;        ArrayList l = new ArrayList(10)&lt;br /&gt;        try {&lt;br /&gt;             Object o= l[l.Count];&lt;br /&gt;             Fail(&quot;Should raise an ArgumentOutOfRangeException&quot;);&lt;br /&gt;        } catch (ArgumentOutOfRangeException) {&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or use the ExceptionTestCase as follows.&lt;br /&gt;1) make your TestCase class a subclass of ExceptionTestCase.&lt;br /&gt;2) write the test ignoring exceptions&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    public void TestArgumentOutOfRangeException() {&lt;br /&gt;        ArrayList l= new ArrayList(10);&lt;br /&gt;        l[l.Count];&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;3) create the TestCase:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    Test t= new ExceptionTestCase(&quot;TestArgumentOutOfRangeException&quot;, typeof(ArgumentOutOfRangeException))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looking at this again, the first way is simpler. Sigh...&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Another way:&lt;/span&gt;&lt;br /&gt;Declare: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Test]&lt;br /&gt;[[ExpectedException( typeof( AException ) )]&lt;br /&gt;void testFunction() {&lt;br /&gt;...............&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;+ How do I organize my Test Cases?&lt;br /&gt;Here is one way:&lt;br /&gt;&lt;br /&gt;   1. create a test namespace for each of your application namespaces. For example, for a namespace MyApp.Util define MyApp.UtilTest. Put all the fixtures for the Util namespace into this namespace.&lt;br /&gt;   2. in MyApp.UtilTest define a class which creates a suite with all the tests in this namespace. To do so define a class AllTests which includes a single static Suite property. Here is an example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;       public static ITest Suite {&lt;br /&gt;         get {&lt;br /&gt;           TestSuite suite= new TestSuite();&lt;br /&gt;           suite.AddTest(Fixture1.Suite);&lt;br /&gt;           suite.AddTest(Fixture2.Suite);&lt;br /&gt;           return suite;&lt;br /&gt;         }&lt;br /&gt;       }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;   3. define similar AllTests classes that create higher level suites containing the suites from other test packages.&lt;br /&gt;&lt;br /&gt;When the fixtures are in a separate test assembly the test cases don&#39;t have access to the methods and fields with internal visibility. A variation of the above convention is to put all fixtures into the application assembly itself. This gives the fixtures access to all the assembly visible methods and fields. To separate the fixture classes from the production classes put them into a separate directory that you then add to the project. This makes it easy to ship the production classes independent of the fixtures.&lt;br /&gt;&lt;br /&gt;How do I run setup code once for all my TestCases?&lt;br /&gt;Wrap the top level suite in a subclass of TestSetup. Here is a sample AllTests.Suite method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    protected class WrappedTestSetup: TestSetup {&lt;br /&gt;      public WrappedTestSetup(ITest test) : base(test) {} &lt;br /&gt;      protected override void SetUp() {&lt;br /&gt;        OneTimeSetUp();&lt;br /&gt;       }&lt;br /&gt;      };&lt;br /&gt;&lt;br /&gt;    public static ITest Suite {&lt;br /&gt;      get {&lt;br /&gt;        TestSuite suite= new TestSuite();&lt;br /&gt;        ...add your tests and suites here...&lt;br /&gt;        TestSetup wrapper= new WrappedTestSetup(suite);&lt;br /&gt;        return wrapper;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;+ I want to debug when a test fails&lt;br /&gt;Start the test runner under the debugger and configure the debugger so that it catches the NUnit.Framework.AssertionFailedError. How you do this depends on the used IDE. Most debuggers support to stop the program when a specific exception is fired. Notice, that this will only break into the debugger when an &quot;anticipated&quot; assertion failed error occurs. &lt;br /&gt; &lt;br /&gt;Steps: &lt;br /&gt;1. Start C# IDE ( Visual .net ide)&lt;br /&gt;2. Build project + test in debug mode.&lt;br /&gt;3. Configure NUnit GUI to support VS.NET project&lt;br /&gt;3. Add debug assembly to NUnit GUI.&lt;br /&gt;4. Put breakpoint to where you want to debug&lt;br /&gt;Finished.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Sample: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;namespace NUnit.Samples.Money &lt;br /&gt;{&lt;br /&gt; using System;&lt;br /&gt; using NUnit.Framework;&lt;br /&gt; /// &lt;summary&gt;&lt;br /&gt; /// &lt;br /&gt; /// &lt;/summary&gt;&lt;br /&gt; /// &lt;br /&gt; [TestFixture]&lt;br /&gt; public class MoneyTest &lt;br /&gt; {&lt;br /&gt;  private Money f12CHF;&lt;br /&gt;  private Money f14CHF;&lt;br /&gt;  private Money f7USD;&lt;br /&gt;  private Money f21USD;&lt;br /&gt;        &lt;br /&gt;  private MoneyBag fMB1;&lt;br /&gt;  private MoneyBag fMB2;&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [SetUp]&lt;br /&gt;  protected void SetUp() &lt;br /&gt;  {&lt;br /&gt;   f12CHF= new Money(12, &quot;CHF&quot;);&lt;br /&gt;   f14CHF= new Money(14, &quot;CHF&quot;);&lt;br /&gt;   f7USD= new Money( 7, &quot;USD&quot;);&lt;br /&gt;   f21USD= new Money(21, &quot;USD&quot;);&lt;br /&gt;&lt;br /&gt;   fMB1= new MoneyBag(f12CHF, f7USD);&lt;br /&gt;   fMB2= new MoneyBag(f14CHF, f21USD);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void BagMultiply() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} *2 == {[24 CHF][14 USD]}&lt;br /&gt;   Money[] bag = { new Money(24, &quot;CHF&quot;), new Money(14, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Multiply(2));&lt;br /&gt;   Assert.AreEqual(fMB1, fMB1.Multiply(1));&lt;br /&gt;   Assert.IsTrue(fMB1.Multiply(0).IsZero);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void BagNegate() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} negate == {[-12 CHF][-7 USD]}&lt;br /&gt;   Money[] bag= { new Money(-12, &quot;CHF&quot;), new Money(-7, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Negate());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void BagSimpleAdd() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} + [14 CHF] == {[26 CHF][7 USD]}&lt;br /&gt;   Money[] bag= { new Money(26, &quot;CHF&quot;), new Money(7, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Add(f14CHF));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void BagSubtract() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]}&lt;br /&gt;   Money[] bag= { new Money(-2, &quot;CHF&quot;), new Money(-14, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Subtract(fMB2));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void BagSumAdd() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} + {[14 CHF][21 USD]} == {[26 CHF][28 USD]}&lt;br /&gt;   Money[] bag= { new Money(26, &quot;CHF&quot;), new Money(28, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Add(fMB2));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void IsZero() &lt;br /&gt;  {&lt;br /&gt;   Assert.IsTrue(fMB1.Subtract(fMB1).IsZero);&lt;br /&gt;&lt;br /&gt;   Money[] bag = { new Money(0, &quot;CHF&quot;), new Money(0, &quot;USD&quot;) };&lt;br /&gt;   Assert.IsTrue(new MoneyBag(bag).IsZero);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void MixedSimpleAdd() &lt;br /&gt;  {&lt;br /&gt;   // [12 CHF] + [7 USD] == {[12 CHF][7 USD]}&lt;br /&gt;   Money[] bag= { f12CHF, f7USD };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, f12CHF.Add(f7USD));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void MoneyBagEquals() &lt;br /&gt;  {&lt;br /&gt;   //NOTE: Normally we use Assert.AreEqual to test whether two&lt;br /&gt;   // objects are equal. But here we are testing the MoneyBag.Equals()&lt;br /&gt;   // method itself, so using AreEqual would not serve the purpose.&lt;br /&gt;   Assert.IsFalse(fMB1.Equals(null)); &lt;br /&gt;&lt;br /&gt;   Assert.IsTrue(fMB1.Equals( fMB1 ));&lt;br /&gt;   MoneyBag equal= new MoneyBag(new Money(12, &quot;CHF&quot;), new Money(7, &quot;USD&quot;));&lt;br /&gt;   Assert.IsTrue(fMB1.Equals(equal));&lt;br /&gt;   Assert.IsTrue(!fMB1.Equals(f12CHF));&lt;br /&gt;   Assert.IsTrue(!f12CHF.Equals(fMB1));&lt;br /&gt;   Assert.IsTrue(!fMB1.Equals(fMB2));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void MoneyBagHash() &lt;br /&gt;  {&lt;br /&gt;   MoneyBag equal= new MoneyBag(new Money(12, &quot;CHF&quot;), new Money(7, &quot;USD&quot;));&lt;br /&gt;   Assert.AreEqual(fMB1.GetHashCode(), equal.GetHashCode());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void MoneyEquals() &lt;br /&gt;  {&lt;br /&gt;   //NOTE: Normally we use Assert.AreEqual to test whether two&lt;br /&gt;   // objects are equal. But here we are testing the MoneyBag.Equals()&lt;br /&gt;   // method itself, so using AreEqual would not serve the purpose.&lt;br /&gt;   Assert.IsFalse(f12CHF.Equals(null)); &lt;br /&gt;   Money equalMoney= new Money(12, &quot;CHF&quot;);&lt;br /&gt;   Assert.IsTrue(f12CHF.Equals( f12CHF ));&lt;br /&gt;   Assert.IsTrue(f12CHF.Equals( equalMoney ));&lt;br /&gt;   Assert.IsFalse(f12CHF.Equals(f14CHF));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void MoneyHash() &lt;br /&gt;  {&lt;br /&gt;   Assert.IsFalse(f12CHF.Equals(null)); &lt;br /&gt;   Money equal= new Money(12, &quot;CHF&quot;);&lt;br /&gt;   Assert.AreEqual(f12CHF.GetHashCode(), equal.GetHashCode());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void Normalize() &lt;br /&gt;  {&lt;br /&gt;   Money[] bag= { new Money(26, &quot;CHF&quot;), new Money(28, &quot;CHF&quot;), new Money(6, &quot;CHF&quot;) };&lt;br /&gt;   MoneyBag moneyBag= new MoneyBag(bag);&lt;br /&gt;   Money[] expected = { new Money(60, &quot;CHF&quot;) };&lt;br /&gt;   // note: expected is still a MoneyBag&lt;br /&gt;   MoneyBag expectedBag= new MoneyBag(expected);&lt;br /&gt;   Assert.AreEqual(expectedBag, moneyBag);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void Normalize2() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} - [12 CHF] == [7 USD]&lt;br /&gt;   Money expected= new Money(7, &quot;USD&quot;);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Subtract(f12CHF));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void Normalize3() &lt;br /&gt;  {&lt;br /&gt;   // {[12 CHF][7 USD]} - {[12 CHF][3 USD]} == [4 USD]&lt;br /&gt;   Money[] s1 = { new Money(12, &quot;CHF&quot;), new Money(3, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag ms1= new MoneyBag(s1);&lt;br /&gt;   Money expected= new Money(4, &quot;USD&quot;);&lt;br /&gt;   Assert.AreEqual(expected, fMB1.Subtract(ms1));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void Normalize4() &lt;br /&gt;  {&lt;br /&gt;   // [12 CHF] - {[12 CHF][3 USD]} == [-3 USD]&lt;br /&gt;   Money[] s1 = { new Money(12, &quot;CHF&quot;), new Money(3, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag ms1= new MoneyBag(s1);&lt;br /&gt;   Money expected= new Money(-3, &quot;USD&quot;);&lt;br /&gt;   Assert.AreEqual(expected, f12CHF.Subtract(ms1));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void Print() &lt;br /&gt;  {&lt;br /&gt;   Assert.AreEqual(&quot;[12 CHF]&quot;, f12CHF.ToString());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void SimpleAdd() &lt;br /&gt;  {&lt;br /&gt;   // [12 CHF] + [14 CHF] == [26 CHF]&lt;br /&gt;   Money expected= new Money(26, &quot;CHF&quot;);&lt;br /&gt;   Assert.AreEqual(expected, f12CHF.Add(f14CHF));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void SimpleBagAdd() &lt;br /&gt;  {&lt;br /&gt;   // [14 CHF] + {[12 CHF][7 USD]} == {[26 CHF][7 USD]}&lt;br /&gt;   Money[] bag= { new Money(26, &quot;CHF&quot;), new Money(7, &quot;USD&quot;) };&lt;br /&gt;   MoneyBag expected= new MoneyBag(bag);&lt;br /&gt;   Assert.AreEqual(expected, f14CHF.Add(fMB1));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void SimpleMultiply() &lt;br /&gt;  {&lt;br /&gt;   // [14 CHF] *2 == [28 CHF]&lt;br /&gt;   Money expected= new Money(28, &quot;CHF&quot;);&lt;br /&gt;   Assert.AreEqual(expected, f14CHF.Multiply(2));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void SimpleNegate() &lt;br /&gt;  {&lt;br /&gt;   // [14 CHF] negate == [-14 CHF]&lt;br /&gt;   Money expected= new Money(-14, &quot;CHF&quot;);&lt;br /&gt;   Assert.AreEqual(expected, f14CHF.Negate());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;br /&gt;  [Test]&lt;br /&gt;  public void SimpleSubtract() &lt;br /&gt;  {&lt;br /&gt;   // [14 CHF] - [12 CHF] == [2 CHF]&lt;br /&gt;   Money expected= new Money(2, &quot;CHF&quot;);&lt;br /&gt;   Assert.AreEqual(expected, f14CHF.Subtract(f12CHF));&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/8521920809235174904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/8521920809235174904' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/8521920809235174904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/8521920809235174904'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/02/nunit-how-to-and-faq.html' title='NUnit === How to and FAQ'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-3785527782153670628</id><published>2008-01-21T21:59:00.000-08:00</published><updated>2008-01-21T22:00:25.635-08:00</updated><title type='text'>How to use interface correctly?</title><content type='html'>A: IparentInterface;&lt;br /&gt;B: IchildInterface;&lt;br /&gt;&lt;br /&gt;This code will generate error: &lt;br /&gt;&lt;br /&gt;B: = IchildInterface (A); ==&gt; will generate expected error whenever we access to B. At the time we down-cast A, we do not have exception.&lt;br /&gt;&lt;br /&gt;The correct code should be:&lt;br /&gt;&lt;br /&gt;A.QueryInterface (IchildInterface, B);&lt;br /&gt;&lt;br /&gt;So the lesson here: Always use QueryInterface when you want to access other inteface of the same object. Do not cast Interface except you really know what you do.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/3785527782153670628/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/3785527782153670628' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/3785527782153670628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/3785527782153670628'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/01/how-to-use-interface-correctly.html' title='How to use interface correctly?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-3062208738667740161</id><published>2008-01-21T21:47:00.000-08:00</published><updated>2008-01-21T21:59:10.743-08:00</updated><title type='text'>Delphi Memory Leak</title><content type='html'>I have a lession learn today when I work with delphi. We should avoid using Tlist to store object which implemented any interfaces.&lt;br /&gt;&lt;br /&gt;We should use TinterfaceList instead of Tlist. The reason is: Tlist use pointer to reference to object. When we get an object and from that object we get one of its interfaces. This interface is not referenced by anyone (although Tlist use pointer to point to the object) .Therefor when go out of variable scope, this interface is freed automatically. &lt;br /&gt;&lt;br /&gt;Hereunder is the sample explaining the lession.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;InterfaceA = Iinterface &lt;br /&gt;            Procedure methodA;&lt;br /&gt;End;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ClassA = class(TinterfaceObject, InterfaceA)&lt;br /&gt;Procedure methodA;&lt;br /&gt;End;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Var &lt;br /&gt;A: TOBbject&lt;br /&gt;List: Tlist;&lt;br /&gt;Begin&lt;br /&gt;List := Tlist.Create;&lt;br /&gt;List.Add(A);&lt;br /&gt;methodX;&lt;br /&gt;methodY; ==&gt; Exception occur here.&lt;br /&gt;End.&lt;br /&gt;&lt;br /&gt;Procedure methodX;&lt;br /&gt;Var&lt;br /&gt;   X: InterfaceA;&lt;br /&gt;   Obj: ClassA;&lt;br /&gt;Begin&lt;br /&gt;   Obj := Tlist.Items[0];&lt;br /&gt;  Obj.QueryInterface(InterfaceA, X); ==&gt; Get interface here. Reference count increased to 1&lt;br /&gt;End; ==&gt; Interface will release reference count. Reference count = 0 ==&gt; Obj is freed.&lt;br /&gt; &lt;br /&gt;Procedure methodY;&lt;br /&gt;Begin&lt;br /&gt;Obj := Tlist.Items[0];&lt;br /&gt;            Obj.methodA; ç Exception because of object is freed.&lt;br /&gt;End;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Hope this lession lean can help you avoid unexpected exception.&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/3062208738667740161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/3062208738667740161' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/3062208738667740161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/3062208738667740161'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/01/delphi-memory-leak.html' title='Delphi Memory Leak'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-5528552978497142072</id><published>2008-01-21T21:27:00.000-08:00</published><updated>2008-01-21T21:46:35.833-08:00</updated><title type='text'>Does java have memory leak?</title><content type='html'>According to the IBM&#39;s artical, JAVA also has problems of memory leak event though they have its own memory garbage collector. The main reason is that: design is not good.&lt;br /&gt;&lt;br /&gt;I. How memory garbage collector work?&lt;br /&gt;&lt;br /&gt;The job of the garbage collector is to find objects that are no longer needed by an application and to remove them when they can no longer be accessed or referenced. The garbage collector starts at the root nodes, classes that persist throughout the life of a Java application, and sweeps though all of the nodes that are referenced. As it traverses the nodes, it keeps track of which objects are actively being referenced. Any classes that are no longer being referenced are then eligible to be garbage collected. The memory resources used by these objects can be returned to the Java virtual machine (JVM) when the objects are deleted.&lt;br /&gt;&lt;br /&gt;II. How it leaks?&lt;br /&gt;&lt;br /&gt;&quot;An object is only counted as being unused when it is no longer referenced&quot; ==&gt; The garbage collector can not do its job if an object is referenced by others object for a very long time even though it is no longer needed. It can lead to a terrible problems: out of memory.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;br /&gt;Object A = new Object; //A exists until program terminate.&lt;br /&gt;During program life cycle, an object B is created and referenced by A&lt;br /&gt;Because programmer believe in garbage collector, so they do not free memory manually,&lt;br /&gt;but let garbage collector do it for him ==&gt; B exist until A freed.&lt;br /&gt;If program create many and many B for its life ==&gt; all B will exist too  ==&gt; cause out of memory.&lt;br /&gt;&lt;br /&gt;It is the obvious example of problem of memory leak. So we do not believe in Garbage Collector 100%. Let free object manually by assigned unused object to nil.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;III.Preventing memory leaks&lt;br /&gt;&lt;br /&gt;You can prevent memory leaks by watching for some common problems. Collection classes, such as hashtables and vectors, are common places to find the cause of a memory leak. This is particularly true if the class has been declared static and exists for the life of the application.&lt;br /&gt;&lt;br /&gt;Another common problem occurs when you register a class as an event listener without bothering to unregister when the class is no longer needed. Also, many times member variables of a class that point to other classes simply need to be set to null at the appropriate time. &lt;br /&gt;&lt;br /&gt;Hope i can help you in avoid memory leak in java.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/5528552978497142072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/5528552978497142072' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5528552978497142072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5528552978497142072'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/01/does-java-have-memory-leak.html' title='Does java have memory leak?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-7939347398316529790</id><published>2008-01-19T04:57:00.000-08:00</published><updated>2008-01-19T05:03:52.572-08:00</updated><title type='text'>How to get the path and filename of executable file of current process (or running process)?</title><content type='html'>How to get the path + file name of the current process or the running process?&lt;br /&gt;There are two way to this:&lt;br /&gt;&lt;br /&gt;I will illustrate idea in delphi&lt;br /&gt;&lt;br /&gt;1. You can call the API function: GetProcessImageFileName&lt;br /&gt;&lt;br /&gt;Declare prototype:&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;function GetProcessImageFileNameA(hProcess: THandle; lpImageFileName: LPSTR;&lt;br /&gt; nSize: DWORD): DWORD; stdcall;&lt;br /&gt;{$EXTERNALSYM GetProcessImageFileNameA}&lt;br /&gt;function GetProcessImageFileNameW(hProcess: THANDLE; lpImageFileName: LPWSTR;&lt;br /&gt;  nSize: DWORD): DWORD; stdcall;&lt;br /&gt;{$EXTERNALSYM GetProcessImageFileNameW}&lt;br /&gt;function GetProcessImageFileName(hProcess: THANDLE; lpImageFileName: LPTSTR;&lt;br /&gt;  nSize: DWORD): DWORD; stdcall;&lt;br /&gt;{$EXTERNALSYM GetProcessImageFileName}&lt;br /&gt;&lt;br /&gt;implementation &lt;br /&gt;&lt;br /&gt;function GetProcessImageFileNameA(hProcess: THandle; lpImageFileName: LPSTR;&lt;br /&gt; nSize: DWORD): DWORD; stdcall; external &#39;Psapi.dll&#39; name &#39;GetProcessImageFileNameA&#39;;&lt;br /&gt;function GetProcessImageFileNameW(hProcess: THANDLE; lpImageFileName: LPWSTR;&lt;br /&gt;  nSize: DWORD): DWORD; stdcall; external &#39;Psapi.dll&#39; name &#39;GetProcessImageFileNameW&#39;;&lt;br /&gt;function GetProcessImageFileName(hProcess: THANDLE; lpImageFileName: LPTSTR;&lt;br /&gt;  nSize: DWORD): DWORD; stdcall; external &#39;Psapi.dll&#39; name &#39;GetProcessImageFileNameA&#39;;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Calling function:&lt;br /&gt;&lt;br /&gt;+ GetWindowThreadProcessId(WindowHandle, @processID); ==&gt; get processid&lt;br /&gt;+ processHandle := OpenProcess(PROCESS_TERMINATE or PROCESS_QUERY_INFORMATION,&lt;br /&gt;        False, processID); ==&gt; Get process handler&lt;br /&gt;+ GetProcessImageFileName(processHandle , bufferout, lenghtofbufferout)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. The second way:&lt;br /&gt;&lt;br /&gt;Call function GetModuleFileName with first parameter nil&lt;br /&gt;+ GetModuleFileName(0, Buffer, 1024); &lt;== Buffer contain the current path of current process.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/7939347398316529790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/7939347398316529790' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7939347398316529790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7939347398316529790'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2008/01/how-to-get-path-and-filename-of.html' title='How to get the path and filename of executable file of current process (or running process)?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-7955034227260307318</id><published>2007-12-30T04:39:00.000-08:00</published><updated>2007-12-30T04:52:44.339-08:00</updated><title type='text'>Delphi --- problem when using function RegisterClass</title><content type='html'>Delphi has a feature: serialize/deserialize object. It means you can save/load object at runtime.&lt;br /&gt;To do this, your classes have to inherit from TPersistent and you have to register this class to system.  To register a class you can you function: RegisterClass. After registering successfully, you can read or write object to the stream. But there are problems: you can not know if class is registered successfully till you call function: getclass or findclass, and you can not know why it is not succeeded. It takes me 1 day to find why it can not register. All i have to do is debug delphi system to find out the reason. And i want to share my experience when i face this problem. The reason is: the class belongs to a group. But this group is deactivated when delphi create group. So this class can not be registered. All we have to do in this case is activate group again by calling function activategroup(class belong to group). Hope i can help you to save time when solve this problem</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/7955034227260307318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/7955034227260307318' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7955034227260307318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7955034227260307318'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/12/delphi-problem-when-using-function.html' title='Delphi --- problem when using function RegisterClass'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-6157650300121244300</id><published>2007-12-30T04:25:00.000-08:00</published><updated>2007-12-30T04:37:52.519-08:00</updated><title type='text'>HTML Parser -- A good html parser.</title><content type='html'>I have found many parser used in my projects. By asking google, we can find some parsers. But after try many parsers, i can tell you that HTMLPaser is the one should be considered.&lt;br /&gt;Website: &lt;a href=&quot;http://www.blogger.com/htmlparser.sourceforge.net&quot;&gt;&lt;span style=&quot;&quot;&gt;&lt;span class=&quot;a&quot;&gt;&lt;b&gt;htmlparser&lt;/b&gt;.sourceforge.net&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;HTMLParser is a parser which can help you parser html file. It has 2 features which i like very much&lt;br /&gt;1. Extracter: It can help us extract information from html file very easily with many filters.&lt;br /&gt;All we have to do is call parser.parse(Filter).  A filter can be an individual filter or it can be predicate which combine many filter(and , or , not, user-defined filter)  to help you extract as much as possible information.&lt;br /&gt;2. Visitor pattern: With visitor pattern you can do what you want with a tag when parser traverse to that tag. You can change the attribute of node, get information, ... whatever you want.&lt;br /&gt;Besides, htmlparser is well-structured, so it can speed-up coding, maintain easily.&lt;br /&gt;&lt;br /&gt;If you want to find a good html parser, why don&#39;t give it a try.&lt;br /&gt;PS: You should try html parser version 2.0  &lt;br /&gt;&lt;span style=&quot;&quot;&gt;&lt;span class=&quot;a&quot;&gt;&lt;/span&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/6157650300121244300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/6157650300121244300' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/6157650300121244300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/6157650300121244300'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/12/html-parser-good-html-parser.html' title='HTML Parser -- A good html parser.'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-7545855790279643777</id><published>2007-12-28T07:37:00.000-08:00</published><updated>2007-12-28T07:46:16.851-08:00</updated><title type='text'>Introduction to blogvertise</title><content type='html'>Today, i surf net and found an interesting website. You know i love to write blog, and share my experiences i met in my work. It is my hobby. But if i can earn money from those posts,it is really interesting. And Blogvertise is what i want to tell you. You can earn money from your blogs with your posts. The re quirement is simple. You get your email, follow their instruction to write a blog with some criteria. And wait for your money.&lt;br /&gt;&lt;br /&gt;Hope it will help you about blogvertise.&lt;br /&gt;&lt;br /&gt;Website: &lt;a href=&quot;http://www.blogsvertise.com/&quot;&gt;Blogvertise&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/7545855790279643777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/7545855790279643777' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7545855790279643777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7545855790279643777'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/12/introduction-to-blogvertise.html' title='Introduction to blogvertise'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-1092047157337627719</id><published>2007-12-09T06:27:00.000-08:00</published><updated>2007-12-09T07:33:26.556-08:00</updated><title type='text'>Java: How to load class dynamically (on fly)?</title><content type='html'>I face a problem of loading class dynamically in JAVA. How to do this?&lt;br /&gt;After do research in internet with many site, i finally find out the way to do this.&lt;br /&gt;In order to save your time on researching problem, i will show you how to do this.&lt;br /&gt;&lt;br /&gt;To understand the framework, we have to know following knowledge:&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;1. Class loader and how does it work&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So how does class loader work:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href=&quot;http://www.shareapic.net/content.php?id=4635706&amp;owner=comboy&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;http://www.shareapic.net/preview2/004635706.png&quot; border=&quot;0&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;  &lt;br /&gt;As shown in Figure 1, the bootstrap class loader (BS) -- Native implemented into JVM -- loads the classes from the JVM, as well as extensions to the JDK. The system class loader (CP) loads all of the classes provided by the CLASSPATH environment variable or passed using the -classpath argument to the java command. Finally we have several additional class loaders, where A1-3 are children of the CP, and B1-2 are children of A3. Every class loader (except BS) has a parent class loader, even if no parent is provided explicitly; in the latter case, the CP is automatically set as the parent.&lt;br /&gt;&lt;br /&gt;Rule for class loader:&lt;br /&gt;&lt;br /&gt;1. lass loaders are hierarchically organized, where each one has a parent class loader, except the bootstrap class loader (the root).&lt;br /&gt;&lt;br /&gt;2. Class loaders should (practically: must) delegate the loading of a class to the parent, but a custom class loader can define for itself when it should do so.&lt;br /&gt;&lt;br /&gt;3. A class is defined by its class type and the effective class loader.&lt;br /&gt;&lt;br /&gt;4. A class is only loaded once and then cached in the class loader to ensure that the byte code cannot change.&lt;br /&gt;&lt;br /&gt;5. Any symbolic links are loaded by the effective class loader (or one of its ancestors), if this is not already done. The JVM can defer this resolution until the class is actually used.&lt;br /&gt;&lt;br /&gt;6. An upcast of an instance to another class fails when the class of the instance and the class of the symbolic link do not match (meaning their class loaders do not match).&lt;br /&gt;&lt;br /&gt;But another problem is: Classes are loaded by many class loader. How can it distinguish them? JAVA do it by combine qualified name: (Class name, Package name, Class loader) --&gt; With the same classes, but loaded by different class loader, it can not be used together. We can check it easily by using URLClassLoader and sun.misc.Launcher$AppClassLoader(class loader which load main function). &lt;br /&gt;&lt;br /&gt;When i work with JAX-WS, i see that we can dynamically add custom handler to WS, and JAX-WS will call our handler. It make me think a lot. How can it do this?&lt;br /&gt;The answer is: JAXWS has its own class loader. And the class loader will find and call our handler. &lt;br /&gt;&lt;br /&gt;Now we will make a framework which can allow us load custom handler like JAX-WS at run time.&lt;br /&gt;&lt;br /&gt;1. We define a interface for handler. This handler is packaged in jar file.&lt;br /&gt;2. User will define class which implemented the interface handler. The implement class is packaged in other jar file.&lt;br /&gt;3. Our framework will scan those jar file and load all custom handlers, and call it sequentially.&lt;br /&gt;&lt;br /&gt;The most difficult part is load all custom handlers and call it at run time.&lt;br /&gt;To solve the problem, we will make our own class loader:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class CustomLoader extends URLClassLoader&lt;br /&gt;{ &lt;br /&gt;    static public ClassLoader MainClassLoader = null;&lt;br /&gt; &lt;br /&gt;    protected CustomLoader (URL[] urls, ClassLoader parent) {&lt;br /&gt;        super(urls, parent);        &lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public Class&lt;?&gt; loadClass(String name) throws ClassNotFoundException {&lt;br /&gt;        //Get system class loader        &lt;br /&gt;        try { &lt;br /&gt;         if (MainClassLoader != null) {&lt;br /&gt;          return MainClassLoader.loadClass(name);&lt;br /&gt;         } else {&lt;br /&gt;          return super.loadClass(name);&lt;br /&gt;         }&lt;br /&gt;        } catch (ClassNotFoundException e) {&lt;br /&gt;         return super.loadClass(name);&lt;br /&gt;        }        &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Class&lt;?&gt; findClass(String name) throws ClassNotFoundException {&lt;br /&gt;        Class clas = super.findClass(name);&lt;br /&gt;        return clas;&lt;br /&gt;    }&lt;br /&gt;} &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The variable MainClassLoader point to sun.misc.Launcher$AppClassLoader( the class loader which call our main function)&lt;br /&gt;&lt;br /&gt;How to used this custom class loader?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void main() {&lt;br /&gt;   ClassLoader base = ClassLoader.getSystemClassLoader(); //get the boot trap class loader&lt;br /&gt;   CustomLoader loader = new CustomLoader(urls, base.getParent()); //create our class loader&lt;br /&gt;   loader.MainClassLoader = Thread.currentThread().getContextClassLoader(); //set the  //application class loader to our class loader. We have to do this because when java //compile and link our code, it used class loader: sun.misc.Launcher$AppClassLoader. &lt;br /&gt;&lt;br /&gt;Class&lt;?&gt; handlerClass = loader.loadClass(&quot;CustomHandler&quot;);&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Object handler =  handlerClass.newInstance();&lt;/span&gt; (1)&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Handler customHandler = (Handler)handler; &lt;/span&gt;(2)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;(1) --&gt; implement class is loaded by our custom class loader. &lt;br /&gt;(2) --&gt; Handler is loaded by sun.misc.Launcher$AppClassLoader&lt;br /&gt;&lt;br /&gt;Thank to this code:&lt;br /&gt;&lt;br /&gt;public Class&lt;?&gt; loadClass(String name) throws ClassNotFoundException {&lt;br /&gt;        //Get system class loader        &lt;br /&gt;        try { &lt;br /&gt;         if (MainClassLoader != null) {&lt;br /&gt;          return MainClassLoader.loadClass(name);&lt;br /&gt;         } else {&lt;br /&gt;          return super.loadClass(name);&lt;br /&gt;         }&lt;br /&gt;        } catch (ClassNotFoundException e) {&lt;br /&gt;         return super.loadClass(name);&lt;br /&gt;        }        &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;We delegate loading class to MainClassLoader first: because in linkage phase, MainClassLoader load all symbols in our source code. If we do not do this, we will violate the rule 6. If MainClassLoader does not load them, we will delegate to parent or we violate rule 2. If both do not load class, we can do it by ourselves. But in this case we do not implement this(for simple).&lt;br /&gt;&lt;br /&gt;So with our custom class loader, (2) can be used without ClassCastException.&lt;br /&gt;&lt;br /&gt;So i hope , you can make a framework easily with the custom class loader as i explain.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/1092047157337627719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/1092047157337627719' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/1092047157337627719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/1092047157337627719'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/12/java-how-to-load-class-dynamically-on.html' title='Java: How to load class dynamically (on fly)?'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-5094571228741850298</id><published>2007-11-22T11:32:00.000-08:00</published><updated>2007-11-22T11:33:36.767-08:00</updated><title type='text'>Memory Management -- Part 6</title><content type='html'>Memory Management&lt;br /&gt;&lt;br /&gt;Note the page attributes from the output of the first instance. The functions are marked read-only, as expected. The unshared variable is also marked read-only. This is because Windows NT tries to share the data space also. As described earlier, such pages are marked for copy-on-write, and as soon as the process modifies any location in the page, the process gets a private copy of the page to write to. The other page attributes show that the PTE is valid, the page is a user-mode page, and nobody has modified the page so far.&lt;br /&gt;&lt;br /&gt;Now, compare the output from the first instance with the output from the second instance when it loaded the MYDLL.DLL at a base address different from that in the first instance. As expected, the virtual addresses of all the memory sections are different than those for the first instance. The physical addresses are the same except for the physical address of the relocatable function. This demonstrates that the code pages are marked as copy-on-write, and when the loader modifies the code pages while performing relocation, the process gets a private writable copy. Our nonrelocatable function does not need any relocation; hence, the corresponding pages are not modified. The second instance can share these pages with the first instance and hence has the same physical page address.&lt;br /&gt;&lt;br /&gt;To cancel out the effects of relocation, the second instance loads MYDLL.DLL at the same base address as that in the first instance. Yup! Now, the virtual address matches the ones from the first instance. Note that the physical address for the relocatable function also matches that in the output from the first instance. The loader need not relocate the function because the DLL is loaded at the preferred base address. This allows more memory sharing and provides optimal performance. It’s reason enough to allocate proper, nonclashing preferred base addresses for your DLLs.&lt;br /&gt;&lt;br /&gt;This ideal share-all situation ceases to exist as soon as a process modifies some memory location. Other processes cannot be allowed to view these modifications. Hence, the modifying process gets its own copy of the page The second instance of the sample program demonstrates this by modifying the data variables and a byte at the start of the nonrelocatable function. The output shows that the physical address of the nonrelocatable doesn’t match with the first instance. The nonrelocatable function is not modified by the loader, but it had the same effect on sharing when we modified the function. The shared variable remains a shared variable. Its physical address matches that in the first instance because all the processes accessing a shared variable are allowed to see the modifications made by other processes. But the nonshared variable has a different physical address now. The second instance cannot share the variable with the first instance and gets its own copy. The copy was created by the system page fault handler when we tried to write to a read-only page and the page was also marked for copy-on-write. Note that the page is now marked read-write. Hence, further writes go through without the operating system getting any page faults. Also, note that the modified pages are marked as dirty by the processor.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SWITCHING CONTEXT&lt;br /&gt;&lt;br /&gt;As we saw earlier, Windows NT can switch the memory context to another process by setting the appropriate page table directory. The 80386 processor requires that the pointer to the current page table directory be maintained in the CR3 register. Therefore, when the Windows NT scheduler wants to perform a context switch to another process, it simply sets the CR3 register to the page table directory of the concerned process.&lt;br /&gt;&lt;br /&gt;Windows NT needs to change only the memory context for some API calls such as VirtualAllocEx(). The VirtualAllocEx() API call allocates memory in the memory space of a process other than the calling process. Other system calls that require memory context switch are ReadProcessMemory() and WriteProcessMemory(). The ReadProcessMemory() and WriteProcessMemory() system calls read and write, respectively, memory blocks from and to a process other than the calling process. These functions are used by debuggers to access the memory of the process being debugged. The subsystem server processes also use these functions to access the client process’s memory. The undocumented KeAttchProcess() function from the NTOSKRNL module switches the memory context to specified process. The undocumented KeDetachProcess() function switches it back. In addition to switching memory context, it also serves as a notion of current process. For example, if you attach to a particular process and create a mutex, it will be created in the context of that process. The prototypes for KeAttachProcess() and KeDetachProcess() are as follows:&lt;br /&gt;&lt;br /&gt;NTSTATUS KeAttachProcess(PEB *);&lt;br /&gt;&lt;br /&gt;NTSTATUS KeDetachProcess ();&lt;br /&gt;&lt;br /&gt;Another place where a call to the KeAttachProcess() function appears is the NtCreateProcess() system call. This system call is executed in the context of the parent process. As a part of this system call, Windows NT needs to map the system DLL (NTDLL.DLL) in the child process’s address space. Windows NT achieves this by calling KeAttachProcess() to switch the memory context to the child process. After mapping the DLL, Windows NT switches back to the parent process’s memory context by calling the KeDetachProcess() function.&lt;br /&gt;&lt;br /&gt;The following sample demonstrates how you can use the KeAttachProcess() and KeDetachProcess() functions. The sample prints the page directories for all the processes running in the system. The complete source code is not included. Only the relevant portion of the code is given. Because these functions can be called only from a device driver, we have written a device driver and provided an IOCTL that demonstrates the use of this function. We are giving the function that is called in response to DeviceIoControl from the application. Also, the output of the program is shown in kernel mode debugger’s window (such as SoftICE). Getting the information back to the application is left as an exercise for the reader.&lt;br /&gt;&lt;br /&gt;void DisplayPageDirectory(void *Peb)&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;unsigned int *PageDirectory =&lt;br /&gt;&lt;br /&gt;(unsigned int *)0xC0300000;&lt;br /&gt;&lt;br /&gt;int i;&lt;br /&gt;&lt;br /&gt;int ctr=0;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;KeAttachProcess(Peb);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;for (i = 0; i &lt; 1024; i++) {&lt;br /&gt;&lt;br /&gt;if (PageDirectory[i]&amp;0x01) {&lt;br /&gt;&lt;br /&gt;if ((ctr%8) == 0)&lt;br /&gt;&lt;br /&gt;DbgPrint(&quot;  \n&quot;);&lt;br /&gt;&lt;br /&gt;DbgPrint(&quot;%08x &quot;, PageDirectory[i]&amp;0xFFFFF000);&lt;br /&gt;&lt;br /&gt;ctr++;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;DbgPrint(&quot;\n\n&quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;KeDetachProcess();&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The DisplayPageDirectory() function accepts the PEB for the process whose page directory is to be printed. The function first calls the KeAttachProcess() function with the given PEB as the parameter. This switches the page directory to the desired one. Still, the function can access the local variables because the kernel address space is shared by all the processes. Now the address space is switched, and the 0xC030000 address points to the page directory to be printed. The function prints the 1024 entries from the page directory and then switches back to the original address space using the KeDetachProcess() function.&lt;br /&gt;&lt;br /&gt;void DisplayPageDirectoryForAllProcesses()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;PLIST_ENTRY ProcessListHead, ProcessListPtr;&lt;br /&gt;&lt;br /&gt; ULONG BuildNumber;&lt;br /&gt;&lt;br /&gt; ULONG ListEntryOffset;&lt;br /&gt;&lt;br /&gt; ULONG NameOffset;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; BuildNumber=NtBuildNumber &amp; 0x0000FFFF;&lt;br /&gt;&lt;br /&gt; if ((BuildNumber==0x421) || (BuildNumber==0x565)) { // NT&lt;br /&gt;&lt;br /&gt;3.51 or NT 4.0&lt;br /&gt;&lt;br /&gt;  ListEntryOffset=0x98;&lt;br /&gt;&lt;br /&gt;  NameOffset=0x1DC;&lt;br /&gt;&lt;br /&gt; } else if (BuildNumber==0x755) {// Windows 2000 beta2&lt;br /&gt;&lt;br /&gt;  ListEntryOffset=0xA0;&lt;br /&gt;&lt;br /&gt;  NameOffset=0x1FC;&lt;br /&gt;&lt;br /&gt; } else {&lt;br /&gt;&lt;br /&gt;  DbgPrint(&quot;Unsupported NT Version\n&quot;);&lt;br /&gt;&lt;br /&gt;  return;&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; ProcessListHead=ProcessListPtr=(PLIST_ENTRY)(((char&lt;br /&gt;&lt;br /&gt;*)PsInitialSystemProcess)+ListEntryOffset);&lt;br /&gt;&lt;br /&gt; while (ProcessListPtr-&gt;Flink!=ProcessListHead) {&lt;br /&gt;&lt;br /&gt;  void *Peb;&lt;br /&gt;&lt;br /&gt;  char ProcessName[16];&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  Peb=(void *)(((char *)ProcessListPtr)-&lt;br /&gt;&lt;br /&gt;ListEntryOffset);&lt;br /&gt;&lt;br /&gt;  memset(ProcessName, 0, sizeof(ProcessName));&lt;br /&gt;&lt;br /&gt;  memcpy(ProcessName, ((char *)Peb)+NameOffset, 16);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  DbgPrint(&quot;**%s Peb @%x**  &quot;, ProcessName, Peb);&lt;br /&gt;&lt;br /&gt;  DisplayPageDirectory(Peb);&lt;br /&gt;&lt;br /&gt;  ProcessListPtr=ProcessListPtr-&gt;Flink;&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The DisplayPageDirectoryForAllProcesses() function calls the DisplayPageDirectory() function for each process in the system. All the processes running in a system are linked in a list. The function gets hold of the list of the processes from the PEB of the initial system process. The PsInitialSystemProcess variable in NTOSKRNL holds the PEB for the initial system process. The process list node is located at an offset of 0x98 (0xA0 for Windows NT 5.0) inside the PEB. The process list is a circular linked list. Once you get hold of any node in the list, you can traverse the entire list. The DisplayPageDirectoryForAllProcesses() function completes a traversal through the processes list by following the Flink member, printing the page directory for the next PEB in the list every time until it reaches back to the PEB it started with. For every process, the function first prints the process name that is stored at a version-dependent offset within the PEB and then calls the DisplayPageDirectory() function to print the page directory.&lt;br /&gt;&lt;br /&gt;Here, we list partial output from the sample program. Please note a couple of things in the following output. First, every page directory has 50-odd valid entries while the page directory size is 1024. The remaining entries are invalid, meaning that the corresponding page tables are either not used or are swapped out. In other words, the main memory overhead of storing page tables is negligible because the page tables themselves can be swapped out. Also, note that the page directories have the same entries in the later portion of the page directory. This is because this part represents the kernel portion shared across all processes by using the same set of page tables for the kernel address range.&lt;br /&gt;&lt;br /&gt;Listing 4-12: Displaying page directories: output&lt;br /&gt;&lt;br /&gt;**System Peb @fdf06b60**&lt;br /&gt;&lt;br /&gt;00500000 008cf000 008ce000 00032000 00034000 00035000 ... ... ...&lt;br /&gt;&lt;br /&gt;00040000 00041000 00042000 00043000 00044000 00045000 ... ... ...&lt;br /&gt;&lt;br /&gt;00048000 00049000 0004a000 0004b000 0004c000 0004d000 ... ... ...&lt;br /&gt;&lt;br /&gt;00050000 00051000 00052000 00053000 00054000 00055000 ... ... ...&lt;br /&gt;&lt;br /&gt;00058000 00059000 0005a000 0005b000 0005c000 0005d000 ... ... ...&lt;br /&gt;&lt;br /&gt;00020000 00021000 00023000 0040b000 0040c000 0040d000 ... ... ...&lt;br /&gt;&lt;br /&gt;00410000 00411000 00412000 00413000 00414000 00415000 ... ... ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;**smss.exe Peb @fe2862e0**&lt;br /&gt;&lt;br /&gt;00032000 00034000 00035000 00033000 00e90000 00691000 ... ... ...&lt;br /&gt;&lt;br /&gt;00043000 00044000 00045000 00046000 00047000 00048000 ... ... ...&lt;br /&gt;&lt;br /&gt;0004b000 0004c000 0004d000 0004e000 0004f000 00050000 ... ... ...&lt;br /&gt;&lt;br /&gt;00053000 00054000 00055000 00056000 00057000 00058000 ... ... ...&lt;br /&gt;&lt;br /&gt;0005b000 0005c000 0005d000 0005e000 0005f000 00020000 ... ... ...&lt;br /&gt;&lt;br /&gt;0040b000 0040c000 0040d000 0040e000 0040f000 00410000 ... ... ...&lt;br /&gt;&lt;br /&gt;00413000 00414000 00415000 00416000 00031000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;... ... ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;**winlogon.exe Peb @fe27dde0**&lt;br /&gt;&lt;br /&gt;00032000 00034000 00035000 00033000 00be1000 00953000 ... ... ...&lt;br /&gt;&lt;br /&gt;00043000 00044000 00045000 00046000 00047000 00048000 ... ... ...&lt;br /&gt;&lt;br /&gt;0004b000 0004c000 0004d000 0004e000 0004f000 00050000 ... ... ...&lt;br /&gt;&lt;br /&gt;00053000 00054000 00055000 00056000 00057000 00058000 ... ... ...&lt;br /&gt;&lt;br /&gt;0005b000 0005c000 0005d000 0005e000 0005f000 00020000 ... ... ...&lt;br /&gt;&lt;br /&gt;0040b000 0040c000 0040d000 0040e000 0040f000 00410000 ... ... ...&lt;br /&gt;&lt;br /&gt;00413000 00414000 00415000 00416000 00031000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;... ... ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;DIFFERENCES BETWEEN WINDOWS NT AND WINDOWS 95/98&lt;br /&gt;&lt;br /&gt;Generally, the memory management features offered by Windows 95/98 are the same as those in Windows NT. Windows 95/98 also offers 32-bit flat separate address space for each process. Features such as shared memory are still available. However, there are some differences. These differences are due to the fact that Windows 95/98 is not as secure as Windows NT. Many times, Windows 95/98 trades off security for performance reasons. Windows 95/98 still has the concept of user-mode and kernel-mode code. The bottom 3GB is user-mode space, and the top 1GB is kernel-mode space. But the 3GB user-mode space can be further divided into shared space and private space for Windows 95/98. The 2GB to 3GB region is the shared address space for Windows 95/98 processes. For all processes, the page tables for this shared region point to the same set of physical pages.&lt;br /&gt;&lt;br /&gt;All the shared DLLs are loaded in the shared region. All the system DLLs–for example, KERNEL32.DLL and USER32.DLL–are shared DLLs. Also, a DLL’s code/data segment can be declared shared while compiling the DLL, and the DLL will get loaded in the shared region. The shared memory blocks are also allocated space in the shared region. In Windows 95/98, once a process maps a shared section, the section is visible to all processes. Because this section is mapped in shared region, other processes need not map it separately.&lt;br /&gt;&lt;br /&gt;There are advantages as well as disadvantages of having such a shared region. Windows 95/98 need not map the system DLLs separately for each process; the corresponding entries of page table directory can be simply copied for each process. Also, the system DLLs loaded in shared region can maintain global data about all the processes and separate subsystem processes are not required. Also, most system calls turn out to be simple function calls to the system DLLs, and as a result are very fast. In Windows NT, most system calls either cause a context switch to kernel mode or a context switch to the subsystem process, both of which are costly operations. For developers, loading system DLLs in a shared region means that they can now put global hooks for functions in system DLLs.&lt;br /&gt;&lt;br /&gt;For all these advantages, Windows 95/98 pays with security features. In Windows 95/98, any process can access all the shared data even if it has not mapped it. It can also corrupt the system DLLs and affect all processes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SUMMARY&lt;br /&gt;&lt;br /&gt;In this chapter, we discussed the memory management of Windows NT from three different perspectives. Memory management offers programmers a 32-bit flat address space for every process. A process cannot access another process’s memory or tamper with it, but two processes can share memory if they need to. Windows NT builds its memory management on top of the memory management facilities provided by the microprocessor. The 386 (and above) family of Intel microprocessors provides support for segmentation plus paging. The address translation mechanism first calculates the virtual address from the segment descriptor and the specified offset within the segment. The virtual address is then converted to a physical address using the page tables. The operating system can restrict access to certain memory regions by using the security mechanisms that are provided both at the segment level and the page level.&lt;br /&gt;&lt;br /&gt;Windows NT memory management provides the programmer with flat address space, data sharing, and so forth by selectively using the memory management features of the microprocessor. The virtual memory manager takes care of the paging and allows 4GB of virtual address space for each process, even when the entire system has much less physical memory at its disposal. The virtual memory manager keeps track of all the physical pages in the system through the page frame database (PFD). The system also keeps track of the virtual address space for each process using the virtual address descriptor (VAD) tree. Windows NT uses the copy-on-write mechanism for various purposes, especially for sharing the DLL data pages. The memory manager has an important part in switching the processor context when a process is scheduled for execution. Windows 95/98 memory management is similar to Windows NT memory management with the differences being due to the fact that Windows 95/98 is not as security conscious as Windows NT.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/5094571228741850298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/5094571228741850298' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5094571228741850298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5094571228741850298'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/11/memory-management-part-6.html' title='Memory Management -- Part 6'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-5156883772786109199</id><published>2007-11-22T11:31:00.002-08:00</published><updated>2007-11-22T11:32:52.950-08:00</updated><title type='text'>Memory Management -- Part 5</title><content type='html'>Memory Management&lt;br /&gt;&lt;br /&gt;IMPACT ON HOOKING&lt;br /&gt;&lt;br /&gt;Now we’ll look at the impact of the memory management scheme explained in the last section in the area of hooking DLL API calls. To hook a function from a DLL, you need to change the first few bytes from the function code. As you saw earlier, the DLL code is shared by all processes and is write protected so that a misbehaving process cannot affect other processes. Does this mean that you cannot hook a function in Windows NT? The answer is, “Hooking is possible under Windows NT, but you need to do a bit more work to comply with stability requirements.” Windows NT provides a system call, VirtualProtect, that you can use to change page attributes. Hence, hooking is now a two-step process: Change the attributes of the page containing DLL code to read-write, and then change the code bytes.&lt;br /&gt;&lt;br /&gt;Copy-on-Write&lt;br /&gt;“Eureka!” you might say, “I violated Windows NT security. I wrote to a shared page used by other processes also.” No! You did not do that. You changed only your copy of the DLL code. The DLL code page was being shared while you did not write to the page. The moment you wrote on that page, a separate copy of it was made, and the writes went to this copy. All other processes are safely using the original copy of the page. This is how Windows NT protects processes from each other while consuming as few resources as possible.&lt;br /&gt;&lt;br /&gt;The VirtualProtect() function does not mark the page as read-write–it keeps the page as read-only. Nevertheless, to distinguish this page from normal read-only pages, it is marked for copy-on-write. Windows NT uses one of the available PTE bits for doing this. When this page is written onto, because it is a read-only page, the processor raises a page fault exception. The page fault handler makes a copy of the page and modifies the page table of the faulting process accordingly. The new copy is marked as read-write so that the process can write to it.&lt;br /&gt;&lt;br /&gt;Windows NT itself uses the copy-on-write mechanism for various purposes. The DLL data pages are shared with the copy-on-write mark. Hence, whenever a process writes to a data page, it gets a personal copy of it. Other processes keep sharing the original copy, thus maximizing the sharing and improving memory usage.&lt;br /&gt;&lt;br /&gt;A DLL may be loaded in memory at different linear address for different processes. The memory references–for example, address for call instruction, address for a memory to register move instruction, and so on–in the DLL need to be adjusted (patched) depending on the linear address where the DLL gets loaded. This process is called as relocating the DLL. Obviously, relocation has to be done separately for each process. While relocating, Windows NT marks the DLL code pages as copy-on-write temporarily. Thus, only the pages requiring page relocation are copied per process. Other pages that do not have memory references in them are shared by all processes.&lt;br /&gt;&lt;br /&gt;This is the reason Microsoft recommends that a DLL be given a preferred base address and be loaded at that address. The binding of the DLL to a specific base address ensures that the DLL need not be relocated if it is loaded at the specified base address. Hence, if all processes load the DLL at the preferred base address, all can share the same copy of DLL code.&lt;br /&gt;&lt;br /&gt;The POSIX subsystem of Windows NT uses the copy-on-write mechanism to implement the fork system call. The fork system call creates a new process as a child of a calling process. The child process is a replica of the parent process, and it has the same state of code and data pages as the parent. Since these are two different processes, the data pages should not be shared by them. However, generally it is wasteful to make a copy of the parent’s data pages because in most cases the child immediately invokes the exec system call. The exec system call discards the current memory image of the process, loads a new executable module, and starts executing the new executable module. To avoid copying the data pages, the fork system call marks the data pages as copy-on-write. Hence, a data page is copied only if the parent or the child writes to it.&lt;br /&gt;&lt;br /&gt;Copy-on-write is an extremely important concept contributing to the efficiency of NT memory management.&lt;br /&gt;&lt;br /&gt;The following sample program demonstrates how copy-on-write works. By running two instances of the program, you can see how the concepts described in this section work. The application loads a DLL, which contains two functions and two data variables. One function does not refer to the outside world, so no relocations are required for it. The other function accesses one global variable, so it contains relocatable instructions or instructions that need relocation. One data variable is put in a shared data section so it will be shared across multiple instances of DLL. One variable is put in a default data section. The two functions are put in separate code sections just to make them page aligned.&lt;br /&gt;&lt;br /&gt;When you run the first instance of the application, the application loads and prints the physical addresses of two functions and two data variables. After this, you run the second instance of the same application. In the second instance, the application arranges to load the DLL at a different base address than that of the first instance. Then it prints the physical addresses of two functions and two data variables. Next, the application arranges to load the DLL at the same base address as that of the first instance. In this case, all physical pages are seen to be shared. Next, the application modifies the shared and nonshared variable and modifies the first few bytes of one function, and it prints the physical addresses for two functions and two variables again. We first discuss the code for this sample program and then describe how the output from the sample program demonstrates memory sharing and the effects of the copy-on-write mechanism.&lt;br /&gt;&lt;br /&gt;Listing 4-8: SHOWPHYS.C&lt;br /&gt;&lt;br /&gt;#include &lt;windows.h&gt;&lt;br /&gt;&lt;br /&gt;#include &lt;stdio.h&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &quot;gate.h&quot;&lt;br /&gt;&lt;br /&gt;#include &quot;getphys.h&quot;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;HANDLE hFileMapping;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* Imported function/variable addresses */&lt;br /&gt;&lt;br /&gt;static void *NonRelocatableFunction = NULL;&lt;br /&gt;&lt;br /&gt;static void *RelocatableFunction = NULL;&lt;br /&gt;&lt;br /&gt;static void *SharedVariable = NULL;&lt;br /&gt;&lt;br /&gt;static void *NonSharedVariable = NULL;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;HINSTANCE hDllInstance;&lt;br /&gt;&lt;br /&gt;The initial portion of the file contains the header inclusion and global variable definitions. The program demonstrates the use of various page attributes, especially to implement the copy-on-write mechanism. As described earlier, the program uses four different types of memory sections. The pointers to the four different types of memory sections are defined as global variables. The hDllInstance stores the instance of the instance handle of the DLL that contains the different kind of memory sections used in this demonstration.&lt;br /&gt;&lt;br /&gt;/* Loads MYDLL.DLL and initializes addresses of&lt;br /&gt;&lt;br /&gt; * imported functions/variables from MYDLL.DLL and&lt;br /&gt;&lt;br /&gt; * locks the imported areas&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;int LoadDllAndInitializeVirtualAddresses()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;hDllInstance = LoadLibrary(&quot;MYDLL.DLL&quot;);&lt;br /&gt;&lt;br /&gt;if (hDllInstance == NULL) {&lt;br /&gt;&lt;br /&gt;printf(&quot;Unable to load MYDLL.DLL\n&quot;);&lt;br /&gt;&lt;br /&gt;return -1;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;printf(&quot;MYDLL.DLL loaded at base address = %x\n&quot;,&lt;br /&gt;&lt;br /&gt;hDllInstance);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;NonRelocatableFunction =&lt;br /&gt;&lt;br /&gt;GetProcAddress(GetModuleHandle(&quot;MYDLL&quot;),&lt;br /&gt;&lt;br /&gt;&quot;_NonRelocatableFunction@0&quot;);&lt;br /&gt;&lt;br /&gt;RelocatableFunction =&lt;br /&gt;&lt;br /&gt;GetProcAddress(GetModuleHandle(&quot;MYDLL&quot;),&lt;br /&gt;&lt;br /&gt;&quot;_RelocatableFunction@0&quot;);&lt;br /&gt;&lt;br /&gt; SharedVariable =&lt;br /&gt;&lt;br /&gt;GetProcAddress(GetModuleHandle(&quot;MYDLL&quot;),&lt;br /&gt;&lt;br /&gt;&quot;SharedVariable&quot;);&lt;br /&gt;&lt;br /&gt; NonSharedVariable =&lt;br /&gt;&lt;br /&gt;GetProcAddress(GetModuleHandle(&quot;MYDLL&quot;),&lt;br /&gt;&lt;br /&gt;&quot;NonSharedVariable&quot;);&lt;br /&gt;&lt;br /&gt;if((!NonRelocatableFunction) ||&lt;br /&gt;&lt;br /&gt;(!RelocatableFunction) ||&lt;br /&gt;&lt;br /&gt;(!SharedVariable) ||&lt;br /&gt;&lt;br /&gt;(!NonSharedVariable)) {&lt;br /&gt;&lt;br /&gt;printf(&quot;Unable to get the virtual addresses for&quot;&lt;br /&gt;&lt;br /&gt;&quot;imports from MYDLL.DLL\n&quot;);&lt;br /&gt;&lt;br /&gt;FreeLibrary(hDllInstance);&lt;br /&gt;&lt;br /&gt;HDllInstance = 0;&lt;br /&gt;&lt;br /&gt;return -1;&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VirtualLock(NonRelocatableFunction, 1);&lt;br /&gt;&lt;br /&gt;VirtualLock(RelocatableFunction, 1);&lt;br /&gt;&lt;br /&gt;VirtualLock(SharedVariable, 1);&lt;br /&gt;&lt;br /&gt;VirtualLock(NonSharedVariable, 1);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;return 0;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The four different types of memory sections that we use for the demonstration reside in MYDLL.DLL. The LoadDllAndInitializeVirtualAddresses() function loads MYDLL.DLL in the calling process’s address space and initializes the global variables to point to different types of memory sections in the DLL. The function uses the GetProcAddress() function to get hold of pointers to the exported functions and variables in MYDLL.DLL. The function stores the instance handle for MYDLL.DLL in a global variable so that the FreeDll() function can later use it to unload the DLL. The function also locks the different memory sections so that the pages are loaded in memory and the page table entries are valid. Generally, Windows NT does not load the page table entries unless the virtual address is actually accessed. In other words, the memory won’t be paged in unless accessed. Also, the system can page out the memory that is not used for some time, again marking the page table entries as invalid. We use the VirtualLock() function to ensure that the pages of interest are always loaded and the corresponding page table entries remain valid.&lt;br /&gt;&lt;br /&gt;/* Unlocks the imported areas and frees the MYDLL.DLL&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;void FreeDll()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;VirtualUnlock(NonRelocatableFunction, 1);&lt;br /&gt;&lt;br /&gt;VirtualUnlock(RelocatableFunction, 1);&lt;br /&gt;&lt;br /&gt;VirtualUnlock(SharedVariable, 1);&lt;br /&gt;&lt;br /&gt;VirtualUnlock(NonSharedVariable, 1);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;FreeLibrary(hDllInstance);&lt;br /&gt;&lt;br /&gt;HDllInstance = 0;&lt;br /&gt;&lt;br /&gt;NonRelocatableFunction = NULL;&lt;br /&gt;&lt;br /&gt;RelocatableFunction = NULL;&lt;br /&gt;&lt;br /&gt;SharedVariable = NULL;&lt;br /&gt;&lt;br /&gt;NonSharedVariable = NULL;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The FreeDll() function uses the VirtualUnlock() function to unlock the memory locations locked by the LoadDllAndInitializeVirtualAddresses() function. The function unloads MYDLL.DLL after unlocking the memory locations from the DLL. As the DLL is unloaded, the global pointers to the memory sections in the DLL become invalid. The function sets all these pointers to NULL according to good programming practice.&lt;br /&gt;&lt;br /&gt;/* Converts the page attributes in readable form&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;char *GetPageAttributesString(unsigned int PageAttr)&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;static char buffer[100];&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;strcpy(buffer, &quot;&quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;strcat(buffer, (PageAttr&amp;0x01)? &quot;P  &quot;: &quot;NP &quot;);&lt;br /&gt;&lt;br /&gt;strcat(buffer, (PageAttr&amp;0x02)? &quot;RW &quot;: &quot;R  &quot;);&lt;br /&gt;&lt;br /&gt;strcat(buffer, (PageAttr&amp;0x04)? &quot;U &quot;: &quot;S &quot;);&lt;br /&gt;&lt;br /&gt;strcat(buffer, (PageAttr&amp;0x40)? &quot;D &quot;: &quot;  &quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;return buffer;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The GetPageAttributesString() function returns a string with characters showing the page attributes given the page attribute flags. The LSB in the page attributes indicates whether the page is present in memory or the page table entry is invalid. This information is printed as P or NP, which stands for present or not present. Similarly, R or RW means a read-only or read-write page; S or U means a supervisor-mode or a user-mode page; and D means a dirty page. The various page attributes are represented by different bits in the PageAttr parameter to this function. The function checks the bits and determines whether the page possesses the particular attributes.&lt;br /&gt;&lt;br /&gt;/* Displays virtual to physical address mapping&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;int DisplayVirtualAndPhysicalAddresses()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;DWORD pNonRelocatableFunction = 0;&lt;br /&gt;&lt;br /&gt;DWORD pRelocatableFunction = 0;&lt;br /&gt;&lt;br /&gt;DWORD pSharedVariable = 0;&lt;br /&gt;&lt;br /&gt;DWORD pNonSharedVariable = 0;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;DWORD aNonRelocatableFunction = 0;&lt;br /&gt;&lt;br /&gt;DWORD aRelocatableFunction = 0;&lt;br /&gt;&lt;br /&gt;DWORD aSharedVariable = 0;&lt;br /&gt;&lt;br /&gt;DWORD aNonSharedVariable = 0;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;\nVirtual to Physical address mapping\n&quot;);&lt;br /&gt;&lt;br /&gt;printf(&quot;\n------------------------------------\n&quot;);&lt;br /&gt;&lt;br /&gt;printf(&quot;Variable/function Virtual Physical Page\n&quot;);&lt;br /&gt;&lt;br /&gt;printf(&quot;   Address Address Attributes\n&quot;);&lt;br /&gt;&lt;br /&gt; printf(&quot;--------------------------------------\n&quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;GetPhysicalAddressAndPageAttributes(&lt;br /&gt;&lt;br /&gt;NonRelocatableFunction,&lt;br /&gt;&lt;br /&gt;&amp;pNonRelocatableFunction, &amp;aNonRelocatableFunction);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;GetPhysicalAddressAndPageAttributes(&lt;br /&gt;&lt;br /&gt;RelocatableFunction,&lt;br /&gt;&lt;br /&gt;&amp;pRelocatableFunction, &amp;aRelocatableFunction);&lt;br /&gt;&lt;br /&gt;GetPhysicalAddressAndPageAttributes(&lt;br /&gt;&lt;br /&gt;SharedVariable,&lt;br /&gt;&lt;br /&gt;  &amp;pSharedVariable,&lt;br /&gt;&lt;br /&gt;&amp;aSharedVariable);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;GetPhysicalAddressAndPageAttributes(&lt;br /&gt;&lt;br /&gt;NonSharedVariable,&lt;br /&gt;&lt;br /&gt;&amp;pNonSharedVariable,&lt;br /&gt;&lt;br /&gt;&amp;aNonSharedVariable);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;NonRelocatableFunction\t %8x\t %8x\t %s\n&quot;,&lt;br /&gt;&lt;br /&gt;NonRelocatableFunction,&lt;br /&gt;&lt;br /&gt;pNonRelocatableFunction,&lt;br /&gt;&lt;br /&gt;GetPageAttributesString(&lt;br /&gt;&lt;br /&gt;aNonRelocatableFunction));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;RelocatableFunction\t %8x\t %8x\t %s\n&quot;,&lt;br /&gt;&lt;br /&gt;  RelocatableFunction,&lt;br /&gt;&lt;br /&gt;pRelocatableFunction,&lt;br /&gt;&lt;br /&gt;GetPageAttributesString(&lt;br /&gt;&lt;br /&gt;aRelocatableFunction));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;SharedVariable\t %8x\t %8x\t %s\n&quot;,&lt;br /&gt;&lt;br /&gt;SharedVariable,&lt;br /&gt;&lt;br /&gt;pSharedVariable,&lt;br /&gt;&lt;br /&gt;GetPageAttributesString(&lt;br /&gt;&lt;br /&gt;aSharedVariable));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;NonSharedVariable\t %8x\t %8x\t %s\n&quot;,&lt;br /&gt;&lt;br /&gt;NonSharedVariable,&lt;br /&gt;&lt;br /&gt;pNonSharedVariable,&lt;br /&gt;&lt;br /&gt;GetPageAttributesString(&lt;br /&gt;&lt;br /&gt;aNonSharedVariable));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;------------------------------------\n\n&quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;return 0;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The DisplayVirtualAndPhysicalAddresses() function is a utility function that displays the virtual address, the physical address, and the page attributes for different memory sections. It uses the global pointers to the different sections in MYDLL.DLL initialized by the LoadDllAndInitializeVirtualAddresses() function. It uses the GetPhysicalAddressAndPageAttributes() function to get hold of the physical page address and the page attributes for the given virtual address. The first parameter to the GetPhysicalAddressAndPageAttributes() function is the input virtual address. The function fills in the physical address for the input virtual address in the memory location pointed to by the second parameter and the page attributes in the location pointed to by the third parameter.&lt;br /&gt;&lt;br /&gt;int FirstInstance()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;printf(&quot;***This is the first instance of the&quot;&lt;br /&gt;&lt;br /&gt;&quot; showphys program***\n\n&quot;);&lt;br /&gt;&lt;br /&gt;printf(&quot;Loading DLL MYDLL.DLL\n&quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if (LoadDllAndInitializeVirtualAddresses()!=0) {&lt;br /&gt;&lt;br /&gt;return -1;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;DisplayVirtualAndPhysicalAddresses();&lt;br /&gt;&lt;br /&gt;printf(&quot;Now Run another copy of showphys ...\n&quot;);&lt;br /&gt;&lt;br /&gt;getchar();&lt;br /&gt;&lt;br /&gt;FreeDll();&lt;br /&gt;&lt;br /&gt;}</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/5156883772786109199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/5156883772786109199' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5156883772786109199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/5156883772786109199'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/11/memory-management-part-5_22.html' title='Memory Management -- Part 5'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-8656673786769986022</id><published>2007-11-22T11:31:00.001-08:00</published><updated>2007-11-22T11:31:40.909-08:00</updated><title type='text'>Memory Management -- Part 5</title><content type='html'>Memory Management&lt;br /&gt;&lt;br /&gt;The first two members dictate the address range represented by the VAD node. Each VAD tree node maintains a pointer to the parent node and a pointer to the left child and the right child. The VAD tree is a binary tree. For every node in the tree, the left subtree consists of nodes representing lower address ranges, and the right subtree consists of nodes representing the higher address ranges. The last member in the VAD node is the flags for the address range.&lt;br /&gt;&lt;br /&gt;The VADDUMP.C file has a few other global variables apart from the VadInfoArray. A couple of global variables are used while locating the root of the VAD tree. The PEB of a process points to the VAD tree root for that process. The offset of this pointer inside the PEB varies with the Windows NT version. We set the VadRootOffset to the appropriate offset value of the VAD root pointer depending on the Windows NT version. There is a similar problem of Windows NT version dependency while accessing the PEB for the process. We use the Thread Environment Block (TEB) to get to the PEB. One field in TEB points to the PEB, but the offset of this field inside the TEB structure varies with the Windows NT version. We set the PebOffset variable to the appropriate offset value of the PEB pointer inside the TEB structure depending on the Windows NT version. Another global variable, NtVersion, stores the version of Windows NT running on the machine.&lt;br /&gt;&lt;br /&gt;That leaves us with two more global variables, namely, VadInfoArrayIndex and VadTreeRoot. The VadInfoArrayIndex is the number of initialized entries in the VadInfoArray. The VadInfoArray entries after VadInfoArrayIndex are free. The VadTreeRoot variable stores the root of the VAD tree.&lt;br /&gt;&lt;br /&gt;The sample has been tested on Windows NT 3.51, 4.0 and Windows 2000 beta2. The sample will run on other versions of Windows 2000, provided the offsets of VadRoot and PEB remain same.&lt;br /&gt;&lt;br /&gt;/* Recursive function which walks the vad tree and&lt;br /&gt;&lt;br /&gt; * fills up the global VadInfoArray with the Vad&lt;br /&gt;&lt;br /&gt; * entries. Function is limited by the&lt;br /&gt;&lt;br /&gt; * MAX_VAD_ENTRIES. Other VADs after this are not&lt;br /&gt;&lt;br /&gt; * stored&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;void _stdcall VadTreeWalk(PVAD VadNode)&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;if (VadNode == NULL) {&lt;br /&gt;&lt;br /&gt;return;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if (VadInfoArrayIndex &gt;= MAX_VAD_ENTRIES) {&lt;br /&gt;&lt;br /&gt;return;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VadTreeWalk(VadNode-&gt;LeftLink);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; VadInfoArray[VadInfoArrayIndex].VadLocation = VadNode;&lt;br /&gt;&lt;br /&gt;VadInfoArray[VadInfoArrayIndex].Vad.StartingAddress =&lt;br /&gt;&lt;br /&gt;VadNode-&gt;StartingAddress;&lt;br /&gt;&lt;br /&gt;VadInfoArray[VadInfoArrayIndex].Vad.EndingAddress =&lt;br /&gt;&lt;br /&gt;VadNode-&gt;EndingAddress;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if (NtVersion == 5) {&lt;br /&gt;&lt;br /&gt;(DWORD)VadInfoArray[VadInfoArrayIndex].&lt;br /&gt;&lt;br /&gt;Vad.StartingAddress &lt;&lt;= 12;&lt;br /&gt;&lt;br /&gt;(DWORD)VadInfoArray[VadInfoArrayIndex].&lt;br /&gt;&lt;br /&gt;Vad.EndingAddress += 1;&lt;br /&gt;&lt;br /&gt;(DWORD)VadInfoArray[VadInfoArrayIndex].&lt;br /&gt;&lt;br /&gt;Vad.EndingAddress &lt;&lt;= 12;&lt;br /&gt;&lt;br /&gt;(DWORD)VadInfoArray[VadInfoArrayIndex].&lt;br /&gt;&lt;br /&gt;Vad.EndingAddress -= 1;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VadInfoArray[VadInfoArrayIndex].Vad.ParentLink =&lt;br /&gt;&lt;br /&gt;VadNode-&gt;ParentLink;&lt;br /&gt;&lt;br /&gt;VadInfoArray[VadInfoArrayIndex].Vad.LeftLink =&lt;br /&gt;&lt;br /&gt;VadNode-&gt;LeftLink;&lt;br /&gt;&lt;br /&gt;VadInfoArray[VadInfoArrayIndex].Vad.RightLink =&lt;br /&gt;&lt;br /&gt;VadNode-&gt;RightLink;&lt;br /&gt;&lt;br /&gt;VadInfoArray[VadInfoArrayIndex].Vad.Flags =&lt;br /&gt;&lt;br /&gt;VadNode-&gt;Flags;&lt;br /&gt;&lt;br /&gt;VadInfoArrayIndex++;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; VadTreeWalk(VadNode-&gt;RightLink);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The VadTreeWalk() function is executed in the kernel mode using the callgate mechanism. The function traverses the VAD tree in the in-order fashion and fills up the VadInfoArray. The function simply returns if the node pointer parameter is NULL or the VadInfoArray is full. Otherwise, the function recursively calls itself for the left subtree. The recursion is terminated when the left child pointer is NULL. The function then fills up the next free entry in the VadInfoArray and increments the VadInfoArrayIndex to point to the next free entry. Windows 2000 stores the page numbers instead of the actual addresses in VAD. Hence, for Windows 2000, we need to calculate the starting address and the ending address from the page numbers stored in these fields. As the last step in the in-order traversal, the function issues a self-recursive to process the right subtree.&lt;br /&gt;&lt;br /&gt;/* C function called through assembly stub */&lt;br /&gt;&lt;br /&gt;void _stdcall CFuncDumpVad(PVAD VadRoot)&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;VadTreeRoot = VadRoot;&lt;br /&gt;&lt;br /&gt;VadInfoArrayIndex = 0;&lt;br /&gt;&lt;br /&gt;VadTreeWalk(VadRoot);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The CfuncDumpVad is the caller of the VadTreeWalk() function. It just initializes the global variables used by the VadTreeWalk() function and calls the VadTreeWalk() function for the root of the VAD tree.&lt;br /&gt;&lt;br /&gt;/* Displays the Vad tree */&lt;br /&gt;&lt;br /&gt;void VadTreeDisplay()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;int i;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;VadRoot is located @%08x\n\n&quot;,&lt;br /&gt;&lt;br /&gt;VadTreeRoot);&lt;br /&gt;&lt;br /&gt;printf(&quot;Vad@\t Starting\t Ending\t Parent\t &quot;&lt;br /&gt;&lt;br /&gt;&quot;LeftLink\t RightLink\n&quot;);&lt;br /&gt;&lt;br /&gt;for (i=0; i &lt; VadInfoArrayIndex; i++) {&lt;br /&gt;&lt;br /&gt;printf(&quot;%08x  %08x  %08x  %8x  %08x  %08x\n&quot;,&lt;br /&gt;&lt;br /&gt;VadInfoArray[i].VadLocation,&lt;br /&gt;&lt;br /&gt;VadInfoArray[i].Vad.StartingAddress,&lt;br /&gt;&lt;br /&gt;VadInfoArray[i].Vad.EndingAddress,&lt;br /&gt;&lt;br /&gt;VadInfoArray[i].Vad.ParentLink,&lt;br /&gt;&lt;br /&gt;VadInfoArray[i].Vad.LeftLink,&lt;br /&gt;&lt;br /&gt;VadInfoArray[i].Vad.RightLink);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;printf(&quot;\n\n&quot;);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The VadTreeDisplay() function is a very simple function that is executed in user mode. The function iterates through all the entries initialized by the VadTreeWalk() function and prints the entries. Essentially, the function prints the VAD tree in the infix order because the VadTreeWalk() function dumps the VAD tree in the infix order.&lt;br /&gt;&lt;br /&gt;void SetDataStructureOffsets()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;switch (NtVersion) {&lt;br /&gt;&lt;br /&gt;case 3:&lt;br /&gt;&lt;br /&gt;PebOffset = 0x40;&lt;br /&gt;&lt;br /&gt;VadRootOffset = 0x170;&lt;br /&gt;&lt;br /&gt;break;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;case 4:&lt;br /&gt;&lt;br /&gt;PebOffset = 0x44;&lt;br /&gt;&lt;br /&gt;VadRootOffset = 0x170;&lt;br /&gt;&lt;br /&gt;break;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;case 5:&lt;br /&gt;&lt;br /&gt;PebOffset = 0x44;&lt;br /&gt;&lt;br /&gt;VadRootOffset = 0x194;&lt;br /&gt;&lt;br /&gt;break;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;As we described earlier, the offset of the PEB pointer within TEB and the offset of the VAD root pointer within the PEB are dependent on the Windows NT version. The SetDataStructureOffsets() function sets the global variables indicating these offsets depending on the Windows NT version.&lt;br /&gt;&lt;br /&gt;main()&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;WORD CallGateSelector;&lt;br /&gt;&lt;br /&gt;int rc;&lt;br /&gt;&lt;br /&gt;short farcall[3];&lt;br /&gt;&lt;br /&gt;void DumpVad(void);&lt;br /&gt;&lt;br /&gt;void *ptr;&lt;br /&gt;&lt;br /&gt;OSVERSIONINFO VersionInfo;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);&lt;br /&gt;&lt;br /&gt;if (GetVersionEx(&amp;VersionInfo) == TRUE) {&lt;br /&gt;&lt;br /&gt;NtVersion = VersionInfo.dwMajorVersion;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if ((NtVersion &lt; 3)||(NtVersion &gt; 5)) {&lt;br /&gt;&lt;br /&gt;printf(&quot;Unsupported NT version, exiting...&quot;);&lt;br /&gt;&lt;br /&gt;return 0;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;SetDataStructureOffsets();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* Creates call gate to read vad tree from Ring 3&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;rc = CreateCallGate(DumpVad, 0, &amp;CallGateSelector);&lt;br /&gt;&lt;br /&gt;if (rc != SUCCESS) {&lt;br /&gt;&lt;br /&gt;printf(&quot;CreateCallGate failed, rc=%x\n&quot;, rc);&lt;br /&gt;&lt;br /&gt;return 1;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;farcall[2] = CallGateSelector;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;_asm {&lt;br /&gt;&lt;br /&gt;call fword ptr [farcall]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;Dumping the Vad tree ...\n\n&quot;);&lt;br /&gt;&lt;br /&gt;VadTreeDisplay();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;Allocating memory using VirtualAlloc&quot;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ptr = VirtualAlloc(NULL, 4096, MEM_COMMIT,&lt;br /&gt;&lt;br /&gt;PAGE_READONLY);&lt;br /&gt;&lt;br /&gt;if (ptr == NULL) {&lt;br /&gt;&lt;br /&gt;printf(&quot;Unable to allocate memory\n&quot;);&lt;br /&gt;&lt;br /&gt;goto Quit;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;printf(&quot;\nMemory allocated @%x\n&quot;, ptr);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;_asm {&lt;br /&gt;&lt;br /&gt;call fword ptr [farcall]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;printf(&quot;\n\nDumping the Vad tree again...\n\n&quot;);&lt;br /&gt;&lt;br /&gt;VadTreeDisplay();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Quit:&lt;br /&gt;&lt;br /&gt;rc = FreeCallGate(CallGateSelector);&lt;br /&gt;&lt;br /&gt;if (rc != SUCCESS) {&lt;br /&gt;&lt;br /&gt;printf(&quot;FreeCallGate failed, Selector=%x, rc=%x\n&quot;,&lt;br /&gt;&lt;br /&gt;    CallGateSelector, rc);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; return 0;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;The main() function starts by getting the Windows NT version and calling SetDataStructureOffsets() to set the global variables storing the offsets for the PEB and the VAD tree root. It then creates a callgate in the same manner as in the SHOWDIR sample program. Issuing a call through this callgate ultimately results in the execution of the VadTreeWalk() function that fills up the VadInfoArray. The main() function then calls the VadTreeDisplay() function to print the VadInfoArray entries.&lt;br /&gt;&lt;br /&gt;We also show you the change in the VAD tree due to memory allocation in this sample program. After printing the VAD tree once, the program allocates a chunk of memory. Then, the program issues the callgate call again and prints the VAD tree after returning from the call. You can observe the updates that happened to the VAD tree because of the memory allocation. The program frees up the callgate before exiting.&lt;br /&gt;&lt;br /&gt;Listing 4-6: RING0.ASM&lt;br /&gt;&lt;br /&gt;.386&lt;br /&gt;&lt;br /&gt;.model small&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;.code&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public _DumpVad&lt;br /&gt;&lt;br /&gt;extrn _CFuncDumpVad@4:near&lt;br /&gt;&lt;br /&gt;extrn _PebOffset:near&lt;br /&gt;&lt;br /&gt;extrn _VadRootOffset:near&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;include ..\include\undocnt.inc&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;_DumpVad proc&lt;br /&gt;&lt;br /&gt;Ring0Prolog&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;;Gets the current thread&lt;br /&gt;&lt;br /&gt;MOV EAX,FS:[00000124h]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;;Gets the current process&lt;br /&gt;&lt;br /&gt;ADD EAX, DWORD PTR [_PebOffset]&lt;br /&gt;&lt;br /&gt;MOV EAX,[EAX]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;;Push Vad Tree root&lt;br /&gt;&lt;br /&gt;ADD EAX, DWORD PTR [_VadRootOffset]&lt;br /&gt;&lt;br /&gt;MOV EAX, [EAX]&lt;br /&gt;&lt;br /&gt;PUSH EAX&lt;br /&gt;&lt;br /&gt;CALL _CFuncDumpVad@4&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ring0Epilog&lt;br /&gt;&lt;br /&gt;RETF&lt;br /&gt;&lt;br /&gt;_DumpVad endp&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;The function to be called from the callgate needs to be written in the Assembly language for reasons already described. The DumpVad() function gets hold of the VAD root pointer and calls the CFuncDumpVad() function that dumps the VAD tree in the VadInfoArray. The function gets hold of the VAD root from the PEB after getting hold of the PEB from the TEB. The TEB of the currently executing thread is always pointed to by FS:128h. As described earlier, the offset of the VAD root pointer inside PEB and the offset of the PEB pointer inside the TEB vary with the Windows NT version. The DumpVad() function uses the offset values stored in the global variable by the SetDataStructureOffsets() function.&lt;br /&gt;&lt;br /&gt;Listing 4-7 presents the output from an invocation of the VADDUMP program. Note that the VAD tree printed after allocating memory at address 0x300000 shows an additional entry for that address range.&lt;br /&gt;&lt;br /&gt;Listing 4-7: Program output&lt;br /&gt;&lt;br /&gt;Dumping the Vad tree...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VadRoot is located @fe21a9c8&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Vad@      Starting  Ending    Parent    LeftLink RightLink&lt;br /&gt;&lt;br /&gt;fe216b08  00010000  00010fff  fe21a9c8  00000000  fe25a0e8&lt;br /&gt;&lt;br /&gt;fe25a0e8  00020000  00020fff  fe216b08  00000000  fe275da8&lt;br /&gt;&lt;br /&gt;fe275da8  00030000  0012ffff  fe25a0e8  00000000  fe22a428&lt;br /&gt;&lt;br /&gt;fe22a428  00130000  00130fff  fe275da8  00000000  fe26b328&lt;br /&gt;&lt;br /&gt;fe26b328  00140000  0023ffff  fe22a428  00000000  fe210fc8&lt;br /&gt;&lt;br /&gt;fe210fc8  00240000  0024ffff  fe26b328  00000000  fe21a8c8&lt;br /&gt;&lt;br /&gt;fe21a8c8  00250000  00258fff  fe210fc8  00000000  fe21be68&lt;br /&gt;&lt;br /&gt;fe21be68  00260000  0026dfff  fe21a8c8  00000000  fe215dc8&lt;br /&gt;&lt;br /&gt;fe215dc8  00270000  002b0fff  fe21be68  00000000  fe231e88&lt;br /&gt;&lt;br /&gt;fe231e88  002c0000  002c0fff  fe215dc8  00000000  fe2449e8&lt;br /&gt;&lt;br /&gt;fe2449e8  002d0000  002dffff  fe231e88  00000000  fe21cb48&lt;br /&gt;&lt;br /&gt;fe21cb48  002e0000  002e0fff  fe2449e8  00000000  fe23b7a8&lt;br /&gt;&lt;br /&gt;fe23b7a8  002f0000  002fffff  fe21cb48  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe21a9c8  00400000  0040cfff         0  fe216b08  fe23c488&lt;br /&gt;&lt;br /&gt;fe21b3e8  10000000  1000dfff  fe2333e8  00000000  fe226348&lt;br /&gt;&lt;br /&gt;fe2176c8  77e20000  77e4bfff  fe226348  00000000  fe2326e8&lt;br /&gt;&lt;br /&gt;fe2152c8  77e50000  77e54fff  fe2326e8  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe2326e8  77e60000  77e9bfff  fe2176c8  fe2152c8  00000000&lt;br /&gt;&lt;br /&gt;fe226348  77ea0000  77ed7fff  fe21b3e8  fe2176c8  fe2197c8&lt;br /&gt;&lt;br /&gt;fe2197c8  77ee0000  77f12fff  fe226348  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe2333e8  77f20000  77f73fff  fe23c488  fe21b3e8  00000000&lt;br /&gt;&lt;br /&gt;fe23c488  77f80000  77fcdfff  fe21a9c8  fe2333e8  fe25aa88&lt;br /&gt;&lt;br /&gt;fe22b408  7f2d0000  7f5cffff  fe25aa88  00000000  fe22c4a8&lt;br /&gt;&lt;br /&gt;fe22c4a8  7f5f0000  7f7effff  fe22b408  00000000  fe23f5e8&lt;br /&gt;&lt;br /&gt;fe23f5e8  7ff70000  7ffaffff  fe22c4a8  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe25aa88  7ffb0000  7ffd3fff  fe23c488  fe22b408  fe218288&lt;br /&gt;&lt;br /&gt;fe21da88  7ffde000  7ffdefff  fe218288  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe218288  7ffdf000  7ffdffff  fe25aa88  fe21da88  00000000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Allocating memory using VirtualAlloc&lt;br /&gt;&lt;br /&gt;Memory allocated @300000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dumping the Vad tree again...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VadRoot is located @fe21a9c8&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Vad@      Starting  Ending    Parent    LeftLink RightLink&lt;br /&gt;&lt;br /&gt;fe216b08  00010000  00010fff  fe21a9c8  00000000  fe25a0e8&lt;br /&gt;&lt;br /&gt;fe25a0e8  00020000  00020fff  fe216b08  00000000  fe275da8&lt;br /&gt;&lt;br /&gt;fe275da8  00030000  0012ffff  fe25a0e8  00000000  fe22a428&lt;br /&gt;&lt;br /&gt;fe22a428  00130000  00130fff  fe275da8  00000000  fe26b328&lt;br /&gt;&lt;br /&gt;fe26b328  00140000  0023ffff  fe22a428  00000000  fe210fc8&lt;br /&gt;&lt;br /&gt;fe210fc8  00240000  0024ffff  fe26b328  00000000  fe21a8c8&lt;br /&gt;&lt;br /&gt;fe21a8c8  00250000  00258fff  fe210fc8  00000000  fe21be68&lt;br /&gt;&lt;br /&gt;fe21be68  00260000  0026dfff  fe21a8c8  00000000  fe215dc8&lt;br /&gt;&lt;br /&gt;fe215dc8  00270000  002b0fff  fe21be68  00000000  fe231e88&lt;br /&gt;&lt;br /&gt;fe231e88  002c0000  002c0fff  fe215dc8  00000000  fe2449e8&lt;br /&gt;&lt;br /&gt;fe2449e8  002d0000  002dffff  fe231e88  00000000  fe21cb48&lt;br /&gt;&lt;br /&gt;fe21cb48  002e0000  002e0fff  fe2449e8  00000000  fe23b7a8&lt;br /&gt;&lt;br /&gt;fe23b7a8  002f0000  002fffff  fe21cb48  00000000  fe27b628&lt;br /&gt;&lt;br /&gt;fe27b628  00300000  00300fff  fe23b7a8  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe21a9c8  00400000  0040cfff         0  fe216b08  fe23c488&lt;br /&gt;&lt;br /&gt;fe21b3e8  10000000  1000dfff  fe2333e8  00000000  fe226348&lt;br /&gt;&lt;br /&gt;fe2176c8  77e20000  77e4bfff  fe226348  00000000  fe2326e8&lt;br /&gt;&lt;br /&gt;fe2152c8  77e50000  77e54fff  fe2326e8  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe2326e8  77e60000  77e9bfff  fe2176c8  fe2152c8  00000000&lt;br /&gt;&lt;br /&gt;fe226348  77ea0000  77ed7fff  fe21b3e8  fe2176c8  fe2197c8&lt;br /&gt;&lt;br /&gt;fe2197c8  77ee0000  77f12fff  fe226348  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe2333e8  77f20000  77f73fff  fe23c488  fe21b3e8  00000000&lt;br /&gt;&lt;br /&gt;fe23c488  77f80000  77fcdfff  fe21a9c8  fe2333e8  fe25aa88&lt;br /&gt;&lt;br /&gt;fe22b408  7f2d0000  7f5cffff  fe25aa88  00000000  fe22c4a8&lt;br /&gt;&lt;br /&gt;fe22c4a8  7f5f0000  7f7effff  fe22b408  00000000  fe23f5e8&lt;br /&gt;&lt;br /&gt;fe23f5e8  7ff70000  7ffaffff  fe22c4a8  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe25aa88  7ffb0000  7ffd3fff  fe23c488  fe22b408  fe218288&lt;br /&gt;&lt;br /&gt;fe21da88  7ffde000  7ffdefff  fe218288  00000000  00000000&lt;br /&gt;&lt;br /&gt;fe218288  7ffdf000  7ffdffff  fe25aa88  fe21da88  00000000&lt;br /&gt;&lt;br /&gt;The output of the VADDUMP program does not really look like a tree. You have to trace through the output to get the tree structure. The entry with a null parent link is the root of the tree. Once you find the root, you can follow the child pointers. To follow a child pointer, search the pointer in the first column, named Vad@, in the output. The Vad entry with the same Vad@ is the entry for the child that you are looking for. An all-zero entry for a left/right child pointer indicates that there is no left/right subtree for the node. Figure 4-5 shows a partial tree constructed from the output shown previously.</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/8656673786769986022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/8656673786769986022' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/8656673786769986022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/8656673786769986022'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/11/memory-management-part-5.html' title='Memory Management -- Part 5'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8742242208674094237.post-7893975140383098101</id><published>2007-11-22T11:29:00.000-08:00</published><updated>2007-11-22T11:30:50.610-08:00</updated><title type='text'>Memory Management -- Part 3</title><content type='html'>&lt;pre&gt;&lt;br /&gt;Memory Management&lt;br /&gt;&lt;br /&gt;Let’s analyze, one step at a time, why the two entries are different. The page tables themselves need to be mapped onto some linear address. When Windows NT needs to access the page tables, it uses this linear address range. To represent 4GB of memory divided into 1MB pages of 4K each, we need 1K page tables each having 1K entries. To map these 1K page tables, Windows NT reserves 4MB of linear address space in each process. As we saw earlier, each process has a different set of page tables. Whatever the process, Windows NT maps the page tables on the linear address range from 0xC0000000 to 0xC03FFFFF. Let’s call this linear address range as the page table address range. In other words, the page table address range maps to different page tables–that is, to different physical pages–for different processes. As you may have noticed, the page table addresses range falls in the kernel address space. Windows NT cannot map this crucial system data structure in the user address space and allow user-mode processes to play with the memory. Ultimately, the result is that two processes cannot share pages in the page table address range although the addresses lie in the kernel-mode address range.&lt;br /&gt;&lt;br /&gt;Exactly one page table is required to map 4MB address space because each page table has 1K entries and each entry corresponds to a 4K page. Consequently, Windows NT cannot share the page table corresponding to the page table address range. This accounts for one of the two mysterious entries in the page table directory. However, the entry’s mystery does not end here–there is one more subtle twist to this story. The physical address specified in this entry matches the physical address of the page table directory. The obvious conclusion is that the page table directory acts also as the page table for the page table address range. This is  possible because the formats of the page table directory entry and PTE are the same on 80386.&lt;br /&gt;&lt;br /&gt;The processor carries out an interesting sequence of actions when the linear address within the page table address range is translated to a physical address. Let’s say that the CR3 register points to page X. As the first step in the address translation process, the processor treats the page X as the page table directory and finds out the page table for the given linear address. The page table happens to be page X again. The processor now treats page X as the required page table and finds out the physical address from it. A more interesting case occurs when the operating system is accessing the page table directory itself. In this case, the physical address also falls in page X!&lt;br /&gt;&lt;br /&gt;Let’s now turn to the second mysterious entry. The 4MB area covered by this page directory entry is internally referred to as hyperspace. This area is used for mapping the physical pages belonging to other processes into virtual address space. For example, a function such as MmMapPageInHyperspace() uses the virtual addresses in this range. This area is also used during the early stages of process creation. For example, when a parent process such as PROGMAN.EXE spawns a child process such as NOTEPAD.EXE, PROGMAN.EXE has to create the address space for NOTEPAD.EXE. This is done as a part of the MmCreateProcessAddressSpace() function. For starting any process, an address space must be created for the process. Address space is nothing but page directory. Also, the upper-half entries of page directory are common for all processes except for the two entries that we have already discussed. These entries need to be created for the process being spawned. The MmCreateProcessAddressSpace() function allocates three pages of memory: the first page for the page directory, the second page for holding the hyperspace page table entries, and the third page for holding the working set information for the process being spawned.&lt;br /&gt;&lt;br /&gt;Once these pages are allocated, the function maps the first physical page in the address space using the MmMapPageInHyperSpace() function. Note that the MmMapPageInHyperSpace() function runs in the context of PROGMAN.EXE. Now the function copies the page directory entries in the upper half of the page directory to the mapped hyperspace virtual address. In short, PROGMAN.EXE creates the page directory for the NOTEPAD.EXE.&lt;br /&gt;&lt;br /&gt;Windows NT supports memory-mapped files. When two processes map the same file, they share the same set of physical pages. Hence, memory-mapped files can be used for sharing memory. In fact, Windows NT itself uses memory-mapped files to load DLLs and executables. If two processes map the same DLL, they automatically share the DLL pages. The memory-mapped files are implemented using the section object under Windows NT. A data structure called PROTOPTE is associated with each section object. This data structure is a variable-length structure based on the size of the section. This data structure contains a 4-byte entry for each page in the virtual address space mapped by the section object. Each 4-byte entry has the same structure as that of the PTE. When the page is not being used by any of the processes, the protopte entry is invalid and contains enough information to get the page back. In this case, the CPU PTE contains a fixed value that is 0xFFFFF480, which indicates that accessing this page will be considered a protopte fault.&lt;br /&gt;&lt;br /&gt;Now comes the toughest of all questions: &quot;How can Windows NT give away 4GB of memory to each process when there is far less physical RAM available on the board?&quot; Windows NT, as well as all other operating systems that allow more address space than actual physical memory, uses a technique called virtual memory to achieve this. In the next section, we discuss virtual memory management in Windows NT.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VIRTUAL MEMORY MANAGEMENT&lt;br /&gt;&lt;br /&gt;The basic idea behind virtual memory is very simple. For each process, the operating system maps few addresses to real physical memory because RAM is expensive and relatively rare. Remaining memory for each process is really maintained on secondary storage (usually a hard disk). That’s why it is called virtual memory. The addresses that are not mapped on physical RAM are marked as such. Whenever a process accesses such an address, the operating system brings the data into memory from secondary storage. If the operating system runs out of physical RAM, some data is thrown out to make space. We can always get back this data because a copy is maintained on secondary storage. The data to be thrown out is decided by the replacement policy. Windows NT uses First-In-First-Out (FIFO) replacement policy. According to this policy, the oldest data (that is, the data that was brought in the RAM first) is thrown out whenever there is a space crunch.&lt;br /&gt;&lt;br /&gt;To implement virtual memory management, Windows NT needs to maintain a lot of data. First, it needs to maintain whether each address is mapped to physical RAM or the data is to be brought in from secondary storage when a request with the address comes. Maintaining this information for each byte itself takes a lot of space (actually, more space than the address space for which the information is to be maintained). So Windows NT breaks the address space into 4KB pages and maintains this information in page tables. As we saw earlier, a page table entry (PTE) consists of the address of the physical page (if the page is mapped to physical RAM) and attributes of the page. Since the processor heavily depends on PTEs for address translation, the structure of PTE is processor dependent.&lt;br /&gt;&lt;br /&gt;If a page is not mapped onto physical RAM, Windows NT marks the page as invalid. Any access to this page causes a page fault, and the page fault handler can bring in the page from the secondary storage. To be more specific, when the page contains DLL code or executable module code, the page is brought in from the DLL or executable file. When the page contains data, it is brought in from the swap file. When the page represents a memory-mapped file area, it is brought in from the corresponding file. Windows NT needs to keep track of free physical RAM so that it can allocate space for a page brought in from secondary storage in case of a page fault. This information is maintained in a kernel data structure called the Page Frame Database (PFD). The PFD also maintains a FIFO list of in-memory pages so that it can decide on pages to throw out in case of a space crunch.&lt;br /&gt;&lt;br /&gt;Before throwing out a page, Windows NT must ensure that the page is not dirty. Otherwise, it needs to write that page to secondary storage before throwing it out. If the page is not shared, the PFD contains the pointer to PTE so that if the operating system decides to throw out a particular page, it can then go back and mark the PTE as invalid. If the page is shared, the PFD contains a pointer to the corresponding PROTOPTE entry. In this case, the PFD also contains a reference count for the page. A page can be thrown out only if its reference count is 0. In general, the PFD maintains the status of every physical page.&lt;br /&gt;&lt;br /&gt;The PFD is an array of 24-byte entries, one for each physical page. Hence, the size of this array is equal to the number of physical pages that are stored in a kernel variable, namely, MmNumberOfPhysicalPages. The pointer to this array is stored in a kernel variable, namely, MmpfnDatabase. A physical page can be in several states–for example, it can be in-use, free, free but dirty, and so on. A PFD entry is linked in a doubly linked list, depending on the state of the physical page represented by it. For example, the PFD entry representing a free page is linked in the free pages list. Figure 4-4 shows these lists linked through the PFD. The forward links are shown on the left side of the PFD, and the backward links are shown on the right side.&lt;br /&gt;&lt;br /&gt;There are in all six kinds of lists. The heads of these lists are stored in following kernel variables:&lt;br /&gt;&lt;br /&gt;MmStandbyPageListHead&lt;br /&gt;&lt;br /&gt;MmModifiedNoWritePageListHead&lt;br /&gt;&lt;br /&gt;MmModifiedPageListHead&lt;br /&gt;&lt;br /&gt;MmFreePageListHead&lt;br /&gt;&lt;br /&gt;MmBadPageListHead&lt;br /&gt;&lt;br /&gt;MmZeroedPageListHead&lt;br /&gt;&lt;br /&gt;All these list heads are actually structures of 16 bytes each. Here is the structure definition:&lt;br /&gt;&lt;br /&gt;typedef struct PageListHead {&lt;br /&gt;&lt;br /&gt; DWORD NumberOfPagesInList,&lt;br /&gt;&lt;br /&gt; DWORD TypeOfList,&lt;br /&gt;&lt;br /&gt; DWORD FirstPage,&lt;br /&gt;&lt;br /&gt; DWORD LastPage&lt;br /&gt;&lt;br /&gt;} PageListHead_t;&lt;br /&gt;&lt;br /&gt;The FirstPage field can be used as an index into the PFD. The PFD entry contains a pointer to the next page. Using this, you can traverse any of the lists. Here is the structure definition for the PFD entry:&lt;br /&gt;&lt;br /&gt;typedef struct PfdEntry {&lt;br /&gt;&lt;br /&gt; DWORD NextPage,&lt;br /&gt;&lt;br /&gt; void *PteEntry/*PpteEntry,&lt;br /&gt;&lt;br /&gt; DWORD PrevPage,&lt;br /&gt;&lt;br /&gt; DWORD PteReferenceCount,&lt;br /&gt;&lt;br /&gt; void *OriginalPte,&lt;br /&gt;&lt;br /&gt; DWORD Flags;&lt;br /&gt;&lt;br /&gt;} PfdEntry_t;&lt;br /&gt;&lt;br /&gt;Using this, you can easily write a program to dump the PFD. However, there is one problem: kernel variables, such as list heads, MmPfnDatabase, and MmNumberOfPhysicalPages, are not exported. Therefore, you have to deal with absolute addresses, which makes the program dependent on the Windows NT version and build type.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VIRTUAL ADDRESS DESCRIPTORS&lt;br /&gt;&lt;br /&gt;Along with the free physical pages, Windows NT also needs to keep track of the virtual address space allocation for each process. Whenever a process allocates a memory block–for example, to load a DLL–Windows NT checks for a free block in the virtual address space, allocates virtual address space, and updates the virtual address map accordingly. The most obvious place to maintain this information is page tables. For each process, Windows NT maintains separate page tables. There are 1 million pages, and each page table entry is 4 bytes. Hence, full page tables for a single process would take 4MB of RAM! There is a solution to this: Page tables themselves can be swapped out. It is inefficient to swap in entire page tables when a process wants to allocate memory. Hence, Windows NT maintains a separate binary search tree containing the information about current virtual space allocation for each process. A node in this binary search tree is called a Virtual Address Descriptor (VAD). For each block of memory allocated to a process, Windows NT adds a VAD entry to the binary search tree. Each VAD entry contains the allocated address range–that is, the start address and the end address of the allocated block, pointers to left and right children VADs, and a pointer to the parent VAD. The process environment block (PEB) contains a pointer, namely, VadRoot, to the root of this tree.&lt;br /&gt;&lt;br /&gt;Listing 4-5: VADDUMP.C&lt;br /&gt;&lt;br /&gt;/* Should be compiled in release mode */&lt;br /&gt;&lt;br /&gt;#define _X86_&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &lt;ntddk.h&gt;&lt;br /&gt;&lt;br /&gt;#include &lt;string.h&gt;&lt;br /&gt;&lt;br /&gt;#include &lt;stdio.h&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &quot;undocnt.h&quot;&lt;br /&gt;&lt;br /&gt;#include &quot;gate.h&quot;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/*Define the WIN32 calls we are using, since we can not include both&lt;br /&gt;&lt;br /&gt;NTDDK.H and&lt;br /&gt;&lt;br /&gt;WINDOWS.H in the same ’C’ file.*/&lt;br /&gt;&lt;br /&gt;typedef struct _OSVERSIONINFO{&lt;br /&gt;&lt;br /&gt; ULONG dwOSVersionInfoSize;&lt;br /&gt;&lt;br /&gt; ULONG dwMajorVersion;&lt;br /&gt;&lt;br /&gt; ULONG dwMinorVersion;&lt;br /&gt;&lt;br /&gt; ULONG dwBuildNumber;&lt;br /&gt;&lt;br /&gt; ULONG dwPlatformId;&lt;br /&gt;&lt;br /&gt; CCHAR szCSDVersion[ 128 ];&lt;br /&gt;&lt;br /&gt;} OSVERSIONINFO, *LPOSVERSIONINFO;&lt;br /&gt;&lt;br /&gt;BOOLEAN _stdcall GetVersionExA(LPOSVERSIONINFO);&lt;br /&gt;&lt;br /&gt;PVOID _stdcall VirtualAlloc(PVOID, ULONG, ULONG, ULONG);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* Max vad entries */&lt;br /&gt;&lt;br /&gt;#define MAX_VAD_ENTRIES   0x200&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* Following variables are accessed in RING0.ASM */&lt;br /&gt;&lt;br /&gt;ULONG NtVersion;&lt;br /&gt;&lt;br /&gt;ULONG PebOffset;&lt;br /&gt;&lt;br /&gt;ULONG VadRootOffset;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#pragma pack(1)&lt;br /&gt;&lt;br /&gt;typedef struct VadInfo {&lt;br /&gt;&lt;br /&gt; void *VadLocation;&lt;br /&gt;&lt;br /&gt; VAD Vad;&lt;br /&gt;&lt;br /&gt;} VADINFO, *PVADINFO;&lt;br /&gt;&lt;br /&gt;#pragma pack()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;VADINFO VadInfoArray[MAX_VAD_ENTRIES];&lt;br /&gt;&lt;br /&gt;int VadInfoArrayIndex;&lt;br /&gt;&lt;br /&gt;PVAD VadTreeRoot;&lt;br /&gt;&lt;br /&gt;The initial portion of the VADDUMP.C file has a few definitions apart from the header inclusion. In this program, we use the callgate mechanism as we did in the showdir program–hence the inclusion of the GATE.H header file. After the header inclusion, the file defines the maximum number of VAD entries that we’ll process. There is no limit on the nodes in a VAD tree. We use the callgate mechanism for kernel-mode execution of a function that dumps the VAD tree in an array accessible from the user mode. This array can hold up to MAX_VAD_ENTRIES entries. Each entry in the array is of type VADINFO. The VADINFO structure has two members: the address of the VAD tree node and the actual VAD tree node. The VAD tree node structure is defined in the UNDOCNT.H file as follows:&lt;br /&gt;&lt;br /&gt;typedef struct vad {&lt;br /&gt;&lt;br /&gt;void *StartingAddress;&lt;br /&gt;&lt;br /&gt;void *EndingAddress;&lt;br /&gt;&lt;br /&gt;struct vad *ParentLink;&lt;br /&gt;&lt;br /&gt;struct vad *LeftLink;&lt;br /&gt;&lt;br /&gt;struct vad *RightLink;&lt;br /&gt;&lt;br /&gt;DWORD Flags;&lt;br /&gt;&lt;br /&gt;}VAD, *PVAD;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://thegioitinhoccuatoi.blogspot.com/feeds/7893975140383098101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8742242208674094237/7893975140383098101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7893975140383098101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8742242208674094237/posts/default/7893975140383098101'/><link rel='alternate' type='text/html' href='http://thegioitinhoccuatoi.blogspot.com/2007/11/memory-management-part-3.html' title='Memory Management -- Part 3'/><author><name>CABA LAM</name><uri>http://www.blogger.com/profile/10808871344367751114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhurmY9hi2O7rKwi7OLXnTWlKKRZhSWkuonVehUk5YOI23QwIqXVr2jPXbJ4obwcu7SnarfIbakErRFcdjKRUMaq5zVFwG0u0yNme1ciwSCpJEv3h6zd9-dUgofzkJG3R37B1Ga4htLQgpj5YtjQUSlhxf4Vf3ShJzj8e_Iuef82XwCmCA/s220/OIG2.jpg'/></author><thr:total>0</thr:total></entry></feed>