Analytics


Google

Saturday, March 29, 2008

Blog Utilities

When I first started this blog. The first thing I started to do was to find a good site to host the blog for free. There was a few available but I found blogger to be the one with the most lenient, which is why I chose it.

Then I started placing images but found that Google placed a 1 Gbyte limit. Then after discussing with colleagues of mine, I was told that there is a lot of things that can be done with the blog.

You can at chats into your blog using cbox, see the side of this blog. Then you can also keep track of your blog statistics using tools from this site. It is also nice to see where your visitors are coming from using tools from this site. Another site providing live feed of the traffic into your blog is here.

Finally, there are a lot of widgets you can add to your blog, which has been collected here.

ASP.Net behavior in IIS

A friend of mine highlighted this article to me:

http://aspnetresources.com/articles/debug_code_in_production.aspx

Even though the article referred to Framework 1.1 but it looks like it is still applicable for Framework 2.0, 3.0 and 3.5.

Some of my key take aways from the article are:

  1. When deploying an application, it is critical to remember to change the debug to false.
  2. Frequent changes on the aspx files will also generate additional files which will only be removed when the application is restarted or the web.config is edited.
  3. Too many aspx files in a folder will incur high start up time, since compilation happens to many files even though only one is accessed.
<compilation debug="false" />
There are a lot more information there and I believe is critical for developers too.

Side Note

On the server (hosting your application), you should always configure your antivirus not to scan your web.config, global.asax and global.asa. From my experience:
  • the act of the antivirus scanning will cause IIS to detect those as changes to those files and will force a restart on the applications.
  • And the very act of accessing any of those pages, will cause the antivirus to scan those files.
Consequently, you may end up with an application that will only allow one person to access the application.

Friday, March 28, 2008

Flow Chart Utilities - DIA vs Visio

One of the key things we need to do as programmers is to draw flow chart, for this I found Visio to be invaluable. However, for those on a budget there are several options available - there are some free tools available, the ones I have tried are DIA and Open Office Draw.

DIA
The screenshots are here.
I found that this is a very rich tool which allows you to perform various kinds of flowcharts. It also a lot of galleries to help with the various diagrams.

Pro:
  • Very powerful
  • Rich selection of galleries.
  • Easy to use.
  • linking of objects for the lines are easily done.
Con:
  • Not easy to do a database diagram (needs to be done manually)
  • Cannot write notes on the line - have to add a new text object to do that.
OpenOffice Draw

OpenOffice Draw is a component of OpenOffice. It is able to draw the basic flow charts and provides those shapes. However, it lacks the other shapes for all the other types of charts.

Viso

Microsoft Visio is much more complete. If you go to the higher end version, you can even do things like reverse engineering your database tables or generate scripts to create the required tables. It is also able to scan your network to create a diagram of your network devices. These are things that you may need when documenting your projects.

Side Note

If your purpose is just to create UML from your application, you can do it directly using:
  • the class designer in Visual Studio 2008
  • directly from Netbeans or Eclipse (if you are using Java) - there are free addons that will do that for you.

Wednesday, March 26, 2008

Lotus NOTES Qtrly Maintenance Task

Domino Server Tasks:

1. shut down Domino
2. Rename log file
3. Rename mail.box file(s)

Servers with Transactional Logging enabled:
(note; these are the NT commands)
4. nfixup names.nsf -J -F
5. ncompact names.nsf -b
6. nupdall names.nsf -R -X

7. nfixup admin4.nsf -J -F
8. ncompact admin4.nsf -b
9. nupdall admin4.nsf -R -X

Servers withOUT Transactional Logging enabled:
(note; these are the NT commands)
4. nfixup names.nsf -F
5. ncompact names.nsf -c -i
6. nupdall names.nsf -R -X

7. nfixup admin4.nsf -F
8. ncompact admin4.nsf -c -i
9. nupdall admin4.nsf -R -X

OS Windows tasks:

1. Delete files in 'Temp' directory
2. Clear all logs in Event viewer
3. OS account management:
a. Remove any unnecessary group in 'administrator' group. i.e: AM\Domain Admins
b. Remove any unnecessary group in 'users' group. i.e: AM\domain users
c. Check other groups and remove any unnecessary group or person
4. Disable then enable swap file (this task will require a reboot)
· Start Domino
· Recover any mail in renamed mail.box files.

List of Lotus NOTES Console commands

The list of all the Lotus NOTES Console command are available in the following links:


To see all the TCPIP connections, just type the following in the console

show port tcpip

Tuesday, March 25, 2008

Timezone support in Application

Oracle Support of TimeZone

The following article provides a very good explanation on how timezone support in Oracle.

http://www.devx.com/dbzone/Article/30501

The following function is extract from the article above:


create or replace FUNCTION
convert_time(datetime IN TIMESTAMP, tz1 IN VARCHAR2, tz2 IN VARCHAR2)
RETURN TIMESTAMP WITH TIME ZONE AS
retval TIMESTAMP WITH TIME ZONE;
/*
Obtained from http://www.devx.com/dbzone/Article/30501
Date: Mar 25, 2008

Note:
List of timezones available via

select * from V$TIMEZONE_NAMES

More reference:
http://toolkit.rdbms-insight.com/tz.php

Sample usage:
select to_char(convert_time(sysdate, 'Asia/Kuala_Lumpur', 'US/Mountain'), 'dd-MON-yyyy HH24:mi') cDate from dual
*/

BEGIN

retval := from_tz(datetime, tz1) AT TIME ZONE tz2;
RETURN retval;

END;


The following a sample of how to use the above function:

select convert_time(sysdate, 'Asia/Kuala_Lumpur', 'US/Mountain') from dual;

select to_char(convert_time(sysdate, 'Asia/Kuala_Lumpur', 'US/Mountain'), 'dd-MON-yyyy HH24:mi') cDate from dual;



Some useful links related to this is found at the following url:

http://stanford.edu/dept/itss/docs/oracle/10g/server.101/b10749/ch4datetime.htm
http://www.dbasupport.com/oracle/ora9i/TimeZone.shtml
http://toolkit.rdbms-insight.com/tz.php



