tag:blogger.com,1999:blog-9063338948812576302024-03-13T14:16:20.694+13:00Technically WorksLife hacks & ideas that worked for meOscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.comBlogger30125tag:blogger.com,1999:blog-906333894881257630.post-76799179777086515242020-01-12T13:24:00.000+13:002020-01-12T13:30:10.704+13:00Coromandel Trip - Xmas 2019<p>Few weeks ago, my son watched a Youtube video about hot water beach and was really excited when I told him it is in New Zealand so we decided to take an impromptu trip there before Xmas.</p>
<p>Hot water beach is actually only around 2.5 hours drive from Auckland but the wife didn't want to sit in the car for 5 hours in the same day. Fortunately we managed to find a nice holiday apartment in Whitianga despite busy holiday season, which is a nice small beach-side town only 30 minutes away from the hot water beach.</p>
<figure>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBdBAshM4W97ad_Qjnr47bDM3pbsEZ2LTt0-mIatVIuV81G23LLRzkBYmE6VQsgfK-RvNja86LSPkA23npsPpH5_M0-deAdH5KNV5-HfuD-fJxySmwGzr7GuiiMWkNtA61IQTxZ6Fr-mk/s1600/B12B4307-354D-4947-BA47-64BB056DE0AB.jpeg" imageanchor="1"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBdBAshM4W97ad_Qjnr47bDM3pbsEZ2LTt0-mIatVIuV81G23LLRzkBYmE6VQsgfK-RvNja86LSPkA23npsPpH5_M0-deAdH5KNV5-HfuD-fJxySmwGzr7GuiiMWkNtA61IQTxZ6Fr-mk/s320/B12B4307-354D-4947-BA47-64BB056DE0AB.jpeg" width="640" /></a>
<figcaption>Kids @ Whitianga beach</figcaption>
</figure>
<h1>Day 1</h1>
<h2>Hot water beach</h2>
<p>The best time to dig your own hot spa at the hot water beach is one hour on either side of the low tide, which was around 10:15am on the day. We decided to leave early and drove there first thing in the morning. We managed to get there by 10:45am but the beach was already packed with visitors.</p>
<p>We tried to find an empty spot and started digging with our big garden spade (most people were using mini toy-like ones) but only cold water came out.</p>
<p>Apparently only the small area in front of the big rock towards the centre of the beach has hot water underground. Luckily a family next to us was leaving and they kindly asked us if we'd like to take over the pool.</p>
<figure>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimXLxQHD5R7hax64uARqz2SfFoIKe6t_kPC77UNAb8RCmshe5tYmw3gS0xpXBR4DvH9MB-8wDVbGPuToF7fRk7EtVV4QcllUdkMHlDH29ea-LpaFXAGN8c55IGwEWIkjoiDK9iY0DwEAY/s1600/DSC03416.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimXLxQHD5R7hax64uARqz2SfFoIKe6t_kPC77UNAb8RCmshe5tYmw3gS0xpXBR4DvH9MB-8wDVbGPuToF7fRk7EtVV4QcllUdkMHlDH29ea-LpaFXAGN8c55IGwEWIkjoiDK9iY0DwEAY/s320/DSC03416.jpg" width="640" height="426" data-original-width="1600" data-original-height="1067" /></a>
<figcaption>Kids and me with our big garden spade</figcaption>
</figure>
<p>The beach itself is also a nice soft white sand beach with decent wave for surfing and bodyboarding but we did't bring our gears with us.</p>
<figure>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4on-YDJI4iY_-oieflS76rUpjfMIGE2HDlNfN_CjI-bHiN-ippWRcCOvB3B-YbB5rT9H9P-zFVk_vc-Ck-8bvV29Xl-gdpEQ4DOL0QUxbj0DJKnJ13byXY01xYuLLcLXkaZb8SXNro4Y/s1600/DSC03486.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4on-YDJI4iY_-oieflS76rUpjfMIGE2HDlNfN_CjI-bHiN-ippWRcCOvB3B-YbB5rT9H9P-zFVk_vc-Ck-8bvV29Xl-gdpEQ4DOL0QUxbj0DJKnJ13byXY01xYuLLcLXkaZb8SXNro4Y/s400/DSC03486.jpg" width="400" height="267" data-original-width="1600" data-original-height="1067" /></a>
<figcaption>The hot water beach</figcaption>
</figure>
<p>We left the beach after an hour or so, had some overpriced fish & chips and headed for Whitianga.</p>
<h2>Whitianga</h2>
<p>This small town has changed a lot since last time we visited nearly 10 years ago. The waterfront and town centre have clearly been done up recently and it feels like a smaller version of the popular Queenstown.</p>
<figure>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii5YPw6clESPk1AAyZspA_hs0iKdcxxPDlOIu7wZt292vVukqw1TJBG7T3Nw1mqUW4JshROwb4adPLGtiRUTr_WR1RiVJCU8rfkNxJGTq54uak6R_XbIpZqW-4nMO7LPcfRq7HSQZpdBA/s1600/DSC03569.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii5YPw6clESPk1AAyZspA_hs0iKdcxxPDlOIu7wZt292vVukqw1TJBG7T3Nw1mqUW4JshROwb4adPLGtiRUTr_WR1RiVJCU8rfkNxJGTq54uak6R_XbIpZqW-4nMO7LPcfRq7HSQZpdBA/s320/DSC03569.jpg" width="400" height="267" data-original-width="1600" data-original-height="1067" /></a>
<figcaption>Whitianga waterfront</figcaption>
</figure>
<p>The glass-bottom boat tour was nice and the crew members did a great job telling history and local stories. However, we were all sea-sick due to the bumpy water after the first 30 minutes and didn't take much picture.</p>
<h1>Day 2</h1>
<p>I didn't sleep particular well so I got up early and went for a morning jog along the water front walkway. I was only planning to run around 10k but it was such a nice morning so I ended up running further.</p>
<figure>
<iframe height='405' width='590' frameborder='0' allowtransparency='true' scrolling='no' src='https://www.strava.com/activities/2949954916/embed/ebf6b732f4f161d40c600529d718520288477351'></iframe>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB2ptLnai5j94DRyeXYaIugkSyyTxLHW-B94R8LOlfDvyPUR1lfjLiTeVVqWn_DlHO6IipsUhutwxah_y6R-KcZyaXJlj7zhuMED0xJUqXAn1BUfuTQb6ni-tkgfUn7YOARz6d7fQBBow/s1600/IMG_0830.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB2ptLnai5j94DRyeXYaIugkSyyTxLHW-B94R8LOlfDvyPUR1lfjLiTeVVqWn_DlHO6IipsUhutwxah_y6R-KcZyaXJlj7zhuMED0xJUqXAn1BUfuTQb6ni-tkgfUn7YOARz6d7fQBBow/s400/IMG_0830.jpg" width="400" height="300" data-original-width="1600" data-original-height="1200" /></a>
<figcaption>Whitianga morning run</figcaption>
</figure>
<p>After my run, we packed up and drove to Coromandel town.</p>
<h2>Coromandel town</h2>
<p>Driving from Whitianga to Coromandel town only took an hour but it was an hour of winding mountain roads so it felt much longer. When we finally arrived, the Coromandel town felt .... exactly the same as 10 years ago. I even recognised the same restaurant we had dinner last time because it had not changed at all.<p>
<p>The holiday park we stayed in for the night was quite nice, it's got a swimming pool and kids play area, everyone especially loved the jumping pillow.</p>
<figure>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguUgyM90lZ9kZxBYZ99HrL4MlezpWyjivYdhbhbk8EaDE_wGYcsEsNCzrdG-Ihy8c7BIJjEQjrd4Crq3E_3cWteEZq0ezBdD02LoLOXZxtKOA9iGmVqWHxcdbb8GqKHrm1Ju5rLj_Sj6I/s1600/DSC03605.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguUgyM90lZ9kZxBYZ99HrL4MlezpWyjivYdhbhbk8EaDE_wGYcsEsNCzrdG-Ihy8c7BIJjEQjrd4Crq3E_3cWteEZq0ezBdD02LoLOXZxtKOA9iGmVqWHxcdbb8GqKHrm1Ju5rLj_Sj6I/s400/DSC03605.jpg" width="400" height="267" data-original-width="1600" data-original-height="1067" /></a>
<figcaption>The jump pillow</figcaption>
</figure>
<p>We went for the <a href="https://dcrail.nz">Driving Creed Railways</a> train ride in the late afternoon and had dinner at the Pepper Tree restaurant.</p>
<h1>Day 3</h1>
<p>I went out for another run the next morning to explore Coromandel town, it actually felt a lot like the Titirangi and Waitakere area in Auckland with its rocky beaches, trees and hills.</p>
<figure>
<iframe height='405' width='590' frameborder='0' allowtransparency='true' scrolling='no' src='https://www.strava.com/activities/2951842897/embed/7b2617317bb5d1af71820d63d37ff2b1b5f929c5'></iframe>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJuH0ov1dhA5M-fTlDVg83YOhqXlbf5uI7YcAys7JYrbu64eYMbOxWMhrTMFI5AB7dHFDiKITTItpLMS2FkGVqmHX5yeKgALlfwaOqht1ilks9PXek2ts0Hf654irj6cX2VeuSWytv7BY/s1600/IMG_0863.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJuH0ov1dhA5M-fTlDVg83YOhqXlbf5uI7YcAys7JYrbu64eYMbOxWMhrTMFI5AB7dHFDiKITTItpLMS2FkGVqmHX5yeKgALlfwaOqht1ilks9PXek2ts0Hf654irj6cX2VeuSWytv7BY/s400/IMG_0863.jpg" width="400" height="300" data-original-width="1600" data-original-height="1200" /></a>
<figcaption>Coromandel Morning Run</figcaption>
</figure>
<p>We packed up and headed home afterward and stopped by a small beach near Thames to stretch our legs.</p>
<figure>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTVujxbktwTSaJgP6N8X7ocqGP7tUYrd3kT49Ix2qw1JU8GLnn_684hOgvzyHvwaNHugkh2ZX3BL1elB8dZtaXG29vgxm1u9YDc1dQP1w5oR-9KgQxd4ST3DHe2bAIF8fOrUGogzgkMM4/s1600/DSC03624.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTVujxbktwTSaJgP6N8X7ocqGP7tUYrd3kT49Ix2qw1JU8GLnn_684hOgvzyHvwaNHugkh2ZX3BL1elB8dZtaXG29vgxm1u9YDc1dQP1w5oR-9KgQxd4ST3DHe2bAIF8fOrUGogzgkMM4/s400/DSC03624.jpg" width="400" height="267" data-original-width="1600" data-original-height="1067" /></a>
<figcaption>Kids throwing rocks at the random small beach</figcaption>
</figure>
<p>All in all, our favourite spot of this entire trip is Whitianga and are thinking of visiting it again next Xmas so we can spend more time on the beach.</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-19895693106958503172020-01-05T15:48:00.001+13:002020-01-06T01:54:05.132+13:00It’s been a while ...<p>It’s been nearly 7 years since my last blog post, during which time UK has decided to withdraw from EU, iPhone is now over $2000 NZD and Donald Trump was elected as US president and then impeached.</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqCMoJfOikn5GFqB3K1rjxASfybPgvhB07ZrNDgSiiKo_mmEad2zsl0ngnhtkPA5bU4I1A-EqhovxfMUJWSsKiWfiGvJb-Q8kVVtIiOJewe9rr5Luc55_F_utQfhMMhQriU-WP2seHZ_A/s1600/festa-numeros-decoracio-cap-d-any-3401900.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqCMoJfOikn5GFqB3K1rjxASfybPgvhB07ZrNDgSiiKo_mmEad2zsl0ngnhtkPA5bU4I1A-EqhovxfMUJWSsKiWfiGvJb-Q8kVVtIiOJewe9rr5Luc55_F_utQfhMMhQriU-WP2seHZ_A/s320/festa-numeros-decoracio-cap-d-any-3401900.jpg" width="320" height="214" data-original-width="640" data-original-height="427" /></a>
<p>While my personal journey of the last 7 years was nowhere near as dramatic, it was still quite a lot for me personally.</p>
<ul>
<li>I changed roles and companies few more times.</li>
<li>Moved house twice.</li>
<li>Run regularly again, more serious this time.</li>
<li>Starting to get my time back as kids grow up.</li>
</ul>
<p>As I'm approaching "officially not young anymore" age, I've learnt to recognise and focus more energy on things that are truely important to me such as family and personal growth.</p>
<p>Billy Yang's video below resonates with me even though I am happily married, have 2 lovely kids, living in our own home instead of a van and love my 9-5 job as a software developer 😅.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/_EFk7nWoKw0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-35302135214250523782013-02-08T13:24:00.001+13:002017-09-09T21:33:22.018+12:00Concatenate lines in a file into a single line with PowerShell<p>Assuming you have a input file <i>in.txt</i> with following lines:</p>
<pre><code>a
b
c</code></pre>
<p>and you'd like to concatenate these 3 lines into a single line to a file <i>out.txt</i> i.e.</p>
<pre><code class="bash">abc</code></pre>
<p>You can use the following PowerShell command:</p>
<pre><code class="powershell">-join (cat in.txt) | out-file out.txt</code></pre>
<p>Basically, the <code>cat <i>in.txt</i></code> is executed first and returned the content of <i>in.txt</i> as an array of strings. The <code>-join</code> cmdlet then joins the lines together into a single line, which is then piped (<code>|</code>) into the <code>out-file</code> cmdlet to produce the <i>out.txt</i> file.Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-47273261110963582442012-04-22T15:40:00.000+12:002017-09-09T21:20:02.039+12:00Replace xscreensaver in CrunchBang Linux 10 "Startler"<p>Deep down in my heart I am a minimalist and to me CrunchBang Linux has the right balance between minimalism and usefulness. However, one thing I've always struggled with it is the choice of xscreensaver for the simple lock screen task because it also does monitor power management, which has to be turned off to avoid conflict with xfce4 power manager.</p>
<p>Last week I finally decided to bite the bullet and replaced it with much simpler slock and here's how I did it.</p>
<h3>Step 1 - Remove xscreensaver and install the screen locker of your choice</h3>
<p>To remove xscreensaver simply:</p>
<pre><code class="bash">sudo apt-get purge xscreensaver</code></pre>
<p>Next, you'll need to install xautolock to lock screen automatically after certain idle time (mine's set to 1 minute):</p>
<pre><code class="bash">sudo apt-get install xautolock</code></pre>
<p>Now you have to choose a screen locker. I use <code>slock</code>, which is probably the simplest screen lock ever because all you get is a blank lock screen. There's also <code>xtrlock</code>, which is installed by <code>apt</code> automatically when I installed xautolock and i3lock, which allows you to choose a background image for the lock screen.</p>
<p>Once you've decided which one you wanna use, here's the hackery part of the job. You need to:</p>
<pre><code class="bash">cd /usr/bin
sudo ln -s slock xflock4</code></pre>
<p>Right now you might be thinking, "gee this is a hack, there must be a better way". The truth is there really isn't because the XFce4 Power Manager is hard-coded to try a list of screen lockers in the source code:</p>
<pre><code class="c">xfpm_lock_screen (void)
{
gboolean ret = g_spawn_command_line_async ("xflock4", NULL);
if ( !ret )
{
g_spawn_command_line_async ("gnome-screensaver-command -l", NULL);
}
if ( !ret )
{
/* this should be the default*/
ret = g_spawn_command_line_async ("xdg-screensaver lock", NULL);
}
if ( !ret )
{
ret = g_spawn_command_line_async ("xscreensaver-command -lock", NULL);
}
if ( !ret )
{
g_critical ("Connot lock screen\n");
}
}</code></pre>
<h3>Step 2 - Auto start xautolock</h3>
<p>You need to add the following line to <code>~/.config/openbox/autostart</code> to auto start the <code>xautolock</code> with right idle interval in minutes and screen locker name:</p>
<pre><code class="bash">xautolock -time 1 -locker "slock" &</code></pre>
<p>Remember to replace <code>slock</code> with the screen locker of your choice.</p>
<h3>Step 3 - Update lock screen keyboard shortcut</h3>
<p>Finally, find the following section in the <code>~/.config/openbox/rc.xml</code> and enter the screen locker name in the <code><Command></Command></code> section.</p>
<pre><code class="xml"><keybind key="W-l">
<action name="Execute">
<startupnotify>
<enabled>true</enabled>
<name>Lock screen</name>
</startupnotify>
<command>cb-lock</command>
</action>
</keybind></code></pre>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com4tag:blogger.com,1999:blog-906333894881257630.post-84296113989830334442011-02-16T10:08:00.009+13:002011-02-16T10:27:31.549+13:00Enable PowerShell Remoting<p>On the remote machine, start a Administrator PowerShell session and run following commands:</p>
<pre class="brush:powershell">Enable-PSRemoting
cd wsman:
cd localhost\client
set-item trustedhosts *</pre>
<p>Just answer "Y" for any prompt.</p>
<p>To start a remote Powershell session, on your local PowerShell session, enter:</p>
<pre class="brush:powershell">Enter-PSSession <the-remote-machine-hostname></pre>
<p>For some reason, <code>Enter-PSSession</code> can't seems to resolve DNS aliases (CNAME records) and you'll get the following error if you used it instead of the hostname.</p>
<blockquote style="color: red;">Enter-PSSession : Connecting to remote server failed with the following error message : WinRM cannot process the request. The following error occured while using Kerberos authentication: The network path was not found.</blockquote>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-11324029195803048232011-02-12T08:43:00.012+13:002011-02-12T11:11:05.272+13:00Running CrunchBang Linux 10 "Statler" on HP mini 1000<p>I was a big Ubuntu fan until they decided to use Unity in netbook remix and moved windows border icons to the left. I then switched to LinuxMint, it was alright but I'm a Chrome user and I hate the fact that they won't let you remove firefox (the package manager forces you to install "abrowser", which is still firefox but with brand removed).</p>
<p>Now CrunchBang Linux is my new favorite Linux distro. I hesitated before puting it on my HP mini 1000 netbook because the latest version "Statler" is based on debian, which is famous for being "Pure Open Source" and hence lack of proprietary drivers. However, I was totally wrong, nearly all hardware worked out of the box for me except for the usual wireless driver, network port and bluetooth.</p>
<h2>Wireless</h2>
<p>To be fair the b43 driver did work but it was fairly unstable and disconnects randomly. The solution turns out is pretty simple, all you have to do is to enter:</p>
<pre class="brush:plain">options b43 pio=1 qos=0</pre>
<p>in the following file:</p>
<pre class="brush:plain">/etc/modprobe.d/b43.conf</pre>
<p>and reboot.</p>
<h2>Bluetooth</h2>
<p>My cheap bluetooth dongle was detected but there were no software installed to interact with it. Therefore, I installed blueman by:</p>
<pre class="brush:plain">sudo apt-get install blueman</pre>
<p>and add <code>blueman-applet</code> to the end of <code>autostart.sh</code>:</p>
<pre class="brush:plain">
## Launch blueman applet
(sleep 4s && blueman-applet) &
</pre>
<h2>Ethernet</h2>
<p>Finally, the ethernet port has got the same problem as Ubuntu where the cable needed to be plugged before the system was booted up and it will freeze the system if you unplug it. To get network working properly you need to open up <code>/etc/default/grub</code>, find the line</p>
<pre class="brush:plain">GRUB_CMDLINE_LINUX_DEFAULT="quiet"</pre>
<p>add option <code>acpi_os_name=Linux</code></p>
<pre class="brush:plain">GRUB_CMDLINE_LINUX_DEFAULT="quiet acpi_os_name=Linux"</pre>
<p>save the file and then run</p>
<pre class="brush:plain">sudo update-grub</pre>
<p>reboot your laptop and the ethernet port should work properly now.</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com4tag:blogger.com,1999:blog-906333894881257630.post-35855164472799730222010-10-09T19:31:00.001+13:002010-10-10T17:51:36.032+13:00AOP with Enterprise Library Policy Injection Block<p>I used to think aspect oriented programming (AOP) is not a very useful idea because quite often it only saves few keystrokes but requires a massive configuration file. I only came to realise the true value of AOP recently when we have to convert 4+ million lines of legacy COBOL code to .NET with Microfocus’ Visual COBOL compiler. Manually adding exception handling and logging code to these existing COBOL subroutines will not only take a lot of time but also makes code merging task much harder later. </p> <p>I started experimenting with the policy injection block in the Enterprise Library to see if it will make this job simpler. I tried to google for AOP and Enterprise Library tutorials but I found most tutorials on the web are either old (most were done using Enterprise Library 3) or over-complicated. Especially many of them use the Enterprise Library Logging Block to demonstrate the policy injection concept, which requires lots of configurations itself.</p> <p>The policy injection in Enterprise Library is actually pretty easy to use and therefore in this tutorial I decided to demonstrate it with a simple custom call handler that requires no configuration. This is done so that you won’t be distracted by those extra settings in the App.config. Also, I’ll be using the Enterprise Library 5 configuration GUI editor, which looks quite different from the old one.</p> <p>So let’s start with a basic console program with a simple interface IGreeter, which has a single operation <code>SayHello</code>:</p> <p></p> <pre class="brush: csharp">public interface IGreeter
{
void SayHello(string to);
}</pre>
<p></p>
<p>and a very basic implementation, which prints a hello message to whatever name that was passed in:</p>
<p></p>
<pre class="brush: csharp">public class Greeter : IGreeter
{
public void SayHello(string to)
{
Console.WriteLine("hello " + to);
}
}</pre>
<p></p>
<p>Next, the Main method simply creates a greeter instance and then says hello to “bob”.</p>
<p></p>
<pre class="brush: csharp">class Program
{
static void Main(string[] args)
{
var greeter = new Greeter();
greeter.SayHello("bob");
}
}</pre>
<p></p>
<p>Finally, below is a simple console logger call handler. It sits in a separate DLL (CustomCallHandlers.dll) and will be applied to the SayHello method above using the policy injection block.</p>
<p></p>
<pre class="brush: csharp">[ConfigurationElementType(typeof(CustomCallHandlerData))]
public class ConsoleCallLogger : ICallHandler
{
public ConsoleCallLogger(NameValueCollection collection)
{
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("Before " + input.MethodBase.Name);
var returnValue = getNext()(input, getNext);
Console.WriteLine("After " + input.MethodBase.Name);
return returnValue;
}
public int Order { get; set; }
}</pre>
<p></p>
<p>The implementation of the Invoke method should be pretty self-explanatory, it basically prints a before and after message around the method invocation. Check <a href="http://msdn.microsoft.com/en-us/library/ff650019.aspx">this link</a> out if you would like to know more about how to implement the ICallHandler interface.</p>
<p>A couple of things to note about the ConsoleCallLogger class above. Firstly It needs to implement the ICallHandler interface and have the ConfigurationElementType class attribute. Following assembly references needs to be added to the project in order for Visual Studio to resolve these classes:</p>
<ul>
<li>Microsoft.Practices.EnterpriseLibrary.Common </li>
<li>Microsoft.Practices.Unity.Interception </li>
<li>System.configuration </li>
</ul>
<p>Secondly, a custom call handler class is required to have a constructor that accepts a NameValueCollection argument. As you’ll see when we start configuring the App.config file, you can pass name/value pair values to the custom call logger from the configuration file.</p>
<p>Now, to apply the ConsoleCallLogger to the SayHello method, we need to configure the policy injection block with app.config. Before we start let’s have a look what the file will look like at the end (note: I have added space in class names so it will wrap properly):</p>
<p></p>
<pre class="brush: xml"><configuration>
<configSections>
<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration. PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
</configSections>
<policyInjection>
<policies>
<add name="My Custom Logging">
<matchingRules>
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules. MemberNameMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Member Name Matching Rule">
<matches>
<add match="SayHello" />
</matches>
</add>
</matchingRules>
<handlers>
<add type="CustomCallHandlers.ConsoleCallLogger, CustomCallHandlers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="ConsoleCallLogger" />
</handlers>
</add>
</policies>
</policyInjection>
</configuration></pre>
<p></p>
<p>Other than the ugly long class names, it's not too bad is it? You can pretty much just code this up by hand. However, since the enterprise library comes with a configuration editor, let's make use of it.</p>
<p>First of all we need to add a policy injection settings block in our file:</p>
<p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/TLAUTINKhuI/AAAAAAAABTs/m6m2BxXGTVo/s1600-h/EntlibConfig1%5B5%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="EntlibConfig1" border="0" alt="EntlibConfig1" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/TLAUT_r842I/AAAAAAAABTw/XFYsu1xMQ0E/EntlibConfig1_thumb%5B3%5D.jpg?imgmax=800" width="644" height="416" /></a> </p>
<p></p>
<p>Next, right click on the policy injection block heading (“My Custom Logging” in my example) and add a member name matching rule block.</p>
<p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/TLAkMJVlTGI/AAAAAAAABT8/b8_JfP71H04/s1600-h/EntlibConfig2%5B4%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="EntlibConfig2" border="0" alt="EntlibConfig2" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/TLAkNFzIZcI/AAAAAAAABUA/vUFXPXIa4tU/EntlibConfig2_thumb%5B2%5D.jpg?imgmax=800" width="645" height="473" /></a> </p>
<p>Enter our our target method name SayHello into the member name match field.</p>
<p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/TLAkNq4sgUI/AAAAAAAABUE/eoJOx9Pz6Yk/s1600-h/EntlibConfig3%5B5%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="EntlibConfig3" border="0" alt="EntlibConfig3" src="http://lh3.ggpht.com/_3k_ZpkI8ITs/TLAkOXiZPQI/AAAAAAAABUI/TbvCaWG8Q30/EntlibConfig3_thumb%5B3%5D.jpg?imgmax=800" width="462" height="152" /></a> </p>
<p>After telling the policy injection block how to find the target method (i.e. our SayHello method), we need to configure the call handler (i.e. the ConsoleCallLogger class). Again, right clean on our policy injection block header and choose “Add Custom Call Handler”.</p>
<p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/TLAkO9POfmI/AAAAAAAABUM/uEyuYyjU65E/s1600-h/EntlibConfig4%5B8%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="EntlibConfig4" border="0" alt="EntlibConfig4" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/TLAkP_QonbI/AAAAAAAABUQ/0SiQ8dWaUN0/EntlibConfig4_thumb%5B6%5D.jpg?imgmax=800" width="640" height="344" /></a> </p>
<p></p>
<p></p>
<p>This will popup a dialog box and if you choose “Add From File”, you can then select the location of the DLL that contains the ConsoleCallLogger class. After selecting the DLL, the ConsoleCallLogger class should show up on the tree control as shown below:</p>
<p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/TLAkQce5B3I/AAAAAAAABUk/3PS8DIwxkr8/s1600-h/EntlibConfig5%5B1%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="EntlibConfig5" border="0" alt="EntlibConfig5" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/TLAkRNHZqzI/AAAAAAAABUo/lNfUP8h7Q-Q/EntlibConfig5_thumb.jpg?imgmax=800" width="539" height="370" /></a> </p>
<p>I did run into few problems when I was adding my DLL. Here are some tips if you run into problems too:</p>
<ol>
<li>Check your build settings, if you are using 64-bits EntLib configuration editor you should build 64-bits DLL too. </li>
<li>Check your class, is it public? did you implement the ICallHandler interface? did you remember to add the ConfigurationElementType attribute? and did you pass typeof(CustomCallHandlerData) to it? </li>
<li>Save and restart your configuration editor. I think there might be some bugs in the configuration editor because the first time I did this I just couldn’t get the ConsoleCallLogger class to show in the tree control no matter what I did. I end up saving and restarting a couple of times and eventually it came up. </li>
</ol>
<p>After the call handler has been added, your setting should look something similar to this:</p>
<p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/TLAkRlB0TcI/AAAAAAAABUc/xRWyYA2Iitc/s1600-h/EntlibConfig6%5B5%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="EntlibConfig6" border="0" alt="EntlibConfig6" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/TLAkScVcqFI/AAAAAAAABUg/Ex87zmCF2XU/EntlibConfig6_thumb%5B3%5D.jpg?imgmax=800" width="627" height="162" /></a> </p>
<p>Now, save the app.config to the console program and add it to the project. To enable policy injection block in the console program, we first need to add following assemblies to the project:</p>
<ul>
<li>Microsoft.Practices.EnterpriseLibrary.PolicyInjection </li>
<li>Microsoft.Practices.Unity.Interception </li>
<li>CustomCallHandlers (the DLL that contains the ConsoleCallLogger class). </li>
</ul>
<p>Next we need to replace the direct object instantiation:</p>
<p></p>
<pre class="brush: csharp">var greeter = new Greeter();</pre>
<p></p>
<p>with PolicyInjection.Create in the Main method:</p>
<p></p>
<pre class="brush: csharp">var greeter = PolicyInjection.Create<Greeter, IGreeter>();</pre>
<p></p>
<p>If everything’s been configured properly, when you run the console program it’ll show the following output:</p>
<p></p>
<pre class="brush: plain">Before SayHello
hello bob
After SayHello</pre>
<p></p>
<p>As you can see, the ConsoleCallLogger class has been magically applied to the SayHello method. </p>
<p>AOP is a very useful technique if you have to extend some legacy code without actually having to touch those code. Enterprise library already shipped with a few useful call handlers for common cross cutting concerns such as the logging and security. If none of them fits your needs you can always follow what I did in this tutorial and write your own call handlers.</p>
<p>Finally, you can download codes in this tutorial with the SkyDrive link below:</p>
<iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-a48f17332e3ed214.office.live.com/embedicon.aspx/Public/AOP%20Examples.zip" frameborder="0" marginwidth="0" scrolling="no"></iframe> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com4tag:blogger.com,1999:blog-906333894881257630.post-44271442860055755252010-09-30T21:40:00.001+13:002010-09-30T21:53:42.179+13:00COBOL Tutorial 00300 – Edited Fields<p>As I have mentioned in the <a href="http://blog.technicallyworks.com/2010/09/cobol-tutorial-000200-basic-data-fields.html">last tutorial</a>, you use edited fields in COBOL to format data fields into human-readable display strings. Let’s start with a numeric field:</p> <p></p> <pre class="brush: plain">01 NUMERIC-FIELD PIC 999999V99.</pre>
<p></p>
<p>and some COBOL code that set and display the field value:</p>
<p></p>
<pre class="brush: plain">MOVE 1234.5 TO NUMERIC-FIELD.
DISPLAY NUMERIC-FIELD: ' NUMERIC-FIELD.</pre>
<p></p>
<p>As we’ve demonstrated in the previous tutorial, unused digits are padded with ugly zeros:</p>
<p></p>
<pre class="brush: plain">NUMERIC-FIELD: 001234.50</pre>
<p></p>
<p>Let me put my C# programmer hat on again (apologies to Java, ruby, python, C/C++, assembly and many other programmers who don’t like C#), when we have to format a variable for display, we often use the string.Format method with a formatting string containing special formatting characters, which is “0,0.00” in the following example:</p>
<p></p>
<pre class="brush: csharp">// returns 1,234.50
string.Format("{0:0,0.00}", 1234.5)</pre>
<p></p>
<p>Now let’s come back to COBOL, an edited field is basically a normal COBOL data field with a formatting string in the picture clause instead of the “A”, “X” or “9” data type specifiers. The edit field's formating string is based on similar ideas as C#’s one. To achieve the same output as the C# code above, I use “ZZZ,ZZZ.99” formating string as shown in the following example:</p>
<p></p>
<pre class="brush: plain">01 EDITED-NUMERIC-FIELD PIC ZZZ,ZZZ.99.</pre>
<p></p>
<p>Unlike the place holding character “9”, each unused “Z” in the picture clause is not filled with “0” and the “,” just inserts a comma in the display value. Therefore, if we move the value of NUMERIC-FIELD to the EDITED-NUMERIC-FIELD and then display the content of EDIT-NUMERIC-FIELD:</p>
<p></p>
<pre class="brush: plain">MOVE NUMERIC-FIELD TO EDITED-NUMERIC-FIELD.
DISPLAY 'EDITED-NUMERIC-FIELD: ' EDITED-NUMERIC-FIELD.</pre>
<p></p>
<p>The result is a much more readable output:</p>
<p></p>
<pre class="brush: plain">EDITED-NUMERIC-FIELD: 1,234.50</pre>
<p></p>
<p>Keep in mind that an edited field is basically an alpha-numeric field, so you cannot perform arithmetic calculation with it. For example if you add following line of code to your program:</p>
<p></p>
<pre class="brush: plain">ADD 1 TO EDITED-NUMERIC-FIELD.</pre>
<p></p>
<p>You will get the following compile error:</p>
<p></p>
<pre class="brush: plain">Error: 'EDITED-NUMERIC-FIELD' is not numeric name</pre>
<p></p>
<p>This is a summary of commonly used formatting special characters:</p>
<ul>
<li>“B” – Inserts a blank space.</li>
<li>“Z” – Place holder for a numeric character or space if unused.</li>
<li>“,” – inserts a comma.</li>
<li>“/” – Inserts a slash.</li>
<li>“0” – Inserts a zero.</li>
</ul>
<p>Here are some examples to demonstrate how to use them:</p>
<p></p>
<table border="1" cellspacing="0" cellpadding="2" width="600">
<thead>
<tr>
<td style="text-align: center; width: 200px"><b>Picture Clause</b></td>
<td style="text-align: center; width: 200px"><b>Input</b></td>
<td style="text-align: center; width: 200px"><b>Output</b></td>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center; width: 200px">9999/99/99</td>
<td style="text-align: center; width: 200px">20100101</td>
<td style="text-align: center; width: 200px">2010/01/01</td>
</tr>
<tr>
<td style="text-align: center; width: 200px">9999B99B99</td>
<td style="text-align: center; width: 200px">20100101</td>
<td style="text-align: center; width: 200px">2010 01 01</td>
</tr>
<tr>
<td style="text-align: center; width: 200px">-ZZZ,ZZZ.99</td>
<td style="text-align: center; width: 200px">-1234.5</td>
<td style="text-align: center; width: 200px">-1234.50</td>
</tr>
<tr>
<td style="text-align: center; width: 200px">ZZZ,ZZZ.99-</td>
<td style="text-align: center; width: 200px">-1234.5</td>
<td style="text-align: center; width: 200px">1234.50-</td>
</tr>
<tr>
<td style="text-align: center; width: 200px">X0X0X0X</td>
<td style="text-align: center; width: 200px">ABCD</td>
<td style="text-align: center; width: 200px">A0B0C0D</td>
</tr>
</tbody>
</table>
<p></p>
<p>Finally, here's the COBOL program I used to develop the example code in this tutorial.</p>
<p></p>
<pre class="brush: plain"> IDENTIFICATION DIVISION.
PROGRAM-ID. EDITED-FIELD.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 NUMERIC-FIELD PIC 999999V99.
01 EDITED-NUMERIC-FIELD PIC ZZZ,ZZZ.99.
01 EDITED-NEGATIVE-FIELD1 PIC -ZZZ,ZZZ.99.
01 EDITED-NEGATIVE-FIELD2 PIC ZZZ,ZZZ.99-.
01 EDITED-DATE-FIELD1 PIC 9999/99/99.
01 EDITED-DATE-FIELD2 PIC 9999B99B99.
01 EDITED-ZERO-FIELD PIC X0X0X0X.
PROCEDURE DIVISION.
MOVE 1234.5 TO NUMERIC-FIELD.
DISPLAY ' NUMERIC-FIELD: ' NUMERIC-FIELD.
MOVE NUMERIC-FIELD TO EDITED-NUMERIC-FIELD.
DISPLAY 'EDITED-NUMERIC-FIELD: ' EDITED-NUMERIC-FIELD.
MOVE -1234.5 TO EDITED-NEGATIVE-FIELD1.
DISPLAY 'EDITED-NEGATIVE-FIELD1: ' EDITED-NEGATIVE-FIELD1.
MOVE -1234.5 TO EDITED-NEGATIVE-FIELD2.
DISPLAY 'EDITED-NEGATIVE-FIELD2: ' EDITED-NEGATIVE-FIELD2.
MOVE 20100101 TO EDITED-DATE-FIELD1.
DISPLAY 'EDITED-DATE-FIELD1: ' EDITED-DATE-FIELD1.
MOVE 20100101 TO EDITED-DATE-FIELD2.
DISPLAY 'EDITED-DATE-FIELD2: ' EDITED-DATE-FIELD2.
MOVE 'ABCD' TO EDITED-ZERO-FIELD.
DISPLAY 'EDITED-ZERO-FIELD: ' EDITED-ZERO-FIELD.
STOP RUN.</pre> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-33232708176857469552010-09-26T12:32:00.001+13:002010-09-30T18:30:39.638+13:00COBOL Tutorial 000200 – Data Fields<p>Variables are called <strong>Fields</strong> in COBOL and definitions of variables are declared in the <code>Picture</code> clause (can be abbreviated with <code>PIC</code>). Why is it called the Picture Clause? According to the book Sams Teach Yourself COBOL in 24 Hours, this is because it “paints a picture of how a field looks by defining every details and characteristic of the field”, still doesn’t quite make sense to me but anyway.</p> <p>Let’s start by talking about what data fields (variables) look like in C#. When we declare a variable, the first thing we have to think about is the data type, which determines what kind of data can it hold. Normally we wouldn’t worry about the number of digits or length of the string unless we know their values can get ridiculously large or long.</p> <p></p> <pre class="brush: csharp">int integerVariable = 12345678;
string stringVariable = "abcd1234";
decimal decimalVariable = 1234.5678m;</pre>
<p></p>
<p>In a COBOL world, however, the size does matter and you have to specify both the type and the size for each data field at the same time with a special character mask in its “Picture Clause” (the PIC keyword) as shown in code below:</p>
<p></p>
<pre class="brush: plain">01 ALPHA-FIELD PIC AAAAAAAAAA.
01 NUMERIC-FIELD PIC 9999999999.
01 ALPHA-NUMERIC-FIELD PIC XXXXXXXXXX.</pre>
<p></p>
<p>There are really only 3 types of data field in COBOL: literal, numeric and alpha-numeric and as their names suggest, they hold alphabetical, numeric and alpha-numeric characters respectively.</p>
<p>Let’s look at the ALPHA-FIELD first, its picture clause specifies a “AAAAAAAAAA” mask, each “A” character is a place holder for a single alphabetical character, so the ALPHA-FIELD can be used to store 10 alphabetical characters. Similarly, each “9” is a place holder for a single digit and a “X” is a place holder for a single alpha-numeric character. Therefore, the NUMERIC-FIELD and ALPHA-NUMERIC-FIELD can hold 10 digits and 10 alpha-numeric characters respectively.</p>
<p>To declare a decimal data field, we need to add the special character “V” in the numeric data field mask, which specifies the decimal point location. For example, the DECIMAL-NUMERIC-FIELD data field declared below let you store decimal values with up to 5 digits before and after the decimal point.</p>
<p></p>
<pre class="brush: plain">01 DECIMAL-NUMERIC-FIELD PIC 99999V99999.</pre>
<p></p>
<p>As you have probably noticed, the format mask can get very ugly for large fields and hence COBOL allows you to abbreviate it with the special character followed by the number of appearances in round brackets, so for example we can abbreviate “XXXXXXXXXX” to X(10) and “99999V99999” to 9(5)V9(5).</p>
<p>Let’s put these all together into a small program:</p>
<p></p>
<pre class="brush: plain"> IDENTIFICATION DIVISION.
PROGRAM-ID. EDITED.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ALPHA-FIELD PIC A(10).
01 NUMERIC-FIELD PIC 9(10).
01 ALPHA-NUMERIC-FIELD PIC X(10).
01 DECIMAL-NUMERIC-FIELD PIC 9(5)V9(5).
PROCEDURE DIVISION.
MOVE 'ABCEFG' TO ALPHA-FIELD
DISPLAY ' ALPHA-FIELD: ' ALPHA-FIELD.
MOVE 123456 TO NUMERIC-FIELD.
DISPLAY ' NUMERIC-FIELD: ' NUMERIC-FIELD.
MOVE 'ABC123' TO ALPHA-NUMERIC-FIELD
DISPLAY ' ALPHA-NUMERIC-FIELD: ' ALPHA-NUMERIC-FIELD.
MOVE 1234.5 TO DECIMAL-NUMERIC-FIELD.
DISPLAY 'EDITED-NUMERIC-FIELD: ' DECIMAL-NUMERIC-FIELD.
STOP RUN.</pre>
<p></p>
<p>Notice that I’ve used the abbreviated picture clause mask (e.g. X(10) instead of XXXXXXXXXX) in the example above. Also, we haven’t covered the MOVE command yet but basically that’s how you move (assign) values into data fields in COBOL. If you compile and run this program you’ll see the following outputs in the console:</p>
<p></p>
<pre class="brush: plain"> ALPHA-FIELD: ABCEFG
NUMERIC-FIELD: 0000123456
ALPHA-NUMERIC-FIELD: ABC123
EDITED-NUMERIC-FIELD: 01234.50000</pre>
<p></p>
<p>As you have probably noticed, unused digits in numeric fields are padded with zeros, which is quite ugly. We’ll cover how to make it look prettier with <em>Edited Fields</em> in the next tutorial.</p>
<p><strong>Update:</strong> This is the 3rd revision of this COBOL data fields tutorial, I’ve decided to cut this tutorial into two parts because it was getting too long and messy.</p> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com2tag:blogger.com,1999:blog-906333894881257630.post-31329695120775328882010-09-23T09:10:00.004+12:002010-09-26T11:27:18.547+13:00COBOL Tutorial 000100 – the ‘Hello World’<p>Due to a new project at work, I'm starting to learn COBOL (not the sexiest language, I know). I found the hardest part about learning COBOL for me is to know what COBOL keywords mean in terms of more modern programming languages. Therefore, I thought I’ll write a couple of short tutorials here to explain some of these differences in case some other programmers are interested in learning this 50+ years old programming language.</p> <p>As with learning any other programming language, the first example has to be the “Hello World” and here’s the source code:</p> <p></p> <pre class="brush: plain"> IDENTIFICATION DIVISION.
PROGRAM-ID. HELLOWORLD.
PROCEDURE DIVISION.
DISPLAY 'HELLO WORLD'.
STOP RUN.</pre>
<p></p>
<p>Every COBOL program needs an <code>IDENFICATION DIVISION</code> and the <code>PROGRAM-ID</code> (which is <code>HELLOWORLD</code> in our example). All program logic will sit under the <code>PROCEDURE DIVISION</code>. The rest of the program should be pretty self-explanatory.</p>
<p>The full stop (.) is the equivalent of semi-colon (;) in C-derived programming languages, which denotes the end of a coding line.</p>
<p>To compile this code, I used the <a href="http://www.opencobol.org">OpenCOBOL</a> compiler. You can install it under Ubuntu 10.04 by typing the following command in the shell:</p>
<p></p>
<pre class="brush:plain">sudo apt-get install open-cobol</pre>
<p></p>
<p>After installing the compiler, you can then compile the program by running (assuming that you’ve saved the source code in a file called <code>helloworld.cob</code>):</p>
<p></p>
<pre class="brush:plain">cobc -x -free helloworld.cob</pre>
<p></p>
<p>The <code>-free</code> compiler flag tells the <code>cobc</code> compiler to use the free source code format. Without it, the compiler will require you to enter 7 spaces at the beginning of each line. The <code>–x</code> flag, on the other hand, tells the compiler to produce an executable rather than a <code>.so</code> file (we’ll talk about .so files later).</p>
<p>Finally, the compiler may produce few warnings about “dereferencing type-punned pointer” but you can just ignore them. After the compilation finishes, you will find the executable <code>helloworld</code> in your directory.</p> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-7105810523257949002009-11-07T13:56:00.005+13:002010-09-26T20:44:50.176+13:00Few Tips for Running Ubuntu 9.10 Karmic Koala on HP Mini 1000<div xmlns="http://www.w3.org/1999/xhtml"><p>Now I have been running Ubuntu 9.10 on my HP Mini 1000 for more than a week and I'm pretty happy with it. In addition to my last <a href="http://blog.technicallyworks.com/2009/10/getting-hp-mini-1000-wireless-to-work.html">post</a> about getting the wireless to work, I thought I'd probably share a few more tricks I learnt in the pass week.</p>
<h2>Ethernet</h2>
<p>Out of box, the Ethernet seems to only work when the cable is plugged in before the system boots up and it will lock up the whole machine if you unplug it. To fix this issue you need to first open up the /etc/default/grub (used to be /boot/menu.list in grub 1 but Ubuntu 9.10 ships with grub 2) with your favourite text editor and locate the following line:</p>
<pre class="brush:plain">GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"</pre>
<p>and append <code>acpi_os_name=Linux</code> to it, so it should now look like this:</p>
<pre class="brush:plain">GRUB_CMDLINE_LINUX_DEFAULT="quiet splash acpi_os_name=Linux"</pre>
<p>save the file and run:</p>
<pre class="brush:shell">sudo update-grub</pre>
<p>After rebooting the machine, the Ethernet should work properly.</p>
<h2>External Monitor</h2>
<p>You have to disable visual effects by setting System -> Appearance -> Visual Effects to None in order for external monitor to work properly. Otherwise the machine just freezes everytime I connect an external LCD monitor to it.</p>
<h2>Firefox Cache Directory</h2>
<p>My HP Mini 1000 has a 16GB SSD drive, which can die quite quickly if being written to repetitively and web browsers such as Firefox writes cache data to disks quite frequently. Therefore, I prefer setting the Firefox cache folder to the ram disk to make my SSD drive last longer.</p>
<p>In order to change the cache folder, you first need to enter <code>about:config</code> in Firefox address bar and click on the "I'll be careful, I promise" button. Create a new string value by right click on the list and select "New -> String". Name the new value <code>browser.cache.disk.parent_directory</code> and set the value to <code>/dev/shm/</code><i><your folder name></i>. The folder name does not matter and the <code>/dev/shm</code> folder is the ram disk folder created by Ubuntu.</p>
<p><em>Update: </em>Corrected the grub file name and location, thanks guys</p>
</div>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com5tag:blogger.com,1999:blog-906333894881257630.post-77480410874404292822009-10-31T16:59:00.008+13:002009-10-31T17:37:28.632+13:00Getting HP Mini 1000 Wireless to Work Under Ubuntu 9.10 Karmic Koala Netbook Remix<p>I installed Ubuntu 9.10 Netbook Remix in my (actually my wife's) HP mini 1000 this afternoon. To my surprise the wireless card did not work. Also, when I looked at System -> Administration -> Hardware Drivers, the list was blank.</p>
<p>After few hours of googling and reading through several not too helpful forum posts, I learned that this was caused by Ubuntu 9.10 shipping "b43" driver out of box, which does not work for HP mini 1000. The proprietary driver "wl" should be used instead. However, no one said exactly what I needed to do to fix this problem.</p>
<p>Eventually, I decided to just launch Synaptic and search for "broadcom". The first result in the filtered list was bcmwl-kernel-source, which looked promising so I just went ahead and installed it.</p>
<p>I had a look at the /etc/modprobe.d folder after the installation finished, I noticed that the package actually created a blacklist file for "b43" related modules for me already. After reboot, my wireless card just worked.</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com41tag:blogger.com,1999:blog-906333894881257630.post-15902834821154609422009-08-18T20:16:00.007+12:002009-08-18T20:40:18.704+12:00VirtualBox: Folder Sharing between Windows Host and Linux Guest<p>To start, you need to install the guest additions. I use Ubuntu Server 9.04 so I followed steps outlined in <a href="http://joergweis.wordpress.com/2009/03/07/install-virtualbox-guest-additions-on-ubuntu-server-810/">this</a> post.</p>
<ol>
<li>install necessary tools for building kernel module by:
<pre class='brush:plain'>sudo apt-get install build-essential linux-headers-`uname -r`</pre></li>
<li>Next, click on menu "Devices" -> "Install Guest Additions..." then mount the guest addition CD-ROM within the Linux virtual machine by:
<pre class='brush:plain'>mount /dev/cdrom /media/cdrom0</pre></li>
<li>Finally, run the installation script:
<pre class='brush:plain'>sudo ./VBoxLinuxAdditions-x86.run</pre></li>
</ol>
<p>If you do not have X server installed, you may see a warning message about the X driver will not be installed. Ignore this message and now we are ready to configure the shared folder.</p>
<ol>
<li>Create the a folder for sharing on the host Windows machine (say C:\Shared).</li>
<li>Click on the menu "Devices" -> "Shared Folders..."</li>
<li>Add a new machine level shared folder by clicking on the + icon on the right</li>
<li>Enter "C:\Shared" for Folder Path and "shared" for the Folder Name.
<li>In Linux guest VM, create a folder /opt/shared and add the following line to the bottom of /etc/fstab:
<pre class='brush:plain'>shared /opt/shared vboxsf defaults 0 0</pre></li>
<li>Reboot the VM</li>
</ol>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-75067237416378072642009-08-17T20:55:00.009+12:002009-08-18T07:01:03.867+12:00Resolve Linux Hostname from Windows<p>The simplest solution is to install samba:</p>
<pre class='brush:plain'>sudo apt-get install samba</pre>
<p>According to <a href="http://ubuntuforums.org/archive/index.php/t-262876.html">this post</a>, this is because SAMBA implements NBT (NetBIOS over TCP/IP) protocol, which broadcasts the Linux machine's hostname on the network. Microsoft Windows understands the NetBIOS protocol so it pickups the Linux machine's hostname.</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com1tag:blogger.com,1999:blog-906333894881257630.post-75248337433067052382009-08-15T09:27:00.010+12:002009-08-15T11:18:05.383+12:00Fix Touchpad Scroll Area in Xubuntu 9.04<p>I have a 6 years old Compaq Presario X1000 that I use occasionally for web browsing and programming. I installed Xubuntu 9.04 on it the other day and found out that the scroll area is too wide for the touchpad.</p>
<p>As my usual trouble shooting procedure, I googled around for answers and I found pieces of information spread across several websites.</p>
<h2>Step 1 - Enable SHMConfig</h2>
<p>First of all, we need to enable the SHMConfig in order to determine the correct scroll area width with <code>synclient</code>. I am pretty sure I used to enable this from the X11 configuration file, however in Ubuntu 9.04 based distributions you are supposed to create a HAL fdi file for this.</p>
<pre class='brush:plain'>sudo vi /etc/hal/fdi/policy/touchpad.fdi</pre>
<p>Put this into the file</p>
<pre class='brush:xml'><?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2">
<device>
<match key="input.x11_driver" string="synaptics">
<merge key="input.x11_options.SHMConfig" type="string">True</merge>
</match>
</device>
</deviceinfo></pre>
<p>Save and close the file then reboot the machine. You can read more about this topic on <a href="https://help.ubuntu.com/community/SynapticsTouchpad#Enabling SHMConfig">Ubuntu community wiki</a>.</p>
<h2>Step 2 - Determine the correct scroll area with synclient</h2>
<p><code>synclient</code> helps you to determine the correct scroll area offset and is pre-installed on my Xubuntu.</p>
<p>Run</p>
<pre class='brush:plain'>synclient -m 1</pre>
<p>Now if you try to touch the touchpad, you will be able to see the coordinate of your finger in following format</p>
<pre class='brush:plain'>
time x y ...
0.000 5468 3475 ...</pre>
<p>After you determined the correct boundary of the scroll area you can then test it with</p>
<pre class='brush:plain'>synclient RightEdge=<your value></pre>
<p>On my laptop, when I move my finger to the right edge of the mouse movement area the x-coordinate is 5942 and it jumps to 8176 as soon as I touch the scroll area. So I tried:</p>
<pre class='brush:plain'>synclient RightEdge=8175</pre>
<p>but for some reason, it disabled the scroll area all together. After some head scratching, I found out that I had to use the x-coordinate right before the scroll area (i.e. 5942).</p>
<h2>Final step</h2>
<p>After figuring out your <code>RightEdge</code> value, you have to save it by adding it to the <code>touchpad.fdi</code> file created eariler. In my case:</p>
<pre class='brush:xml'><merge key="input.x11_options.RightEdge" type="string">5942</merge></pre>
<p>So the <code>touchpad.fdi</code> file now looks like this:</p>
<pre class='brush:xml'><?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2">
<device>
<match key="input.x11_driver" string="synaptics">
<merge key="input.x11_options.SHMConfig" type="string">True</merge>
<merge key="input.x11_options.RightEdge" type="string">5942</merge>
</match>
</device>
</deviceinfo></pre>
<p>Save and close the file then reboot and your touchpad scroll should work correctly now.</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-91073139793479022122009-08-09T10:28:00.003+12:002009-08-09T10:53:48.246+12:00Installing FIrefox 3.5 on Ubuntu 9.04 Jaunty Jackalope<p>Firefox is slow in Linux compared to XP or even Vista on the same hardware. Do a quick google search for "<a href="http://www.google.co.nz/search?hl=en&client=firefox-a&rls=com.ubuntu%3Aen-US%3Aunofficial&hs=jVF&q=firefox+slow+linux&btnG=Search&meta=">firefox slow linux</a>" and you see it's not just me who's saying this.</p>
<p>I tried a couple of tips from other users in discussion forums such as disabling visual effects and IPv6 but it didn't really help. That's why I started thinking about installing Firefox 3.5 on my Ubuntu 9.04 64-bits machine to see if it makes a difference.</p>
<p>At first I though I will have to download it directly from the Firefox website, however to my surprise I found the package in Synaptic. I am not sure which repository it came from but I can see the package "firefox-3.5" from the Synaptic package manager.</p>
<p>I went through the usual installation process and frankly it did not prompt me to remove existing firefox-3.0 packages. Instead, it installs the Firefox 3.5 alone side Firefox 3.0 under the name Shiretoko Web Browser in the Application -> Internet -> Shiretoko Web Browser.</p>
<p>Could be just my wishful thinking but Firefox 3.5 does seems to be a bit faster than 3.0.</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-80643461412195309222009-06-25T16:29:00.001+12:002009-06-25T21:51:55.545+12:00Load Testing ASP.NET Sites with JMeter<p>Following my previous <a href="http://blog.technicallyworks.com/2009/06/load-testing-aspnet-with-jmeter.html">post</a> about using JMeter to test MOSS, I tried to figure out what are the bare minimum requirements of using JMeter against a plain ASP.NET website. </p> <p>I wrote a very simple ASP.NET web application with just a button, a text fields and a static label. This application displays the content of a text file in the static label when it loads and write content of the text field back to the file when the button is clicked.</p> <p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/SkL9DI1Q-VI/AAAAAAAABIo/vgQ0EfIWZ_k/s1600-h/image%5B2%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkL9EV8JzaI/AAAAAAAABIs/p4bvF7e9hj0/image_thumb.png?imgmax=800" width="244" height="244" /></a> </p> <p>I found all I need to do in order to script this using JMeter is to extract __VIEWSTATE and __EVENTVALIDATION fields then send them back in the update request. My JMeter test plain looks like this:</p> <p><a href="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkL9FLQej6I/AAAAAAAABIw/0zFCA_ZZeDw/s1600-h/image%5B5%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkL9F6r3GFI/AAAAAAAABI0/aazIkmIJi0Q/image_thumb%5B1%5D.png?imgmax=800" width="244" height="198" /></a> </p> <p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkL9GZMy0AI/AAAAAAAABI4/f8J6PdfyPh8/s1600-h/image%5B8%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_3k_ZpkI8ITs/SkL9HZFX5RI/AAAAAAAABI8/OtXkudhCZz8/image_thumb%5B2%5D.png?imgmax=800" width="244" height="198" /></a> </p> <p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkL9HzPUzCI/AAAAAAAABJA/oPnfANEQ7RY/s1600-h/image%5B11%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/SkL9Iso7DtI/AAAAAAAABJE/pv8ob8xkEiI/image_thumb%5B3%5D.png?imgmax=800" width="244" height="198" /></a> </p> <p><a href="http://lh4.ggpht.com/_3k_ZpkI8ITs/SkL9JW_0WaI/AAAAAAAABJI/ZoHT04WRk58/s1600-h/image%5B14%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkL9KCIGpTI/AAAAAAAABJM/lWDoMzDarF8/image_thumb%5B4%5D.png?imgmax=800" width="244" height="198" /></a></p> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com12tag:blogger.com,1999:blog-906333894881257630.post-57776506080883482772009-06-23T11:40:00.003+12:002009-06-25T21:52:55.257+12:00Load Testing SharePoint (MOSS) Sites with JMeter<p>I have used JMeter for load testing few non-ASP.NET web sites before, however I could not get it to work with ASP.NET web sites. This is mainly due to ASP.NET ViewState and event validations, which stops a recorded JMeter script from being played back.</p> <p>Recently I worked on a MOSS project and we were looking for tools to perform load testing on the server. Many people said the load testing tool in Microsoft Team System for Testers works well with MOSS. However, it is quite expensive so I decided to give JMeter another go. After several hours of hacking, I actually got it to work and here’s how I did it.</p> <p>My test page is the pretty standard MOSS edit document property screen with few extra text fields added and the goal here is to use a JMeter script to change the document properties. Once I have a working script, I can configure JMeter to fire hundreds of instances of this script simultaneously to simulate the user workload.</p> <p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkBOLZ0UwhI/AAAAAAAABHQ/C429tsgjutU/s1600-h/image%5B5%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkBOMFihDCI/AAAAAAAABHU/-q80UZhxgbA/image_thumb%5B1%5D.png?imgmax=800" width="244" height="94" /></a> </p> <p>As shown in the screenshot below, the test plan contains two HTTP requests recorded using the JMeter HTTP Proxy component and four JMeter Regular Expression Extractors in between them.:</p> <p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkBf-MBxz4I/AAAAAAAABHw/XHBq3uycsZk/s1600-h/image%5B5%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/SkBf_JY7TzI/AAAAAAAABH0/ML7ImfjJZT0/image_thumb%5B1%5D.png?imgmax=800" width="218" height="200" /></a></p> <p>The main trick here is to capture four key MOSS fields from the HTTP response of the “Load Edit Property From” HTTP request and send them back to the server <strong>AS IS</strong> in the “Submit Edit Property From” HTTP request along with new property values. These key fields are:</p> <ol> <li>__EVENTVALIDATION </li> <li>__VIEWSTATE </li> <li>__REQUESTDIGEST </li> <li><em><control ID</em>>_owshiddenversion  </li> </ol> <h2>Load Edit Property Form </h2> <p>The Load Edit Property Form step is a simple JMeter HTTP Sampler generated by running the JMeter HTTP Proxy component and recording the HTTP request created by click on the Edit Properties menu item in the SharePoint drop down.</p> <p> <a href="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkFI9iqZg5I/AAAAAAAABH4/Z93Jcnpq_rM/s1600-h/image%5B8%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkFI-grlNTI/AAAAAAAABH8/V1wEiA6Z9Co/image_thumb%5B2%5D.png?imgmax=800" width="210" height="228" /></a> </p> <p>The response from the MOSS server will contains the four key fields that can be captured using JMeter Regular Expression Extractors. </p> <h2><strong>Extract Event Validation</strong></h2> <p>Regular Expression:</p> <pre class="brush:plain">id="__EVENTVALIDATION" value="(.+?)"</pre>
<p>Screenshot:</p>
<p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/SkFI_A2Bf2I/AAAAAAAABIA/AJUxGNp6DVw/s1600-h/image%5B23%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkFI_4DVptI/AAAAAAAABIE/ieukx_exntI/image_thumb%5B7%5D.png?imgmax=800" width="244" height="96" /></a> </p>
<h2>Extract View State</h2>
<p>Regular Expression:</p>
<pre class="brush:plain">id="__VIEWSTATE" value="(.+?)"</pre>
<p>Screenshot:</p>
<p><a href="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkFJAUtyA8I/AAAAAAAABII/-kvafRXdjPw/s1600-h/image%5B26%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkFJBFskM4I/AAAAAAAABIM/-E9G9dAuhdM/image_thumb%5B8%5D.png?imgmax=800" width="244" height="96" /></a> </p>
<h2>Extract Request Digest</h2>
<p>Regular Expression:</p>
<pre class="brush:plain">id="__REQUESTDIGEST" value="(.+?)"</pre>
<p>Screenshot:</p>
<p><a href="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkFJBqYNpWI/AAAAAAAABIQ/WHm0cZuNx4Y/s1600-h/image%5B29%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_3k_ZpkI8ITs/SkFJCqD_JYI/AAAAAAAABIU/7RXQnW53qx0/image_thumb%5B9%5D.png?imgmax=800" width="244" height="96" /></a> </p>
<h2>Extract Hidden Version</h2>
<p>This one is not so straight forward as the other three because the <em><Control ID></em> changes from page to page depending on the layout. It can be found by simply searching the string “owshiddenversion” in the HTML source for the edit property page. The HTML tag should look something like this:</p>
<pre class="brush:html"><input id="ctl00_m_g_026c19e0_cd4b_48c9_a4b3_9e7409f252ac_ctl00_ctl02_ctl00_ctl05_ctl00_owshiddenversion" type="hidden" value="15" name="ctl00$m$g_026c19e0_cd4b_48c9_a4b3_9e7409f252ac$ctl00$ctl02$ctl00$ctl05$ctl00$owshiddenversion" /></pre>
<p>Hence, the regular expression in this case is:</p>
<pre class="brush:plain">id="ctl00_m_g_026c19e0_cd4b_48c9_a4b3_9e7409f252ac_ctl00_ctl02_ctl00_ctl05_ctl00_owshiddenversion" value="(.+?)"</pre>
<p>and the screenshot:</p>
<p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkFJDOQsofI/AAAAAAAABIY/LPfO2wQo3p4/s1600-h/image%5B32%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/SkFJEA2gVOI/AAAAAAAABIc/QV-GSm7rmBM/image_thumb%5B10%5D.png?imgmax=800" width="244" height="96" /></a> </p>
<h2>Submit Edit Property Form</h2>
<p>The “Submit Edit Property Form” JMeter HTTP sampler is generated by recording the Submit button click using the JMeter HTTP Proxy component. JMeter displays all POST parameters contained in this request. </p>
<p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/SkK-UUNbJBI/AAAAAAAABIg/_P-__EcOcAk/s1600-h/image%5B5%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/SkK-VT-hVpI/AAAAAAAABIk/3Ry2AD2427U/image_thumb%5B1%5D.png?imgmax=800" width="244" height="76" /></a> </p>
<p>The <em>Reference Names</em> of the four keys fields (e.g. <code>${viewState}</code> as shown in screenshot above) captured previously need to be entered into their corresponding parameter value fields. My test script also updates other parameters such as author and title for my testing purpose.</p> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com9tag:blogger.com,1999:blog-906333894881257630.post-26152741924050006712009-06-15T20:30:00.008+12:002009-06-16T08:41:45.806+12:00Installing VMware Server 2.0.1 on Ubuntu 9.04I use Ubuntu on daily basis and I can live without Windows most of the time. However, once in a while I need to use Windows for things like accessing the iTunes store and editing Microsoft Word documents (yes I know OpenOffice.org works but it messes up the style from time to time). I created a WIndows VM image under VirtualBox, which worked quite well except that I noticed the constant high CPU load as mentioned by <a href='http://forums.virtualbox.org/viewtopic.php?t=7552'>several other users</a>. I tried their suggestions such as using nohz=off and turn off ACPI, which did reduce the CPU load to 50% on one core but didn't get rid of the problem completely.
<br/><br/>
I eventually decided to give the free VMware Server a try. Setting it up on Windows was pretty simple, just double click and then click through the Wizard. However, installing it on Ubuntu 9.04 is a non-trivial task and took me a little while to complete.<br/>
<ol>
<li>Launch Synaptic package manager and make sure you have linux-header, linux-source, gcc and binutils installed.</li>
<li>Download the .tar.gz package from VMware web site (VMware-server-2.0.1-156745.x86_64.tar.gz in my case).</li>
<li>Extract the files then run the the installation script as super user<pre class='brush:plain'>sudo vmware-install.pl</pre></li>
<li>The installer will prompt you with half a thousand questions, I just use the default value for most of them.</li>
<li>Eventually the installer will start compiling and loading kernel modules, most of them will go through fine but the VSOCK module installation will fail with the following error message.<br /><pre class='brush:plain'>Unable to make a vsock module that can be loaded in the running kernel:<br/>
insmod: error inserting '/tmp/vmware-config0/vsock.o': -1 Unknown symbol in module</pre>
Ignore this insmod error for now and continue to finish the installation.</li>
<li>Apply the following patch to vmware-config.pl, this fixes the insmod error above so we can re-run it to complete the VSOCK module installation. <pre class='brush:diff'>+++ /usr/bin/vmware-config-tools.pl 2008-12-01 16:55:59.000000000 +0100
@@ -4121,6 +4121,11 @@
return 'no';
}
+ if ($name eq 'vsock') {
+ print wrap("VMWare config patch VSOCK!\n");
+ system(shell_string($gHelper{'mv'}) . ' -vi ' . shell_string($build_dir . '/../Module.symvers') . ' ' . shell_string($build_dir . '/vsock-only/' ));
+ }
+
print wrap('Building the ' . $name . ' module.' . "\n\n", 0);
if (system(shell_string($gHelper{'make'}) . ' -C '
. shell_string($build_dir . '/' . $name . '-only')
@@ -4143,6 +4148,12 @@
if (try_module($name, $build_dir . '/' . $name . '.o', 0, 1)) {
print wrap('The ' . $name . ' module loads perfectly into the running kernel.'
. "\n\n", 0);
+
+ if ($name eq 'vmci') {
+ print wrap("VMWare config patch VMCI!\n");
+ system(shell_string($gHelper{'cp'}) . ' -vi ' . shell_string($build_dir.'/vmci-only/Module.symvers') . ' ' . shell_string($build_dir . '/../'));
+ }
+
remove_tmp_dir($build_dir);
return 'yes';
}</pre></li>
<li>Now run the vmware-config.pl script as super user and the VSOCK module should compile and install fine now.<pre class='brush:shell'>sudo vmware-config.pl</pre></li>
<li>Now run <code>vmware</code> from the command prompt to launch vmware UI in your browser. You will see following error message as shown below:<br/><img src='http://lh6.ggpht.com/_3k_ZpkI8ITs/SjR_8V2AP5I/AAAAAAAABGU/7qm4L4tLfig/s400/Page%20Load%20Error.jpg' style='max-width: 800px;'/></li><li>Click on "Add Exception", which will pop up another dialog. Click on "Get Certificate" and then "Confirm Security Exception" to add the VMware URL to the exception list.<br/><img src='http://lh3.ggpht.com/_3k_ZpkI8ITs/SjR_8fO5HBI/AAAAAAAABGQ/4LzOvr1S0Ec/s400/Add%20Security%20Exception.png' style='max-width: 800px;'/><br/></li><li>Finally, you should see the login screen. You need to login as the root user but Ubuntu does not set password for root user by default. Therefore, you first need to set the password by executing <pre class='brush:shell'>sudo passwd root</pre>after setting the password just login as root and enjoy.<br/></li>
</ol>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com6tag:blogger.com,1999:blog-906333894881257630.post-14168437304342846002009-05-27T18:51:00.001+12:002009-05-27T19:12:20.770+12:00Ubuntu 9.04 and NVIDIA Driver<div xmlns='http://www.w3.org/1999/xhtml'>I have been using Ubuntu for quite a while and I loved it for it's simplicity. I downloaded and installed 9.04 in my machine last night and to my surprise I no longer get the nice pop-up prompting me to install proprietary NVIDIA driver for my NVIDIA 8800 GTS graphics card.<br/><br/>The "nv" driver worked fine but it doesn't allow me to enable desktop effects. Also, I really want the full hardware acceleration so I can do some OpenGL programming. After search around the ubuntu package repository, I found several versions of the old familiar nvidia-glx. So I installed the latest one with:<br/><pre class='brush:plain'>sudo apt-get install nvidia-glx-180</pre>Another surprise was that it no longer updates the xorg.conf file for me so I ended up running nvidia-xconfig myself by:<br/><pre class='brush:plain'>sudo nvidia-xconfig</pre>This overrides the xorg.conf file to make sure X server loads the proprietary "nvidia" rather than open source "nv" driver. I wonder what the Ubuntu team was thinking, this definitely felt like a step backward. Were they trying to push people to use the open source "nv" driver?</div>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-15026131983606740562009-05-26T19:43:00.001+12:002009-05-26T21:09:55.992+12:00East Coast Trip<p>I met a former colleague who was born in Hawkes bay and had some random chat about work and holidays. This was when I realised that even though I’ve been living in New Zealand for 12 years, I’ve never travel to the east coast of north island before. Therefore, when my wife said she felt like to go for a holiday I immediately suggested that we should travel to the east.</p> <h2>Day 1</h2> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 5px; display: inline; float: left; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:f25aeec7-05d9-4859-a70a-70626e85fde8" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="144" src="http://lh3.ggpht.com/_3k_ZpkI8ITs/Sg1OTXmM6RI/AAAAAAAAAQU/jKcgGxVMIoE/s144/IMG_0052.JPG" width="108"></a></p></div> <p>Napier is quite far away from Auckland (420 km, ~6 hours drive according to Google Map), so we’ve decided to stop by few other cities alone the way. Our first stop was Rotorua, which is famous for its geothermal sites and hot pools. After checked into our hotel, we were wondering around the street trying to find a place for dinner. Eventually, I pulled out my iPhone, browsed to <a href="http://www.tripadvisor.com/">tripadviser.com</a>, found a highly rated bar <a href="http://www.pigandwhistle.co.nz/get_flash.html">Pig & Whistle</a> and found it using iPhone GPS and Google Map. I just can’t imagine how to live without iPhone and Internet these days. After dinner, we visited the <a href="http://www.polynesianspa.co.nz/">Polynesian Spa</a>, which is quite famous in New Zealand for its geothermal hot pools. </p> <h2>Day 2</h2> <div style="padding-bottom: 0px; margin: 0px; padding-left: 5px; padding-right: 0px; display: inline; float: right; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:4126a550-438f-4588-b53b-c15714cb965f" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="144" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/Sg1ERFFnkVI/AAAAAAAAAO0/1moYnPPftjE/s144/IMG_0073.JPG" width="108"></a></p></div> <p>Our plan for the day was to drive from Rotorua to Napier. We stopped by Huka fall prawn farm and Lake Taupo along the way. We arrived Huka fall prawn farm at around 11am and joined the hatchery tour to see how they feed prawns. All I can say is life of a prawn is sad, you either fight and eat your fellow prawns or be eaten. We actually saw that in action, a prawn killed another one and ate it. </p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 5px; display: inline; float: left; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:781d9822-a0ef-4e2f-8394-9fe690576641" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="144" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/Sg1OomZYcyI/AAAAAAAAAQk/qzcsk3miwKA/s144/IMG_0078.JPG" width="108"></a></p></div> <p>After the tour we went on to prawn fishing, we spent one hour sitting in the cold winter and just couldn’t catch any. We gave up at the end because we were so cold and hungry. We had lunch in the prawn farm restaurant then headed to Taupo. We didn’t spent too much time in Taupo, we took a short walk around the lake side, the museum and the city centre then headed off to Napier.</p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 5px; padding-right: 0px; display: inline; float: right; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:9665bc43-e2ce-4d72-99cb-9c30f6133419" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="144" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/Sg1PITNtDkI/AAAAAAAAAQ4/asVa2uaAJok/s144/IMG_0088.JPG" width="108"></a></p></div> <p>The first half of the drive was quite enjoyable, it was flat and road was straight with nice country side views. We stopped by a nice little lookout alone the way. The second half, however, were terrible. It was all mountain road and unbelievably foggy. Eventually, We managed to arrived Napier safely and checked into the hotel at around 5:30pm</p> <h2>Day 3</h2> <p>The third day was quite a boring one. Firstly, the weather was crap, it was wet and cold. Secondly, I somehow had this horrible stomach ache and I can’t walk a long distance or eat anything. We visited few lookout in the morning but then I slept through the afternoon. </p> <h2>Day 4</h2> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 5px; display: inline; float: left; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:043d3f28-53af-4b4a-899e-3d6545c952ef" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="144" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/Sg3JEMm7P6I/AAAAAAAAAVg/FhGW-sNnPb0/s144/IMG_0157.JPG" width="108"></a></p></div> <p>My stomach finally recovered after a whole day rest and we decided to join the guided Art Deco walking tour in Napier. There was a massive 7.8 earthquake in Napier in early 30s, which destroyed most of its commercial district. They rebuild the whole city with the most modern architecture style of that period called  <a href="http://en.wikipedia.org/wiki/Art_Deco">Art Deco</a>.  I really enjoyed the tour and the guide told very nice stories about the city and its buildings. I would definitely recommend anyone who have a chance to visit Napier to join the guided tour.</p> <p>We had lunch at <a href="http://cobb.co.nz/">Cobb & Co</a> in Napier, which had really nice 2 course lunch for just $10, visited the mission estate winery and then we were on our way to Gisborne. </p> <h2>Day 5</h2> <div style="padding-bottom: 0px; margin: 0px; padding-left: 5px; padding-right: 0px; display: inline; float: right; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:9bd11fb0-1f42-4446-be0e-41d2c8513161" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="108" src="http://lh5.ggpht.com/_3k_ZpkI8ITs/Sg3NIXZODrI/AAAAAAAAAY8/Kc-qFIw7K5g/s144/IMG_0261.JPG" width="144"></a></p></div> <p>According to <a href="http://en.wikipedia.org/wiki/Gisborne,_New_Zealand">Wikipedia</a>, Gisborne is the first city to see the sun shine each day. Therefore we decided to wake up early in the morning to see the sunrise. We waked up at 6am and I was kinda worried that the sun will already be up by the time we reach the beach. Our sacrifice to sleep did pay off, the sky was clear and the sunrise was really beautiful.</p> <p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 5px; display: inline; float: left; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:2bb42d3e-200f-4e5d-88dd-3723552408b8" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="108" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/Sg3PgPSa1jI/AAAAAAAAAa8/r012UKCQFlE/s144/IMG_0305.JPG" width="144"></a></p></div> </p> <p>After seeing the sunrise, we drove to the Tolaga Bay, it has the longest concrete wharf in New Zealand. At 660m, it is said to be the longest concrete wharf in the southern hemisphere. That was pretty much the last stop of our east cost trip. We were heading back home.</p> <p>We headed off to Tauranga after lunch and it took us around 4 hours to reach there. We took a walk in Mt Mauao and had dinner in a Turkish restaurant.</p> <h2>Day 6</h2> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 5px; display: inline; float: left; padding-top: 0px" id="scid:51CF81A4-8F44-4a2c-8837-198C090B9994:54f8b422-4adb-4e7b-a809-ff8d13a459ff" class="wlWriterEditableSmartContent"><p><a href="http://picasaweb.google.com/oscar.y.kuo/EastCoastTrip" atomicselection="true"><img style="border-right: 2px; border-top: 2px; border-left: 2px; border-bottom: 2px" height="108" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/Sg3Ty4IZAWI/AAAAAAAAAd4/vCoCxQ6zoAU/s144/IMG_0359.JPG" width="144"></a></p></div> <p>It was time to go home, I had been computer-less for 5 days and I really missed it. We stopped by the Karangahake Gorge, which has a 1 meter long dark tunnel. We tried to walk through it but it was just way too dark and scary. We only spent around 5 minutes in it then headed back to our car. </p> <p>We arrived Auckland at around 5pm and gosh the weather was miserable.</p> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-46970375943269304632009-05-16T10:29:00.001+12:002009-05-16T11:06:44.991+12:00Referencing Local Variables in jQuery Callback Functions<p>One day I was working on a <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> application, which contains a lot of form fields and controls. Each of them requires a mouse over event handler to display a tooltip message. After lots of copy’n’pasting, I decided to refactor the repetitive event registration code out and put them into a loop. </p> <p>To test out my idea, I developed a very simple page with just three <div>s:</p> <pre class="brush:xml"><div id="div1">Click me</div>
<div id="div2">Click me</div>
<div id="div3">Click me</div></pre>
<p>and a list of messages indexed by the <div>s’ ID:</p>
<pre class="brush:javascript">var messages = new Object();
messages['div0'] = 'hello';
messages['div1'] = 'bonjour';
messages['div2'] = 'ciao';</pre>
<p>When someone click on one of the <div>’s region, I would like to show a popup an dialog and display the message associated with it’s ID. Since my goal was to eliminate repetitive lines of code, I put the event registration code in a loop:</p>
<pre class="brush:javascript">for(var i=0; i<3; ++i) {
$("#div" + i).click(function() {
alert(messages['div' + i]);
});
}</pre>
<p>When I tried this out, instead of showing the right message, the alert box always shows ‘undefined’:</p>
<p><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/Sg3s3cPDVDI/AAAAAAAAAiE/vFpb24va4WM/s1600-h/image%5B3%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_3k_ZpkI8ITs/Sg3s4ZrliWI/AAAAAAAAAiI/KfraFxCS4dg/image_thumb%5B1%5D.png?imgmax=800" width="282" height="104" /></a> </p>
<p>After a lot of head scratching, I finally realised that the problem is with this line of code referencing the loop variable <em>i:</em></p>
<pre class="brush:javascript">alert(messages['div' + i]);</pre>
<p>The computer scientists’ way of describing this is that it formed a “<a href="http://en.wikipedia.org/wiki/Closure_(computer_science)">Closure</a>” referencing the variable <em>i</em>. Since this line of code sits inside the callback function,  the ‘<em>div’ + i</em> statement wasn’t evaluated to ‘div0’, ‘div1’ and ‘div2’ in each loop iteration as I expected. Instead, because each of the callback function holds a reference to the variable <em>i</em>, the ‘<em>div’ + i </em>statement was using the final value of <em>i</em>, which is 3 in this case.</p>
<p>This can be proven by changing the <em>alert</em> statement to show the value of <em>i</em>, which will always be 3 when any of the <div>s is clicked.</p>
<pre class="brush:javascript">alert(i);</pre>
<p>I Googled around for solutions but most of them are pretty complicated but eventually I found an answer in the jQuery reference document. All jQuery objects have a data() method, which allows you to bind any data to it. The bound data will be instance specific and the value will be evaluated at the binding time. </p>
<p>Therefore, to fix my code I just have to change the event registration code to:</p>
<pre class="brush:javascript">for(var i=0; i<3; ++i) {
$('#div' + i).data('divID', 'div' + i);
$('#div' + i).click(function() {
alert(messages[$(this).data('divID')]);
});
}</pre>
<p>So in each iteration of the loop, the value ‘div0’, ‘div1’ and ‘div2’ are stored into the ‘divID’ data section of the respective <div> objects and then the callback function uses the stored value to find out which message to display when they are clicked.</p>
<p>The final solution looks like this:</p>
<pre class="brush:javascript">$(function() {
var messages = new Object();
messages['div0'] = 'hello';
messages['div1'] = 'bonjour';
messages['div2'] = 'ciao';
for(var i=0; i<3; ++i) {
$('#div' + i).data('divID', 'div' + i);
$('#div' + i).click(function() {
alert(messages[$(this).data('divID')]);
});
}
});</pre> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com4tag:blogger.com,1999:blog-906333894881257630.post-44923391725405646682009-04-28T20:27:00.001+12:002009-05-10T09:38:33.057+12:00Building Native 64-bits Boost Library<p>I started using 64-bits Vista recently and I thought it made sense to make a native 64-bits build my favourite C++ library too. </p> <p>My first attempt was a complete failure because I naively thought all I have to do is to run the bjam in Visual Studio 64-bits Tools Command Prompt. It turned out this only builds a regular 32-bits Boost library.</p> <p>After some Googling and reading forum posts, I found some useful information in the <a href="http://www.boost.org/doc/tools/build/doc/html/bbv2/reference/tools.html">Boost.Build document</a>. Apparently, Boost does support building 64-bits target and what I end up doing is to specify the <em>architecture</em> and <em>address-mode</em> flags when running the bjam.</p> <pre class="brush:plain">c:\boost_1_38_0>bjam ^
More? --toolset=msvc ^
More? --build-type=complete ^
More? architecture=x86 address-model=64 ^
More? stage
</pre>
<p>then I installed the library by</p>
<pre class="brush:plain">c:\boost_1_38_0>bjam ^
More? --toolset=msvc ^
More? --build-type=complete ^
More? architecture=x86 address-model=64 ^
More? install
</pre> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0tag:blogger.com,1999:blog-906333894881257630.post-44083734618213330672009-04-22T21:03:00.002+12:002009-08-19T20:33:58.216+12:00Installing the WTL Application Wizard in Visual C++ 2008 Express Edition<p>I love Google Chrome, it is fast, elegant and beautiful. After I realized that it was written using WTL, I felt quite keen to learn about this library.</p> <p>I downloaded WTL 8.0 from SourceForge.net and Visual C++ 2008 Express Edition from Microsoft.com only to realise that there is no WTL Wizard support for Visual C++ 2008 Express Edition. </p> <p>WTL 8.0 ships with WTL/ATL application wizard but the setup script only supports the Visual C++ 2005 Express Edition (setup80x.js). However, the good news is that you can make it work with Visual C++ 2008 Express Edition in few simple steps:</p> <ol> <li>Make a copy of the setup80x.js and rename it to setup90x.js. </li> <li>Open setup90x.js up and do a global search and replace from “8.0” to “9.0”. <br /><a href="http://lh6.ggpht.com/_3k_ZpkI8ITs/Se7dTcH2jWI/AAAAAAAAAK8/D73wzEm-LBM/s1600-h/image%5B6%5D.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="168" alt="image" src="http://lh3.ggpht.com/_3k_ZpkI8ITs/Se7dUSDLHuI/AAAAAAAAALA/wNHLeFbcJ8s/image_thumb%5B2%5D.png?imgmax=800" width="308" border="0" /></a> </li> <li>Save the file and execute it. </li> </ol> <p>If you are as lucky as I am, you should see a dialog that tells you that the wizard has been successfully installed.</p> <p><a href="http://lh5.ggpht.com/_3k_ZpkI8ITs/Se7dWA_W2sI/AAAAAAAAALE/MDTuqmJ3tH8/s1600-h/image%5B5%5D.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="154" alt="image" src="http://lh6.ggpht.com/_3k_ZpkI8ITs/Se7dXGFb0NI/AAAAAAAAALI/epfFQcDue5M/image_thumb%5B1%5D.png?imgmax=800" width="244" border="0" /></a> </p> <p>Now, when you run Visual C++ 2008 Express Edition and go File –> New –> Project… you should now see the new WTL/ATL Application Wizard.</p> <p><a href="http://lh3.ggpht.com/_3k_ZpkI8ITs/Se7e3pc-kCI/AAAAAAAAALM/k-R0DmyWpqY/s1600-h/image%5B10%5D.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="258" alt="image" src="http://lh3.ggpht.com/_3k_ZpkI8ITs/Se7e5DiIn-I/AAAAAAAAALQ/c8UFujTCu40/image_thumb%5B4%5D.png?imgmax=800" width="411" border="0" /></a> </p> <p>For some reason when I tried to create a new solution from the wizard, the generated code doesn’t compile. It gives me the following error:</p> <pre class="brush:ps">stdafx.h(33) : error C2065: '_stdcallthunk' : undeclared identifier</pre>
<p>To fix this problem I had to manually add this include:</p>
<pre class="brush:cpp">#include <atlstdthunk.h></pre>
<p>into the generated stdafx.h, right after this line</p>
<pre class="brush:cpp">#include <atlbase.h></pre>
<p>This worked on my machine anyway :)</p>Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com5tag:blogger.com,1999:blog-906333894881257630.post-55091340114555319502009-04-13T09:40:00.001+12:002009-05-10T09:30:47.600+12:00Updating .config Files from Visual Studio Setup Project<p>To open Web.config from within a web setup project:</p> <pre class="brush:csharp">string path = Context.Parameters["assemblypath"];
path = path.Substring(0, path.LastIndexOf(
Path.DirectorySeparatorChar));
path = Path.Combine(path, "Web.config");
var config =
ConfigurationManager.OpenExeConfiguration(path)</pre>
<p>To open App.config from within a setup project:</p>
<pre class="brush:csharp">var map = new ExeConfigurationFileMap();
map.ExeConfigFilename =
Context.Parameters["assemblypath"] + ".config";
var config =
ConfigurationManager.OpenMappedExeConfiguration(
map, ConfigurationUserLevel.None);</pre>
<p>To update settings in .config files</p>
<pre class="brush:csharp">// update connection strings
var cs = config.ConnectionStrings;
cs.ConnectionStrings["cs1"].ConnectionString =
BuildConnectionString(host, user, pass);
// update app settings
var appSettings = config.AppSettings;
appSettings.Settings["key"].Value = "new value";
</pre>
<p>Finally, to save the .config file changes</p>
<pre class="brush:csharp">config.Save();</pre> Oscar Kuohttp://www.blogger.com/profile/17330923022574523457noreply@blogger.com0