.Net Framework 1.1 and 2.0 does not have an easy way to convert time to the various timezone. It is necessary to manually calculate the offset and then perform the computation. This is a problem due to the changing daylight savings. However, the following is a good library recommended by several sites to help with the conversion:

http://www.michaelbrumm.com/simpletimezone.html





VB.NET Framework 3.5

TimeZoneInfo.ConvertTimeBySystemTimeZoneId(oldTime, FromZoneId, ToZoneId)

The list of TimeZone IDs are as follows:

Greenwich Standard Time
GMT Standard Time
W. Europe Standard Time
Central Europe Standard Time
Romance Standard Time
Central European Standard Time
W. Central Africa Standard Time
Jordan Standard Time
GTB Standard Time
Middle East Standard Time
Egypt Standard Time
South Africa Standard Time
FLE Standard Time
Israel Standard Time
E. Europe Standard Time
Namibia Standard Time
Arabic Standard Time
Arab Standard Time
Russian Standard Time
E. Africa Standard Time
Georgian Standard Time
Iran Standard Time
Arabian Standard Time
Azerbaijan Standard Time
Caucasus Standard Time
Armenian Standard Time
Afghanistan Standard Time
Ekaterinburg Standard Time
West Asia Standard Time
India Standard Time
Sri Lanka Standard Time
Nepal Standard Time
N. Central Asia Standard Time
Central Asia Standard Time
Myanmar Standard Time
SE Asia Standard Time
North Asia Standard Time
China Standard Time
North Asia East Standard Time
Singapore Standard Time
W. Australia Standard Time
Taipei Standard Time
Tokyo Standard Time
Korea Standard Time
Yakutsk Standard Time
Cen. Australia Standard Time
AUS Central Standard Time
E. Australia Standard Time
AUS Eastern Standard Time
West Pacific Standard Time
Tasmania Standard Time
Vladivostok Standard Time
Central Pacific Standard Time
New Zealand Standard Time
Fiji Standard Time
Tonga Standard Time
Azores Standard Time
Cape Verde Standard Time
Mid-Atlantic Standard Time
E. South America Standard Time
SA Eastern Standard Time
Greenland Standard Time
Montevideo Standard Time
Newfoundland Standard Time
Atlantic Standard Time
SA Western Standard Time
Central Brazilian Standard Time
Pacific SA Standard Time
Venezuela Standard Time
SA Pacific Standard Time
Eastern Standard Time
US Eastern Standard Time
Central America Standard Time
Central Standard Time
Central Standard Time (Mexico)
Mexico Standard Time
Canada Central Standard Time
US Mountain Standard Time
Mountain Standard Time (Mexico)
Mexico Standard Time 2
Mountain Standard Time
Pacific Standard Time
Pacific Standard Time (Mexico)
Alaskan Standard Time
Hawaiian Standard Time
Samoa Standard Time
Dateline Standard Time


Sample code is as follows:

' Time Zone Class
' Author Strovek
' Date Mar 25, 2008
' Revised

Public Class tZone

Public Shared Function Version() As String
With _
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location)
Return .FileMajorPart & "." & .FileMinorPart & "." & .FileBuildPart & "." & .FilePrivatePart
End With
End Function

Public Sub listTimeZone()
Dim aList As System.Collections.ObjectModel.ReadOnlyCollection(Of TimeZoneInfo)
Dim tInfo As TimeZoneInfo

aList = System.TimeZoneInfo.GetSystemTimeZones()

For Each tInfo In aList
Console.WriteLine(tInfo.Id)
Next

End Sub

Public Sub displayTime()
Dim vTime As DateTime = Now
Dim vTime2 As DateTime

Try

vTime2 = convTime(vTime, "Singapore Standard Time", "Mountain Standard Time")

Console.WriteLine(String.Format("Time converted from {0} to {1}", vTime, vTime2))
Catch ex As Exception
Throw New Exception("[displayTime01]" & ex.Message)
End Try


End Sub

Public Function convTime(ByVal pTime As DateTime, ByVal pFromZone As String, ByVal pToZone As String) As DateTime
Dim vTime As DateTime
Try
vTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(pTime, pFromZone, pToZone)
Catch ex As Exception
Throw New Exception("[convTime01]" & ex.Message)
End Try
convTime = vTime
End Function

Public Shared Sub Main(ByVal args() As String)
Dim tZObj As New tZone
Console.WriteLine("Start TimeZone - Version " & tZone.Version())
Try
tZObj.listTimeZone()
tZObj.displayTime()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.WriteLine("End Program")
End Sub

End Class





Java TimeZone (JDK 1.6)


This is based on the information obtained from here.
The code:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package tzone;

/**
*
* @author strovek
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Program start");
tZone tzObj = new tZone();
tzObj.listZone();
tzObj.printDate();
System.out.println("Program End");

}

}

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package tzone;

import java.text.DateFormat;
import java.util.TimeZone;
import java.util.Locale;

/**
*
* @author strovek
*/
public class tZone {

public void printDate() {
java.util.Date cDate = new java.util.Date();
TimeZone tzFrom, tzTo, localZone;
Locale lZone = Locale.ENGLISH;
DateFormat dForm = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, lZone);

tzFrom = TimeZone.getTimeZone("Asia/Bangkok");
tzTo = TimeZone.getTimeZone("US/Mountain");

System.out.println("Current time " + cDate);
dForm.setTimeZone(tzFrom);
System.out.println("From Date - " + dForm.format(cDate));
dForm.setTimeZone(tzTo);
System.out.println("To Date - " + dForm.format(cDate));

}

public void listZone() {
String aList[];
int loopCnt;

aList = java.util.TimeZone.getAvailableIDs();
loopCnt = 0;
while (loopCnt < aList.length) {
System.out.println(aList[loopCnt]);
loopCnt++;
}
}
}

==========

Output:


Program start
Etc/GMT+12
Etc/GMT+11
MIT
Pacific/Apia
Pacific/Midway
Pacific/Niue
Pacific/Pago_Pago
Pacific/Samoa
US/Samoa
America/Adak
America/Atka
Etc/GMT+10
HST
Pacific/Fakaofo
Pacific/Honolulu
Pacific/Johnston
Pacific/Rarotonga
Pacific/Tahiti
SystemV/HST10
US/Aleutian
US/Hawaii
Pacific/Marquesas
AST
America/Anchorage
America/Juneau
America/Nome
America/Yakutat
Etc/GMT+9
Pacific/Gambier
SystemV/YST9
SystemV/YST9YDT
US/Alaska
America/Dawson
America/Ensenada
America/Los_Angeles
America/Tijuana
America/Vancouver
America/Whitehorse
Canada/Pacific
Canada/Yukon
Etc/GMT+8
Mexico/BajaNorte
PST
PST8PDT
Pacific/Pitcairn
SystemV/PST8
SystemV/PST8PDT
US/Pacific
US/Pacific-New
America/Boise
America/Cambridge_Bay
America/Chihuahua
America/Dawson_Creek
America/Denver
America/Edmonton
America/Hermosillo
America/Inuvik
America/Mazatlan
America/Phoenix
America/Shiprock
America/Yellowknife
Canada/Mountain
Etc/GMT+7
MST
MST7MDT
Mexico/BajaSur
Navajo
PNT
SystemV/MST7
SystemV/MST7MDT
US/Arizona
US/Mountain
America/Belize
America/Cancun
America/Chicago
America/Costa_Rica
America/El_Salvador
America/Guatemala
America/Indiana/Knox
America/Indiana/Tell_City
America/Knox_IN
America/Managua
America/Menominee
America/Merida
America/Mexico_City
America/Monterrey
America/North_Dakota/Center
America/North_Dakota/New_Salem
America/Rainy_River
America/Rankin_Inlet
America/Regina
America/Swift_Current
America/Tegucigalpa
America/Winnipeg
CST
CST6CDT
Canada/Central
Canada/East-Saskatchewan
Canada/Saskatchewan
Chile/EasterIsland
Etc/GMT+6
Mexico/General
Pacific/Easter
Pacific/Galapagos
SystemV/CST6
SystemV/CST6CDT
US/Central
US/Indiana-Starke
America/Atikokan
America/Bogota
America/Cayman
America/Coral_Harbour
America/Detroit
America/Eirunepe
America/Fort_Wayne
America/Grand_Turk
America/Guayaquil
America/Havana
America/Indiana/Indianapolis
America/Indiana/Marengo
America/Indiana/Vevay
America/Indiana/Winamac
America/Indianapolis
America/Iqaluit
America/Jamaica
America/Kentucky/Louisville
America/Kentucky/Monticello
America/Lima
America/Louisville
America/Montreal
America/Nassau
America/New_York
America/Nipigon
America/Panama
America/Pangnirtung
America/Port-au-Prince
America/Porto_Acre
America/Resolute
America/Rio_Branco
America/Thunder_Bay
America/Toronto
Brazil/Acre
Canada/Eastern
Cuba
EST
EST5EDT
Etc/GMT+5
IET
Jamaica
SystemV/EST5
SystemV/EST5EDT
US/East-Indiana
US/Eastern
US/Michigan
America/Anguilla
America/Antigua
America/Aruba
America/Asuncion
America/Barbados
America/Blanc-Sablon
America/Boa_Vista
America/Campo_Grande
America/Caracas
America/Cuiaba
America/Curacao
America/Dominica
America/Glace_Bay
America/Goose_Bay
America/Grenada
America/Guadeloupe
America/Guyana
America/Halifax
America/La_Paz
America/Manaus
America/Martinique
America/Moncton
America/Montserrat
America/Port_of_Spain
America/Porto_Velho
America/Puerto_Rico
America/Santiago
America/Santo_Domingo
America/St_Kitts
America/St_Lucia
America/St_Thomas
America/St_Vincent
America/Thule
America/Tortola
America/Virgin
Antarctica/Palmer
Atlantic/Bermuda
Atlantic/Stanley
Brazil/West
Canada/Atlantic
Chile/Continental
Etc/GMT+4
PRT
SystemV/AST4
SystemV/AST4ADT
America/St_Johns
CNT
Canada/Newfoundland
AGT
America/Araguaina
America/Argentina/Buenos_Aires
America/Argentina/Catamarca
America/Argentina/ComodRivadavia
America/Argentina/Cordoba
America/Argentina/Jujuy
America/Argentina/La_Rioja
America/Argentina/Mendoza
America/Argentina/Rio_Gallegos
America/Argentina/San_Juan
America/Argentina/Tucuman
America/Argentina/Ushuaia
America/Bahia
America/Belem
America/Buenos_Aires
America/Catamarca
America/Cayenne
America/Cordoba
America/Fortaleza
America/Godthab
America/Jujuy
America/Maceio
America/Mendoza
America/Miquelon
America/Montevideo
America/Paramaribo
America/Recife
America/Rosario
America/Sao_Paulo
Antarctica/Rothera
BET
Brazil/East
Etc/GMT+3
America/Noronha
Atlantic/South_Georgia
Brazil/DeNoronha
Etc/GMT+2
America/Scoresbysund
Atlantic/Azores
Atlantic/Cape_Verde
Etc/GMT+1
Africa/Abidjan
Africa/Accra
Africa/Bamako
Africa/Banjul
Africa/Bissau
Africa/Casablanca
Africa/Conakry
Africa/Dakar
Africa/El_Aaiun
Africa/Freetown
Africa/Lome
Africa/Monrovia
Africa/Nouakchott
Africa/Ouagadougou
Africa/Sao_Tome
Africa/Timbuktu
America/Danmarkshavn
Atlantic/Canary
Atlantic/Faeroe
Atlantic/Faroe
Atlantic/Madeira
Atlantic/Reykjavik
Atlantic/St_Helena
Eire
Etc/GMT
Etc/GMT+0
Etc/GMT-0
Etc/GMT0
Etc/Greenwich
Etc/UCT
Etc/UTC
Etc/Universal
Etc/Zulu
Europe/Belfast
Europe/Dublin
Europe/Guernsey
Europe/Isle_of_Man
Europe/Jersey
Europe/Lisbon
Europe/London
GB
GB-Eire
GMT
GMT0
Greenwich
Iceland
Portugal
UCT
UTC
Universal
WET
Zulu
Africa/Algiers
Africa/Bangui
Africa/Brazzaville
Africa/Ceuta
Africa/Douala
Africa/Kinshasa
Africa/Lagos
Africa/Libreville
Africa/Luanda
Africa/Malabo
Africa/Ndjamena
Africa/Niamey
Africa/Porto-Novo
Africa/Tunis
Africa/Windhoek
Arctic/Longyearbyen
Atlantic/Jan_Mayen
CET
ECT
Etc/GMT-1
Europe/Amsterdam
Europe/Andorra
Europe/Belgrade
Europe/Berlin
Europe/Bratislava
Europe/Brussels
Europe/Budapest
Europe/Copenhagen
Europe/Gibraltar
Europe/Ljubljana
Europe/Luxembourg
Europe/Madrid
Europe/Malta
Europe/Monaco
Europe/Oslo
Europe/Paris
Europe/Podgorica
Europe/Prague
Europe/Rome
Europe/San_Marino
Europe/Sarajevo
Europe/Skopje
Europe/Stockholm
Europe/Tirane
Europe/Vaduz
Europe/Vatican
Europe/Vienna
Europe/Warsaw
Europe/Zagreb
Europe/Zurich
MET
Poland
ART
Africa/Blantyre
Africa/Bujumbura
Africa/Cairo
Africa/Gaborone
Africa/Harare
Africa/Johannesburg
Africa/Kigali
Africa/Lubumbashi
Africa/Lusaka
Africa/Maputo
Africa/Maseru
Africa/Mbabane
Africa/Tripoli
Asia/Amman
Asia/Beirut
Asia/Damascus
Asia/Gaza
Asia/Istanbul
Asia/Jerusalem
Asia/Nicosia
Asia/Tel_Aviv
CAT
EET
Egypt
Etc/GMT-2
Europe/Athens
Europe/Bucharest
Europe/Chisinau
Europe/Helsinki
Europe/Istanbul
Europe/Kaliningrad
Europe/Kiev
Europe/Mariehamn
Europe/Minsk
Europe/Nicosia
Europe/Riga
Europe/Simferopol
Europe/Sofia
Europe/Tallinn
Europe/Tiraspol
Europe/Uzhgorod
Europe/Vilnius
Europe/Zaporozhye
Israel
Libya
Turkey
Africa/Addis_Ababa
Africa/Asmara
Africa/Asmera
Africa/Dar_es_Salaam
Africa/Djibouti
Africa/Kampala
Africa/Khartoum
Africa/Mogadishu
Africa/Nairobi
Antarctica/Syowa
Asia/Aden
Asia/Baghdad
Asia/Bahrain
Asia/Kuwait
Asia/Qatar
Asia/Riyadh
EAT
Etc/GMT-3
Europe/Moscow
Europe/Volgograd
Indian/Antananarivo
Indian/Comoro
Indian/Mayotte
W-SU
Asia/Riyadh87
Asia/Riyadh88
Asia/Riyadh89
Mideast/Riyadh87
Mideast/Riyadh88
Mideast/Riyadh89
Asia/Tehran
Iran
Asia/Baku
Asia/Dubai
Asia/Muscat
Asia/Tbilisi
Asia/Yerevan
Etc/GMT-4
Europe/Samara
Indian/Mahe
Indian/Mauritius
Indian/Reunion
NET
Asia/Kabul
Asia/Aqtau
Asia/Aqtobe
Asia/Ashgabat
Asia/Ashkhabad
Asia/Dushanbe
Asia/Karachi
Asia/Oral
Asia/Samarkand
Asia/Tashkent
Asia/Yekaterinburg
Etc/GMT-5
Indian/Kerguelen
Indian/Maldives
PLT
Asia/Calcutta
Asia/Colombo
IST
Asia/Katmandu
Antarctica/Mawson
Antarctica/Vostok
Asia/Almaty
Asia/Bishkek
Asia/Dacca
Asia/Dhaka
Asia/Novosibirsk
Asia/Omsk
Asia/Qyzylorda
Asia/Thimbu
Asia/Thimphu
BST
Etc/GMT-6
Indian/Chagos
Asia/Rangoon
Indian/Cocos
Antarctica/Davis
Asia/Bangkok
Asia/Hovd
Asia/Jakarta
Asia/Krasnoyarsk
Asia/Phnom_Penh
Asia/Pontianak
Asia/Saigon
Asia/Vientiane
Etc/GMT-7
Indian/Christmas
VST
Antarctica/Casey
Asia/Brunei
Asia/Chongqing
Asia/Chungking
Asia/Harbin
Asia/Hong_Kong
Asia/Irkutsk
Asia/Kashgar
Asia/Kuala_Lumpur
Asia/Kuching
Asia/Macao
Asia/Macau
Asia/Makassar
Asia/Manila
Asia/Shanghai
Asia/Singapore
Asia/Taipei
Asia/Ujung_Pandang
Asia/Ulaanbaatar
Asia/Ulan_Bator
Asia/Urumqi
Australia/Perth
Australia/West
CTT
Etc/GMT-8
Hongkong
PRC
Singapore
Australia/Eucla
Asia/Choibalsan
Asia/Dili
Asia/Jayapura
Asia/Pyongyang
Asia/Seoul
Asia/Tokyo
Asia/Yakutsk
Etc/GMT-9
JST
Japan
Pacific/Palau
ROK
ACT
Australia/Adelaide
Australia/Broken_Hill
Australia/Darwin
Australia/North
Australia/South
Australia/Yancowinna
AET
Antarctica/DumontDUrville
Asia/Sakhalin
Asia/Vladivostok
Australia/ACT
Australia/Brisbane
Australia/Canberra
Australia/Currie
Australia/Hobart
Australia/Lindeman
Australia/Melbourne
Australia/NSW
Australia/Queensland
Australia/Sydney
Australia/Tasmania
Australia/Victoria
Etc/GMT-10
Pacific/Guam
Pacific/Port_Moresby
Pacific/Saipan
Pacific/Truk
Pacific/Yap
Australia/LHI
Australia/Lord_Howe
Asia/Magadan
Etc/GMT-11
Pacific/Efate
Pacific/Guadalcanal
Pacific/Kosrae
Pacific/Noumea
Pacific/Ponape
SST
Pacific/Norfolk
Antarctica/McMurdo
Antarctica/South_Pole
Asia/Anadyr
Asia/Kamchatka
Etc/GMT-12
Kwajalein
NST
NZ
Pacific/Auckland
Pacific/Fiji
Pacific/Funafuti
Pacific/Kwajalein
Pacific/Majuro
Pacific/Nauru
Pacific/Tarawa
Pacific/Wake
Pacific/Wallis
NZ-CHAT
Pacific/Chatham
Etc/GMT-13
Pacific/Enderbury
Pacific/Tongatapu
Etc/GMT-14
Pacific/Kiritimati
America/Indiana/Petersburg
America/Indiana/Vincennes
Current time Thu Mar 27 05:58:58 SGT 2008
From Date - Thursday, March 27, 2008 4:58:58 AM ICT
To Date - Wednesday, March 26, 2008 3:58:58 PM MDT
Program End





Friday, March 21, 2008

Maintaining textbox input formatting

When we create an application with multiline textbox webcontrol, our users expects that the page break they enter will be maintained. This is a quick way of doing that. First the display:



In the above example, the box @ the top is the textbox and at the bottom is a label which is enclosed in a panel (with border for illustration).

The aspx is as follows:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="RichText.aspx.vb" Inherits="RichText" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>RichText Page testing</title>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox ID="txtInput" runat="server" Rows="7" TextMode="MultiLine"></asp:TextBox>
<br />
<br />
<asp:Button ID="btnSubmit" runat="server" Text="Submit" />
<br />
<hr />
<br />
<asp:Panel ID="Panel1" runat="server" BorderStyle="Inset" Width="500px">
<asp:Label ID="lblMsg" runat="server"></asp:Label>
</asp:Panel>
<br />
</form>
</body>
</html>


The code behind is as follows:



Partial Class RichText
Inherits System.Web.UI.Page

Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click
Dim tmpStr As String
tmpStr = Replace(txtInput.Text, vbNewLine, "
")

lblMsg.Text = Replace(tmpStr, " ", " ")

End Sub


End Class


As shown above, we just need to detect the newline and replace it with <br> which is a new line in html. The same goes for spaces.

Alternatives
However, if you need to include bold, underline etc. You can probably build your own control or purchase a commercial control. There also a GPL solution available that supports multiple platform - call FCKeditor. This appears more promising but requires some setup. There also other - just google "richtext html editor" and you will find a lot of other alternatives.

There is also an article I found that does some of this here.

Thursday, March 20, 2008

XSLT is very Powerful

XSLT is a very powerful programming language and can substantially reduce the amount of code needed to transform the data from one format to another.

For example, if you have the following raw data:



You could use XSLT to summarized the data as follows:



Or even summarized it as follows:



The XSLT used is shown below. Noticed that it is possible to create subroutines within XSLT but instead of calling it sub, it is known as templates. The two templates used to produce the above summary are "Horizontal" and "Vertical".

There is an extensive tutorial on XSLT and XML found at http://www.w3schools.com/xsl/. There is a very powerful free tool from Microsoft called XSLTMajic. This tool can verify your xml and also allow you to debug your xslt. Unfortunately, I can no longer find it. You could find other commercial software to do this but I have tried any of these.

<!--
Program: rpt.xsl
Author: Strovek
Date: Dec 05, 2004
Revised
Note
XSLT template for formating summary output
-->

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:param name="mode" />
<xsl:output method="html"/>
<xsl:key name="ProdKey" match="partMaster" use="PROD" />
<xsl:key name="StepKey" match="partMaster" use="CURRENTSTEP" />
<xsl:key name="ProdStepKey" match="partMaster" use="concat(PROD, ' ', CURRENTSTEP)" />

<xsl:template match="/">
<link rel="stylesheet" type="text/css" href="stylesheets/xsl.css" title="Style"/>
<table border="1">
<xsl:choose>
<xsl:when test="$mode='V'">
<xsl:call-template name="Vertical" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="Horizontal" />
</xsl:otherwise>
</xsl:choose>
</table>
</xsl:template>

<xsl:template name="Horizontal">
<tr class="head">
<th>Step</th>
<xsl:for-each select="//partMaster[count(.|key('ProdKey', PROD)[1])=1]">
<xsl:sort select="PROD" />
<th><xsl:value-of select="PRODUCT" /></th>
</xsl:for-each>
</tr>
<xsl:for-each select="//partMaster[count(.|key('StepKey', CURRENTSTEP)[1])=1]">
<xsl:sort select="CURRENTSTEPNUM" />
<tr class="row{position() mod 2}">
<xsl:variable name="stepName"><xsl:value-of select="CURRENTSTEP" /></xsl:variable>
<td><xsl:value-of select="$stepName" /></td>
<xsl:for-each select="//partMaster[count(.|key('ProdKey', PROD)[1])=1]">
<xsl:sort select="PROD" />
<xsl:variable name="prodName"><xsl:value-of select="PROD" /></xsl:variable>
<xsl:call-template name="sumPrdStep">
<xsl:with-param name="vProd"><xsl:value-of select="$prodName" /></xsl:with-param>
<xsl:with-param name="vStep"><xsl:value-of select="$stepName" /></xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>

<xsl:template name="Vertical">
<tr class="head">
<th>Step</th>
<xsl:for-each select="//partMaster[count(.|key('StepKey', CURRENTSTEP)[1])=1]">
<xsl:sort select="CURRENTSTEP" />
<th><xsl:value-of select="CURRENTSTEP" /></th>
</xsl:for-each>
</tr>
<xsl:for-each select="//partMaster[count(.|key('ProdKey', PROD)[1])=1]">
<xsl:sort select="PROD" />
<tr class="row{position() mod 2}">
<xsl:variable name="prodName"><xsl:value-of select="PROD" /></xsl:variable>
<td><xsl:value-of select="PRODUCT" /></td>
<xsl:for-each select="//partMaster[count(.|key('StepKey', CURRENTSTEP)[1])=1]">
<xsl:sort select="CURRENTSTEPNUM" />
<xsl:variable name="stepName"><xsl:value-of select="CURRENTSTEP" /></xsl:variable>
<xsl:call-template name="sumPrdStep">
<xsl:with-param name="vProd"><xsl:value-of select="$prodName" /></xsl:with-param>
<xsl:with-param name="vStep"><xsl:value-of select="$stepName" /></xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>


<xsl:template name="sumPrdStep">
<xsl:param name="vProd" />
<xsl:param name="vStep" />
<xsl:variable name="cStr"><xsl:value-of select="concat($vProd, ' ', $vStep)" /></xsl:variable>
<td>
<xsl:value-of select="sum(key('ProdStepKey', $cStr)/CURRENTQTY)" />
</td>
</xsl:template>


</xsl:stylesheet>


In ASP.Net, you can then use xml web control to perform the transformation easily:

The aspx is as follows:

<%@ Page Language="vb" autoeventwireup="false" codebehind="WipSum.aspx.vb" Inherits="PJ.WipSum" %>
<html>
<head>
<link href="stylesheets/ANOB.css" type="text/css" rel="stylesheet" />
</head>
<body>
<form runat="server">
<div class="BannerStyle"> Wip Tracking System - Wip Summary Display
</div>
<br />
<asp:Label id="lblMsg" runat="server"></asp:Label>
<br />
<asp:Label id="lblErrMsg" runat="server" forecolor="Red"></asp:Label>
<table>
<tbody>
<tr>
<td>
<asp:RadioButtonList id="SumOptList" runat="server" RepeatDirection="Horizontal" Width="250px">
<asp:ListItem Value="H" Selected="True">Horizontal</asp:ListItem>
<asp:ListItem Value="V">Vertical</asp:ListItem>
</asp:RadioButtonList>
</td>
</tr>
<tr>
<td>
<asp:Button id="btnSubmit" runat="server" Text="Submit"></asp:Button>
</td>
</tr>
</tbody>
</table>
</form>
<p>
<hr />
</p>
<p>
<asp:Label id="lblRslt" runat="server"></asp:Label>
</p>
<p>
<asp:Xml id="XmlOut" runat="server"></asp:Xml>
</p>
</body>
</html>

The code behind that does the transformation is as follows:


Private Sub ShowSum(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnSubmit.Click
Dim ds As New DataSet

Trace.Write("Button clicked")
lblErrMsg.Text = ""
Try
If IsNothing(Cache(cacheName)) Then
' Populate the dataset
Conn.Open()
Dim sql As String = "select prod, id product, currentstep, " _
& "currentstepnum, " _
& "sum(currentqty) currentqty from partmaster p, system_lookup s where " _
& "status = 'Active' and s.systemid = 'Prod' and id_type = prod group " _
& "by prod, id, currentstep, currentstepnum"
Dim dbCmd As OleDbCommand = Conn.CreateCommand
dbCmd.CommandText = sql
Dim dbAdapt As New OleDbDataAdapter(dbCmd)
dbAdapt.Fill(ds, "partMaster")
' Populate the dataset

' After getting the dataset, store in cache for 120 seconds.
Cache.Add(cacheName, ds, Nothing, DateTime.Now.AddSeconds(120), _
System.TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Normal, _
Nothing)
Else
' Read from Cache
ds = Cache.Get(cacheName)
End If

' Perform transformation
Trace.Write("Selected value is " & SumOptList.SelectedValue)
Dim xmlStr As String = ds.getXML
Dim xmlDoc As New XMLDocument
Dim xslArg As New System.Xml.Xsl.XsltArgumentList()
Dim uriStr As String = xmlDoc.BaseURI
Trace.Write("BaseUri is *" & uriStr & "*")
xslArg.AddParam("mode", uriStr, SumOptList.SelectedValue)
xmlDoc.LoadXML(xmlStr)
'XMLOut.Document = xmlDoc
XmlOut.DocumentContent = xmlStr
XMLOut.TransformSource = "xslt/rpt.xsl"
XMLOut.TransformArgumentList = xslArg
' Perform transformation

Catch ex As Exception
lblErrMsg.Text += "<br>Error " & ex.Message
Trace.Write("<br>" & ex.StackTrace)
Finally
If Conn.State = ConnectionState.Open Then
Conn.Close()
End If
End Try
End Sub



Wednesday, March 19, 2008

Create functions to join and split strings in SQL

There is an article on creating join and split string in SQL url: http://articles.techrepublic.com.com/5100-9592-5259821.html

The split transaction is as follows:

create or replace type split_tbl as table of varchar2(32767);
/
CREATE OR REPLACE FUNCTION split

/*
Simulate the split function in VB
Author Strovek
Date Jul 29, 2005
Revised Mar 05, 2007
Version 1.00.1

Modification History

*/

(p_list VARCHAR2, p_del VARCHAR2 := ',') RETURN split_tbl pipelined IS l_idx pls_integer;
l_list VARCHAR2(4000) := p_list;
BEGIN
LOOP
l_idx := instr(l_list, p_del);

IF l_idx > 0 THEN
pipe ROW(SUBSTR(l_list, 1, l_idx -1));
l_list := SUBSTR(l_list, l_idx + LENGTH(p_del));
ELSE
pipe ROW(l_list);
EXIT;
END IF;

END LOOP;

RETURN;
END split;
=============================

A sample on how to use the function is as follows:

select * from table(split('one,two,three'));



As a add on to that article, you may want to get the X row, this can be done using the following stored function:

=================================
To get the Xth row from the above:

CREATE OR REPLACE FUNCTION getsplitrow

/*
For obtaining the Xth row from the split transaction. Row starts from 0
Author Strovek
Date Feb 22, 2008
Revised
*/

(pstr IN VARCHAR2, prow IN NUMBER, pdelim IN VARCHAR2 := ',') RETURN VARCHAR2 AS

CURSOR cgetsplit IS
SELECT *
FROM TABLE(CAST(split(pstr, pdelim) AS
split_tbl));
vgetsplit cgetsplit % rowtype;
vcnt NUMBER;
vrtn VARCHAR2(40);
vval VARCHAR2(40);

BEGIN
vcnt := 0;
vrtn := '';

OPEN cgetsplit;
LOOP
FETCH cgetsplit
INTO vgetsplit;
EXIT
WHEN cgetsplit % NOTFOUND;

IF vcnt = prow THEN
vrtn := vgetsplit.column_value;
END IF;

vcnt := vcnt + 1;
END LOOP;

RETURN vrtn;
END getsplitrow;



Useful links to solve some oracle Issues

This post is here to make it easier for me to search some of the common problems I faced when programming with Oracle.

The following are some useful links to resolve some Oracle programming issues:

  1. Oracle OleDb Provider for .Net and Stored Procedures and Ref Cursors
  2. Oracle Not FoundThis same issue is also documented in Metalink article 215255.1 which basically affects Oracle 9i R2 and above installation to NTFS. It has to do with the rights to the Oracle folder.
Some useful sites for Oracle programming tips are:
  1. Oracle Tips of the Week from Akadia
  2. OraFAQ
  3. Burlenson Consulting Website
  4. Eight ways to Hack Oracle
  5. Oracle Java and stored procedures
  6. Accessing Oracle 9i stored procedures using ADO.Net
  7. Writing Timezone aware code in Oracle

Unable to find table in stored procedure in Oracle

Recently I had our DBA create an Oracle account for a new programmer. He was given select access to tables in another schema so is able to perform a select on those tables.

However, when we tried to create a stored function using one of those tables, we kept hitting an error that the table could not be found. Finally, after working with the DBA we found that we also needed "Select Any Table" privilege. It seems strange that we needed this privilege, probably Oracle is using some other table to identify the table when compiling.

Monday, March 17, 2008

Header documentation

I try to make it a practice to have at least the following information as the header of all my programs:

  1. Title
  2. Author
  3. Date Created
  4. Date Revised
  5. Requestor
  6. Objective of the code.
The following is how it is done in Java, C# and PL/SQL

/*
Class Program
Author: Strovek
Date: Mar 01, 2008
Revised: Mar 17, 2008
This is a sample code.
*/

In VB and ASP, it is done as follows:

' Class Program
' Author: Strovek
' Date: Mar 01, 2008
' Revised: Mar 17, 2008
' This is a sample code.


For dos scripts use


REM Author: Strovek
REM Date: Mar 01, 2008
REM Revised: Mar 17, 2008
REM This is a sample code.


Friday, March 14, 2008

Oracle SQL Queries - Oracle SQL Developer Vs Toad

Oracle SQL Developer is a good substitute (which provides a visual interface for SQL Query into Oracle database) to Toad for basic sql programming.

SQL Developer
Pro:
  • Free
  • When exporting result to an excel file, it includes column header and also adds a sheet showing the sql statement used. (Toad does not include the header nor does it include the sql statement).
  • No installation Needed - just download and unzip the folder then the program can be executed immediately.
  • Supports TNS or non TNS (using jdbc). Does not need Oracle Client to work.
  • Easy to create stored package, procedures and functions. Can use compile to verify the code. (same as Toad)
  • Can do show plan (as long as the user has right to create tables).
  • Short cut keys like F9 to execute, F4 to describe the table etc (same as Toad)
  • Supports MS Access???
  • You can import data into the tables straight from an Excel spreadsheet.

Con:
  • Cannot perform automatic sql tuning.
  • Does not work with Oracle version before 9i (may access but will show error).
  • Doesn't work very well to run full pl/sql scripts - (non stored procedure/function).

Toad
Pro:
  • Can provide automatic SQL Tuning (but only Xpert edition).
  • Easy to create stored package, procedures and functions. Can use compile to verify the code.
  • Can do show plan (as long as the user has right to create tables).
  • Expandable - can add modules - DBA, Xpert etc.
  • Works with all version of Oracle.
Con:
  • The save as option to save to Excel does not include the sql statement).
  • Supports only TNS (SQLNet needs to be set) i.e. requires Oracle client installed and SQLNet to be configured prior to use.
  • Can only import data from text file and need to go through Oracle Loader (meaning you need to configure a control file).
Note: There is also a Freeware version of Toad. However, it expires every 60 days and need to be reinstalled.

Other free SQL front end I have tried (but I did not find them as robust when I tested them a few years ago):
There are a lot of other front end interfaces which I haven't tried but can be found in SourceForge.

JDBC Navigator provides a quick and easy way to insert records into a JDBC enabled database from a csv file.

Saturday, March 8, 2008

Error Handling in Applications

With Java and Microsoft .Net applications, the try ... catch statement is very powerful. There are many articles that says that the process is very costly. However, I found the following to be very effective in simplifying my application (will use vb.net in the example but can come up with similar example in Java if requested):

Sub process1()

try
...
Process2()
...
catch (ex as exception)
throw new Exception("[process01]" & ex.message)
end try

End Sub

Sub Process2
try
...
catch (ex as Exception)
throw new Exception("[process02]" & ex.Message())
End Try
End Sub

...

Public Sub Main(ByVal args() As String)
try
...
process1()
catch (ex As Exception)
Console.Writeline(ex.Message())
End Try


In the example above, if an error happens in Process2, it will be thrown upwards until it reaches the main subroutine. So when you see the error message, you know that the error was encountered in Process2. You will then see the error as

[process1][Process2]...


I found this makes it easier to zoom into the errors in my application. This concept can be used in Console applications, Web Applications and Windows Application. Just need to make sure that the message is displayed somewhere.

Wednesday, March 5, 2008

Lucky Draw Application

This week our team was tasked to prepare the System to support our annual Dinner. One of the events expected was of course the lucky draw.

First thing, I did was to see how others have done it. An excellent example can be found ind DaniWeb. Which provided me as the basis for the interface to perform the top 5 prizes.

Since the annual dinner will be held off site, we will not have access to the database that contains the employee info. This is resolved by using xml to hold the information and then using DataTable for data manipulation. With this in mind, I created a class to perform the lucky draw using the random number generator as shown below:

' Common Library to be used by the Project
' Date Feb 29, 2008
' Revised

Imports System.Data.OleDb

Public Class clsDrawLib

Dim _dTab As New DataTable
Dim _dDraw As New DataTable
Dim conn As OleDbConnection
Dim _lastDraw As DataRow

Public Property dataTab()
Get
Return _dTab
End Get
Set(ByVal Value)
_dTab = Value
End Set
End Property

Public Property dataDraw()
Get
Return _dDraw
End Get
Set(ByVal Value)
_dDraw = Value
End Set
End Property

Public ReadOnly Property lastDraw()
Get
Return _lastDraw
End Get
End Property

Public Sub qryData(ByRef lbMsg As ListBox)
conn = New OleDbConnection(System.Configuration.ConfigurationSettings.AppSettings("devdb"))
popListMsg(lbMsg, Now & " - In dbclick")

Try
conn.Open()
popListMsg(lbMsg, Now & " - Connection successful")

Dim dbCmd As OleDbCommand = conn.CreateCommand
Dim sql As String = "select eno, name from empmast where eno like '16%'"
dbCmd.CommandText = sql
Dim dbAdapt As New OleDbDataAdapter
dbAdapt.SelectCommand = dbCmd
dbAdapt.Fill(_dTab)
addRsltCol()

popListMsg(lbMsg, Now & " - Data bound")

Catch ex As Exception
popListMsg(lbMsg, Now & " - Error: " & ex.Message)
Finally
If conn.State = ConnectionState.Open Then
conn.Close()
End If
End Try

End Sub

Private Sub addRsltCol()
Dim dRow As DataRow = _dTab.NewRow
Dim dCol, dCol2 As DataColumn

_dDraw.Columns.Add("PrizeNum", System.Type.GetType("System.Int32"))

For Each dCol In _dTab.Columns
_dDraw.Columns.Add(dCol.ColumnName, dCol.DataType)
Next

End Sub

Public Sub drawRslt(ByRef lbMsg As ListBox, ByVal pGiftNum As Integer)
Dim colCnt As Integer
Dim giftRow, drawnRow As DataRow
Dim dCol As DataColumn
Dim drawFlg As Boolean

If _dTab.Rows.Count = 0 Then
popListMsg(lbMsg, "Can't proceed - no data available")
Else
'giftNum = CInt(pGiftNum)
'lbmsg.Items.Add("Gift number is " & giftNum)
drawnRow = _dTab.NewRow

drawFlg = clsDraw.clsDraw.drawRec(_dTab, drawnRow)
giftRow = _dDraw.NewRow
giftRow.Item(0) = pGiftNum
colCnt = 0

Do While colCnt < _dTab.Columns.Count
giftRow.Item(colCnt + 1) = drawnRow.Item(colCnt)
colCnt += 1
Loop

_lastDraw = giftRow

_dDraw.Rows.Add(giftRow)
'dgRslt.DataSource = dDraw

End If

End Sub


Sub popListMsg(ByRef lbMsg As ListBox, ByVal msg As String)
If (lbMsg Is Nothing) = False Then
lbMsg.Items.Add(msg)
End If
End Sub

End Class


The above class can then be used to select the lucky record and can be used either for the table draw or the individual employee draw.

Additional things added for the interface of the lottery is as follows

The following sample queries from a database but in the actual windows application, it will obtain the data from an XML file.

I then obtain the DataRow of the lucky number from the clsDraw class shown above and then use multiple threads to simulate the drawing and then start populating the number one at a time until all the numbers of the employee number is displayed.

There is one thread which will in turn generate six threads (one for each number assuming the employee number is six digit. I keep track of the threads in an Arraylist and join to the thread so that it won't continue until the thread is complete.

The main thread will be in charge of going through each of the numbers and ensuring that the remaining digits continue to spin until all the numbers are displayed. The other threads are the ones that actually perform the number movement.

Private Sub btnDraw_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDraw.Click
Dim empName As String

If Microsoft.VisualBasic.IsNumeric(tbNum.Text) = True Then
lblName.Text = "drawn"
If CType(_drawObj.dataTab, DataTable).Rows.Count <= 0 Then
_drawObj.qryData(Nothing)
End If
_drawObj.drawRslt(Nothing, tbNum.Text)
'displayRnd()
drawnRow = _drawObj.lastDraw
eno = drawnRow.Item(1)
empName = drawnRow.Item(2)
lblName.Text += vbNewLine & eno
displayENO2(eno)
lblName.Text = empName

Else
lblName.Text = "No draw"
End If
End Sub

==================
Private Sub displayENO2(ByVal eno As String)
Dim tmpStr, tmpStr2, ctrlName As String
Dim i, loc, loc2 As Integer
Dim thrd As System.Threading.Thread

aList.Clear()


For i = 1 To 6
tmpStr = "txtN" & i
aList.Add(tmpStr)
Next

loc = 6 - eno.Length
tmpStr = eno

For i = 0 To eno.Length - 1
'rndDispList(aList)
thrd = New System.Threading.Thread(AddressOf rndDispList)
thrd.Start()
loc2 = loc - 1
tmpStr2 = Microsoft.VisualBasic.Right(tmpStr, 1)
tmpStr = Microsoft.VisualBasic.Left(tmpStr, tmpStr.Length - 1)
ctrlName = "txtN" & (6 - i)
thrd.Join()
setTxtBoxVal(ctrlName, tmpStr2)
aList.Remove(ctrlName)
Next

End Sub

Sub rndDispN1()
rndDisp("txtN1")
End Sub
Sub rndDispN2()
rndDisp("txtN2")
End Sub
Sub rndDispN3()
rndDisp("txtN3")
End Sub
Sub rndDispN4()
rndDisp("txtN4")
End Sub
Sub rndDispN5()
rndDisp("txtN5")
End Sub
Sub rndDispN6()
rndDisp("txtN6")
End Sub

Private Sub rndDispList()
Dim ctrlName As String
Dim thrd As System.Threading.Thread
Dim tList As New ArrayList

For Each ctrlName In aList
Select Case ctrlName
Case "txtN1"
thrd = New System.Threading.Thread(AddressOf rndDispN1)
thrd.Start()
tList.Add(thrd)
Case "txtN2"
thrd = New System.Threading.Thread(AddressOf rndDispN2)
thrd.Start()
tList.Add(thrd)
Case "txtN3"
thrd = New System.Threading.Thread(AddressOf rndDispN3)
thrd.Start()
tList.Add(thrd)
Case "txtN4"
thrd = New System.Threading.Thread(AddressOf rndDispN4)
thrd.Start()
tList.Add(thrd)
Case "txtN5"
thrd = New System.Threading.Thread(AddressOf rndDispN5)
thrd.Start()
tList.Add(thrd)
Case "txtN6"
thrd = New System.Threading.Thread(AddressOf rndDispN6)
thrd.Start()
tList.Add(thrd)
Case Else
End Select
Next

For Each thrd In tList
thrd.Join()
Next

End Sub