Tuesday, March 18, 2008

SQL Server Project (1)

Visual Studio 2005 provides a template for creating SQL Server Project, which you can create stored procedures, functions, triggers and more. When the project is deployed to a SQL server, all the SQL database objects defined in the project are created on the server. The project is a class library. Therefore, the assembly is also created on the SQL server database. All the database objects defined in the project are dependent on the assembly.

There are some articles on this issue on the web. The following are introduction ones good for beginners.



However, if you want to get into depth of real implementation for your business cases, you will encounter many issues and there are not any good resources available, at least I have not found out yet. I started to work on SQL server project from the idea to call .Net assembly classes from SQL server stored procedure. When I googled this topic. I found the above links. That's actually what I want to do. It looks very attractive. I jumped in the ocean and that has been quite good experience.

Any way, I found out many issues I have to resolve in order to create SQL database objects for real business cases. The links above give you very good start point and some very basic skills such as to create connection to SQL server, to get data, to execute SQL commands, to insert or to delete data, and to send result back to SQL server.

The cool thing of using .Net to create SQL database objects (stored procedure or SP, function and triggers) is that you can use the current context connection to a SQL server since the SP is running in the SQL server. Then you can execute almost any SQL commands. It is very fast. All of these can be done in .Net, for example, c#.

I tried to query data and to insert data with SQL database tables, and compared to the same simple SQL SP scripts as well. For a query of repeating 1000 times, the .Net SP is a little bit faster than the SQL scripts, 00:12:00 compared to 00:12:17. For the insert statement, the SQP scripts is faster than .Net SP, by a very little difference (just seconds for 1000 rows). However if you think in more perspective view, .Net objects are much more attractive than SQL scripts.


  • .Net SQL Sever Project is very similar to PL/SQL's package. You can deploy only the SPs you want to expose, for example, and to hide all the other classes or related methods data in the library. While SQL SP scripts are public only. If you write several SPs and they have dependent relationship, you cannot hide the dependent SPs in a SQL server database. I wrote a calculation SP. Then I separate many codes to other SPs and user defined functions. As a result, the calculation SP is dependent on several other SPs and functions. Those dependent SPs and Function are not supposed to be used for public directly, but I have no way to hide them. This makes the management and usage of SPs and functions very difficult.

  • .Net SQL Server Project separates source codes from SQL Server. What you have to do is to deploy your assembly and dependent assemblies to a SQL Server Database. All the assemblies, SPs and functions are in binary forms. This avoids other people to change them directly. All the source codes can be saved in a source code repository such as SVN.

  • Since .Net SQL Server Project is written in .Net managed codes such as C#, the performance of SPs could be improved. For example, in SQL scripts, there are very limited way to cache data. One way I found to share a group of data between SPs is to use temporary table. I read some articles on this technique. It is not recommended by many people. A temporary table is created in local hard disk. If you constantly access to it such as querying, deleting and saving to the table, your script's performance would be affected. However, in a .Net project, you can easily cache data in memory. For some small amount of data, which will be used constantly during the process, I can run one query to get the data and save them in cache as a class object. Then it can be used by other classes. You don't need to do query again or read from a temporary table. That's a great improvement in terms of performance since less query is needed.

  • By using .Net Server Project, some of existing libraries can be reused. Those reused resources are hidden in assembly form or referenced to, so you can improve or update those resources without changing the public interface of deployed SPs. However, those dependent resources or library assembly files if referenced have to be registered on the SQL server database. I'll continue to discuss on this issue in later articles.

  • The .Net Server Project should provide very simple interface for deploying SQL server database objects such as SPs, Functions and Triggers. Those codes should contain SQL server related classes and objects. All the other business logic, services, and resources should be in another assembly files. In this way, the same codes can be reused in other projects such as Windows applications, console applications, and generic libraries.



I have mentioned so many advantages about SQL Server Project. However, when you get your hands dirty or do it for a real business case, you may find out it's not so easy as you do in other .Net projects. There is a difference how these assemblies are used or executed in a SQL server database or just an application in Windows. There are many issues to be resolved as well before you can fully take the advantages of .Net. For example, you may find out that there are very little references available you can add to your project at the first time. How can you reuse your libraries, third party libraries, even many other Microsoft libraries? That's the next topic for this series.

Read More...

Thursday, February 07, 2008

Use Temporary Table to Pass Global Variables

One limitation of stored procedures (SP) in Microsoft SQL 2005 is that there is no global variables you can define. You can pass values by parameters; however, if you want to add additional information between SPs which are already in use, it will be difficult to make change since the signature of SPs are already defined. Oracle's package is much more convenient. All the global variables can be defined in a package and you can even define private SPs. Off course, .Net assemblies developed by managed codes as C# offer much more choices.

Any way, I find a way to pass values between SPs without changing parameters: using a temporary table. A temporary table can be created in a caller SP and then all the callee SPs can access to it. I have implement this strategy in a SQL application and it works very well.

The structure is that a temporary table, for example #calculationInfo_CachedForCalc, is created in the starting SP. The table contains only one row of data, global variables. Some values are updated in the caller SP (they can be updated in any other SPs). Then the callee SPs will get the value for use. One problem is that the callee SPs may be called or executed in other cases, since they are public accessible. I have created a utility function to check if the temporary table exists or not. Here is the function:

CREATE FUNCTION [dbo].[udf_IsCachedDataAvailable](
@p_cachedDataType int = 0)
RETURNS int
AS
BEGIN
DECLARE @v_ret int;
SET @v_ret = CASE @p_cachedDataType
WHEN 1 THEN
WHEN OBJECT_ID( 'tempdb..#calculationInfo_CachedForCalc') IS NULL
THEN 1
ELSE 0
END
-- ... Check for other objects by other possible parameter values
END
RETURN @v_ret;
END

In the start SP, or caller SP, here are some codes:
IF dbo.udf_IsCachedDataAvailable(1) = 1
BEGIN
CREATE TABLE #calculationInfo_CachedForCalc(
[id] [int] IDENTITY(1, 1) NOT NULL,
[calcName] [varchar](50) NULL,
[logFlags] [varchar](50) NULL,
[calcCumulativeType] [varchar](10) NULL)
INSERT INTO #calculationInfo_CachedForCalc
([logFlags] VALUES(@p_parameter); -- log flags passed in by SP parameter
END

-- FETCH LOOP read a row from calculation table
-- ....
UPDATE #calculationInfo_CachedForCalc SET
[calcName] = @v_calcNam, -- Update calculation name variable
[calcCumulativeType] = @v_calcCumulativeType; -- Update calculation type var

EXEC sp_CalcFormula;
-- ....

In other callee SPs, such as sp_CalcFormula(), the temporary table is accessed to get a global variable such as log flags:
IF dbo.udf_IsCachedDataAvailable(1) = 0
BEGIN
SET @v_flags = (SELECT [logFlags] FROM #calculationInfo_CachedForCalc);
-- ....
END

Read More...

Tuesday, February 05, 2008

GridView and AJAX PopupCalendar Issue

I tried to use ASP.NET AJAX CalendarExtender control in GridView's edit template for a date field in the way just as the example show on How Do I: Configure the ASP.NET AJAX Calendar Control? That is, I added a text box binding to a field in the gridview as date, a image button next to it as a button, and a CalendarExtender control which is AJAX control. The extender control's TargetControlID is the text box, and PopupButtonID is the image control.

However, it does not work. Actually, the pop-up calendar does pop up, but it disappears right away, and the whole page is refreshed. It looks like that the lick on image button caused a call to server to send a post-back call. The example works fine, but it is too simple(a text box, a image button and a CalendarExtender on an aspx page). I am not sure why this extender does not work when it is embedded in the gridview. I think I have to find a way to prevent the post-back call so that the pop-up calendar will stay.

Read More...

Friday, February 01, 2008

SQL's NULL Marker and Its Propagation

NULL is a SQL's marker to indicate missing information or unknown status. It is essential part of relational database and SQL. One of NULL feature is that it can propagate the result in many cases and they do make sense.

For example, 1 + NULL is NULL, or NULL in any mathematical expression will result NULL. That's OK. However, it further propagates to SQL internal functions, like LEN and CHARINDEX. It is very difficult to argue why not the result is NULL. I have to say it will depend on your case.

Recently, I have been working on a project with Microsoft SQL 2005. I did not realize that LEN and CHARINDEX functions return NULL if its parameters are NULL. That caused problem in my stored procedures. What I need these functions is to find out if a specified string exists in an expression. The expression is a value retrieved from db table. For my case, if the value is NULL, the result should be either LEN = 0 or CHARINDEX = 0.

As a result, I have to overwrite these internal functions as user defined functions such as udf_LEN() and udf_CHARINDEX. Then will handle NULL parameters and return the correct result. For example, I use the following CASE statement to check an input parameter:

DECLARE @v_ret int;
SET @v_ret = CASE
WHEN @p_string IS NULL THEN 0
ELSE LEN(@p_string);

When you use internal functions, you have to aware NULL's implication or propagation in your result.

Read More...

Saturday, January 05, 2008

Initialize Castle.AR and NHiberate

I tried to use Castle Project's ActiveRecord to map a .Net class to an Oracle DB in an ASP project. I followed the instruction to create an xml configuration file, to load the configuration and then to initialize ActiveRecord like this:

  XmlConfigurationSource source = new XmlConfigurationSource(
"C:\\temp\\ActiveRecords.xml");

ActiveRecordStarter.Initialize(source, typeof(Employee));

I got a failure exception when Initialize() method is called. I struggled for almost a whole day to find out why, and downloaded Castle's example project as well. I got the same error message (System.TypeInitializationException).

Finally, I found out in the detail information in debug mode that the inner exception was caused by "could not find log4net.dll file". When I checked the path for this file in the project references, I realized that it points to a wrong dll file in the MBUnit folder. After I removed and re-added the correct dll file, the problem was solved!

Read More...

Wednesday, December 05, 2007

PageFlow, WorkFlow classes and Designer

Widnows WorkFlow Foundation provides an in-process workflow engine for many classes with visual work flow process in design mode in VS, such as Sequential WorkFlow, Machine WorkFlow, Activities, PageFlow, and so on.

By using the visual designer, you can easily define work flow, process item relationships and constrains as a work flow class. In addition to WWF core WorkFlow classes, other third parties or organizations or open source groups (such as PnP) adds other useful WorkFlow classes. The APS.NET solution I have been working on includes a project with PageFlow class.

The PageFlow class is defined within Microsoft.Practices.PageFlow.WorkFlowFoundation.dll by Microsoft Patterns and Practices group. The problem I encountered is that after I created the project by using Guidance Package Manger Recipe of Add PageFlow Project, I got an error message when I tried to open PageFlow1.cs in design mode in VS, but I don't have problem to compile the project. It looks like that its related designer class is not available.

Actually, the designer class is Microsoft.Workflow.VSDesigner.dll. I have to search for this file first. Then I copy this file to the folder where ...WorkFlowFoundation.dll file sits. After making the designer class directly available to the owner assembly, I can see the class in design mode in VS after I re-open the VS again.

Read More...

Tuesday, December 04, 2007

Add Reference to Visual Studio 2005

I have installed .Net Framework 3.5 in my box (not 3.0) and WCSF for a ASP.NET project based on WCSF. When I tried to use Guidance Package Manger's Add Page Flow Project recipe I got an error and failed to add the project.

I wondered why it failed. It must use some kind template to add project. I then found the template project which has the following references but I cannot see them in my VS Add Reference Dialog window:

<Reference Include="System.Workflow.Activities" />
<Reference Include="System.Workflow.ComponentModel" />
<Reference Include="System.Workflow.Runtime" />

I think that is the reason I get the failure. My direct reaction was that I thought .Net 3.5 may not include or install 3.0 assembly files. That's wrong. From VS Add Reference list, I can see that System.WorkFlowServices.dll is located in C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\, and others which are not in VS's Add Reference window are in ...\v.30 folder.

It must be some settings such as environment or registry settings which make these assembly files visible in VS. I can find where those settings are. Anyway, cut the story in short, I found a msdn link about How to: Add ore Remove Reference in Visual Studio 2005. This web page does provide information on how to add customized assemblies in VS by setting windows registries. I followed the instruction I found that the NUnit and MbUnit assembly files I installed are set in the registry and they are in VS Add Reference window. Here are examples of NUnit 2.4.3 and MbUnit in the current user registry:
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\.NETFramework\AssemblyFoldersEx\NUnit 2.4.3]@="c:\Program Files\NUnit 2.4.3\bin\"
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx\MbUnit]@="c:\Program Files\MbUnit\"

Notice that NUnit is not related to any version while MbUnit is under .Net v2.0.50727. According to the msdn, if you add assembly location folders in HKEY_LOCAL_MACHINE in the same way, any user in the pc then can see these assemblies in VS.

Those are settings for third party to add their assemblies to VS 2005's Add Reference window. However, I still can not find any thing bout Microsoft assembly settings. I know where assembly files are, but I don't know what settings to cause them visible in the VS 2005's Add Reference window. Maybe Microsoft does not want people to mess up third party programs with Microsoft ones.

Anyway, I finally find a way to get them visible in VS 2005. I installed Visual Studio 2005 extensions for .NET Framework 3.0 (Windows Workflow Foundation). The installation did some magic settings and I can add Page Flow Project without any failure and I do see these v3.0 WowkFlow assembly files in VS 2005.

Read More...

Monday, December 03, 2007

ASP.NET Authentication Configuration

I was working on a Web application based on ASP.NET and WCSF. The application has a root virtual path with web.config and several sub-folders for other views. Each sub-folder also contains a web.config file.

Before I introduce authentication and Login to the application. It works fine. However, after I added security checking, I got compiling and running exception error. It complains about any authentication in web.config file. The following is an example error:

Line 107: <authentication mode="Forms">
Error It is an error to use a section registered
as allowDefinition='MachineToApplication' beyond application level.
This error can be caused by a virtual directory not
being configured as an application in IIS.
...EmployeeList\Web.config 37

After about half day investigating, I finally found that the error is caused by authentication defined in multiple web.config files. According to ASP.NET's recommendation, in a forum discussion, the authentication can only be defined in the root web.config for each APS.NET application or machine config file once.

That means I have to move all the authentication configurations to the root web.config file. In addition to that, in my application case, if a client logs out from the security from master page's LoginStatus control, it should be redirected to the main page or LoginPage.

After all these changes, my application works like charm!

Read More...

Friday, November 30, 2007

Create Templates for Visual Studio 2005

Create customized templates for Visual Studio 2003 and VS 2005 are very different. David Hayden has one blog on this issue with VS 2005.

It is very easy to create either a template project or template item in VS 2005. VS 2005 provides a wizard to do that from File|Export Templates... By the way, if you cannot find the Export menu item, you have to add it from Customize toolbar and drag the Export item under the File menu item.

However, I don't like to use the wizard to create or modify my template if I find some minor things to change or to create another one based on an existing one. I would like to work on copy-and-past pattern. The first thing I have to find out where my templates are and how they are organized.

For example, I created a template class item for NUnit test class. I want to create a similar one for MbUnit. I found out that after finished the wizard, my NUnit template is created under the following three locations (%userprofile% is "c:\Documents and Settings\[username]"):

%userprofile%\Application Data\Microsoft\VisualStudio\8.0\ItemTemplatesCache\NUnitTestClass.zip\
%userprofile%\My Documents\Visual Studio 2005\My Exported Templates\NUnitTestClass.zip
%userprofile%\My Documents\Visual Studio 2005\Templates\ItemTemplates\NUnitTestClass.zip

Actually, the template is saved at the third location. The first one is a cached template. If I want to change or create a new one, I have to go the last location to update or create one. After that, I copy and paste the new template to the second location.

It is much easier to update or create new template in this way.

By the way, I find one location in Google Code to host project files for downloading. I created my project site. I am going to put my source codes there. Here is list of my templates for NUnit and MbUnit test classes.

Read More...

Thursday, November 29, 2007

A Solution for Assembly Version Issue

Continuing with the issue I described in Rhino.Mocks and AutoMockingContainer, I found another solution to resolve the problem instead of recompiling AutoMockingContainer.

It is not always the case that you can get dependent library source codes. The alternative solution to redirect the .Net CLR to load a new version of the specified assembly. I'll take the my test project with Rhino.Mocks and AutoMockingContainer as an example. The Rhino.Mocks version I used in my project is version 3.3.0.906, while AutoMockingContainer was built with Rhino.Mocks version 2.8.1.2631 as its dependency. My test project will be compiled as TestClasses.dll. By default, all the binaries will be copied to Output directory with the latest Rhino.Mocks (3.3.0.906).

When my test class tries to call any AutoMockingContainer class which depends on Rhino.Mocks (2.8.1.2631), the .Net CLR cannot find the specified strong named assembly dll, and then exception was thrown.

I remembered that when .Net was released, it claimed to resolve COM-hell issue with versions and it allows two or more different versions of assembly running within a process. One way to resolve the version issue is to promote the version by using assembly's configuration file. For more information about .Net configuration, see Configuration File Schema for the .NET Framework.

That link provides a way to redirect old version to new version by using configuration's bindingRedirect element. Here is what I did in my test project:

  1. Add a configuration file to the project where AutoMockingContainer is referenced. The file name should be as same as the project name with congif extension. For example, I used "TestClasses.dll.config".

  2. Right click on this file and set Copy to Output Directory to Copy Always.

  3. Add the following line to the configuration file (make changes depending on your case):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Rhino.Mocks"
publicKeyToken="0b3305902db7183f"
culture="neutral" />
<bindingRedirect oldVersion="2.8.1.2631"
newVersion="3.3.0.906"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

  1. Finally compile the project. The project should run (with NUnit test program) without exception.


I used the tool of Lutz' .Net Reflector to find assembly's version. It's a great tool for developers.

Read More...

Wednesday, November 28, 2007

Update Assembly Version

Continuing from the previous blog, I found two places talking the related issue: how to manage assembly version:



The utility program's author, Matt, also posted two interesting tools: one for system tray icon with tooltips, and another PrefTimer timer class based on API.

Read More...

Rhino.Mocks and AutoMockingContainer

I am working on an ASP.Net project based on WCSF (Web Client Software Factory). For test, I downloaded the latest Rhino.Mocks, NUnit and MbUnit libraries. It works fine at the beginning because all the libraries are new.

For the tests, I need to mock some services and views. I remembered that I used AutoMockingContainer class and I had a generic class for mocking purpose. Then I copied the AutoMockingContainer to the project. After that, my mocking objects stop working and an exception was raised. Here is the error message:

[System.IO.FileLoadException] {"Could not load file
or assembly 'Rhino.Mocks, Version=3.2.0.781, Culture=neutral,
PublicKeyToken=0b3305902db7183f' or one of its dependencies. The located assembly's
manifest definition does not match the assembly reference. (Exception from HRESULT:
0x80131040)":"Rhino.Mocks, Version=3.2.0.781, Culture=neutral, PublicKeyToken=0b3305902db7183f"}
System.IO.FileLoadException


It was very confusing. I thought it might be my Visual Studio or Windows wrong. However, I restarted my Windows and VS again. Still I got the error message. Finally I found that it is the call to AutoMockingContainer class where exception is raised. When I read the error message. I realize that the version of Rhino.Mocks is difference from the same library which is used in the project (ver 3.3.0906).

The problem is that when AutoMockingContainer class is called, CLR is looking for Rhino.Mocks with ver 3.2.0.781. This not-matching caused the exception. It is very interesting. Why does CLR not automatically search for the class or method in a new ver?

Any way, fortunately, I found the source codes of AutoMockingContainer and rebuilt the binary with the same version of Rhino.Mocks. After that, everything works fine. I think that if a library used in a project which depends on old version of assembly, the old version ones have to be installed correctly. It is not as simple as copy library dll files. You have to run deployment or installation to install all the libraries, including dependencies. If both old and new one are used in the same project, the old ones have to be upgraded to new ones.

I think this is an issue of assembly version, and Microsoft has solve the problem. As developers, we have to follow the version management policy to manage version issue. I'll further investigate the issue and try this project with two different versions of Rhino.Mocks library files in one bin directory.

Read More...

Friday, November 23, 2007

Connect to Oracle Dadabase by OracleConnection

I installed Oracle 10g XE in my box, and then Oracle Developer Tools for .Net (ODTwithODAC1020221). After the installation, I started to work on .Net C# project against Oracle DB.

The first thing I have to do is to build a connection. At the start, I got exception when I tried to open an OracleConnection to userID=HR, Data Source=XE:

ORA-12154: TNS:could not resolve the connect identifier specified


Without connection, I cannot continue to do anything about the Oracle DB. After one day struggle, I finally found the reason from Oracle Developer Tools Forum: the home directory for Tools Kit is different from the Oracle db home directory! I have to follow the instruction to copy Oracle's TNSNAMES.ORA to the Tools' home directory. After that, the problem is solved!

Read More...

Thursday, November 22, 2007

Internet Security Issue

Internet is so convenient and useful. It opens a wide door to the world. For most people, it is an important part of their lifes. However, Internet security is a problem for most people who use Internet, especially who use it doing emails, banking and evening personal stuff in their local computer. Most people don't know about it and don't know how to protect their private information.

As a programmer, I realized it and I found it so easy to get some personal private information by writing a simple program, for example, login id and password. For window users, there are some window API functions which can be used to monitor process and keyboard. They are part of Windows' core and available for all windows platforms. A programmer can easily write a program by including those APIs to log keyboard and mouse activities without user's notice (in background).

For example, I wrote a program just for personal test purpose and as a demo to show to my friends. It is a console application. When you start it in a console, it starts to monitor keyboard and mouse activities and print out keys and mouse click on the console. I tried it to login many secured programs such log in and web sites. I can see their login ids and password!

The following is a snap-shot of a case: login to a bank's web site:



You can see that login id is "watchme", then tab to the password text box with "password".

You can image that this feature can be enhanced to work with some process monitors to monitor specified process and steal your personal information (I also verified that there are Windows APIs available to check current process and their titles such browser changes). Some virus or unknown programs can be easily installed in one's machine with those kind of spies.

How can you protect yourself? As a demo shown above, I always advice people don't type in your personal information in a regular habit: login id, tab or mouse click, and then password! You can make fool of these monitor programs. Don't type in your login and password continuously and correctly! You may purposely type in wrong ones or partial ones. Jump around between login and password, even browser tabs. Type something on the page. Make it hard for the spy programs to get your personal information! As well, change your login password regularly.

Some web sites provide more security options for users. For example, in above demo, there is an optional description for you to type in anything. After login the site, this site may ask you additional questions you have previously set if you login from a new location (which may be inconvenient for many users). All these efforts are made to protect you.

Read More...

Saturday, November 17, 2007

Another Update on My Blog Template: Show/Hide Posts

Vin posted another good tip on Show/Hide Posts.

The Show/Hide Posts Tip is actually much better than my previous template update. It uses a javascript and updates on a widget("id=Blog1"). I like this tip very much.

When I tried to update my template with this new feature yesterday, I could not do it. I thought it is Blogger server problem. I tried today. The respond was very quick. However, I got several error messages about #comments or #cdata not being allowed in <skin> or widget tags (I commented out existing codes and added some comments for the changes). Finally, I have no choice to remove all the comments. The update is successful.

I am very happy with the update. I think it is very easy for people to see all the posts in a outline. In addition to the tip, I added some changes to the template. For example, I added title attribute for Post Title. This provides a help information (mouse hover tooltip) because the Post Title is a link and when it is clicked, only this post is displayed with comments visible. If "[+/-]Show/Hide..." is expanded, comments are not visible. Because of this behavior (I don't want to spend time to figure it out why), another change I did is to add a link(See/leave comments for this post in another window/tab) after "[+/-]Show/Hide...".

Happy Blogging!

Read More...

Friday, November 16, 2007

Update My Post Template Formatting

Updated my template based on Vin's guide on Posting Summaries again.

I made a comments about Vin's guide on Enable Post Comments. This page is the testing based on the notes. What I did is to place the following codes in the post template at my Settings|Formatting:

Here is the beginning of my post. <span class="fullpost">And here is the rest of it.</span>


Then save it.

How this works...

When you post a new blog, the "Here is the beginning ..." part is the first paragraph in the blog, which will be displayed in the new post followed by Read more... link. If you click on the link, a new window or tab is displayed with the whole blog content. That looks pretty good!

Read More...

Thursday, November 15, 2007

Updated My Blog with Blogger's New Template

In the past two days, I tried to updated my blog with the new template from Blogger. The new one is much better and simpler than the previous one. The key improvement is widget elements which can be changed from Blogger's Template Tab (Page elements).

Of course, the change removed all my previous changes as well. I had to manually to add my previous changes in, such as RRS section on sidebar and scrollable text boxes. I also find out Dummies Guid to Google Blogger is much helpful!

Enjoy Blog!

Read More...

Monday, November 12, 2007

Single Instance of Application

Many cases require only one instance of a specified application running. This can be done by using Microsoft .Net Mutex class. However, by using Mutex, you can tell if the application is already running or not. If it is running, either active, inactive, or minimized, how can we bring the application to the front? I found that you have to use API functions to do that.

As a result, I have created a class ApplicaitonUtil. This class has two methods exposed: IsAlreadyRunning(name) and SwitchToCurrentInstance(). They are very straightforwd. Here is an example to use it:

[STAThread]
static void Main()
{
AssemblyInfo1 assemblyInfo = new AssemblyInfo1(
Assembly.GetExecutingAssembly());
FileSystemInfo fileinfo = new FileInfo(
Assembly.GetExecutingAssembly().Location);

string name = string.Format("{0} {1}",
fileinfo.Name,
assemblyInfo.Description.Description);

if (ApplicationUtil.IsAlreadyRunning(name))
{
ApplicationUtil.SwitchToCurrentInstance();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Form1 form = new Form1(); // Assume Form1 in your project
form.Text = name; // Set caption for the form
Application.Run(form);
}
}

Here I used assembly exe name and assembly information description (see my previous blog on AssemblyInfo and Related Utility Classes) as a name for the Mutex object. The name can be anything (no path is included! otherwise you would get an exception). The reason I used assembly's description information is that the same exe file might be deployed to local version and QA version. You may need each one as single instance but you can run two at same time.

By the way, I updated my AssemblyInfo class so that it has additional constructor with specified assembly object. If you place this class in another class or exe as library, you need to pass assembly object as shown in above codes.

Here is the my ApplicationUtil class:
public class ApplicationUtil
{
# region Windows APIs
[DllImport("user32.dll")]
private static extern int IsIconic(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd,
int nCmdShow);

[DllImport("user32.dll")]
private static extern int SetForegroundWindow(
IntPtr hWnd);
# endregion

# region Constructor
private ApplicationUtil()
{
}
# endregion

# region Data members
// Define const used for restoring the window
// already opened
private const int SW_RESTORE = 9;
private static Mutex _mutex;
# endregion

# region Public methods
public static bool IsAlreadyRunning(string name)
{
bool mutexCreated;

_mutex = new Mutex(true, name, out mutexCreated);
if (mutexCreated)
_mutex.ReleaseMutex();

return !mutexCreated;
}

public static void SwitchToCurrentInstance()
{
IntPtr handle = GetCurrentInstanceWindowHandle();
if (handle != IntPtr.Zero)
{
// Restore window first if minimized.
// Do not restore if already in
// normal or maximized window state,
// since we don't want to
// change the current state of the window.
if (IsIconic(handle) != 0)
{
ShowWindow(handle, SW_RESTORE);
}

SetWindowToForeground(handle);
}

return;
}

public static void SetWindowToForeground(
IntPtr hWnd)
{
SetForegroundWindow(hWnd);
}
# endregion

# region Private methods
private static IntPtr
GetCurrentInstanceWindowHandle()
{
IntPtr handle = IntPtr.Zero;
Process currentProcess =
Process.GetCurrentProcess();
Process[] processes =
Process.GetProcessesByName(
currentProcess.ProcessName);

foreach (Process process in processes)
{
// Get the first instance that is not this
// instance, has the same process name and
// was started from the same file name
// and location. Also check that the process
// has a valid window handle in this session
// to filter out other user's processes.
if (process.Id != currentProcess.Id &&
process.MainModule.FileName ==
currentProcess.MainModule.FileName &&
process.MainWindowHandle != IntPtr.Zero)
{
handle = process.MainWindowHandle;
break;
}
}

return handle;
}

# endregion
}

This class has another method SetForegroundWindow(), which is used to set a form as foreground. For example, you have a lengthy splash window running, and this form might be set to background if user is working on something else. In this case, when the splash window finishes its job, you can call this method to set the main form to the front so that user is notified the application being ready.

Read More...

Sunday, November 11, 2007

AssemblyInfo and Related Utility Classes

I worked on one project where I need to get an assembly's information defined in the project's AssemblyInfo.cs. There are many ways to get these information. One way is by calling Assemlby class' method GetCustomAttributes() like this:

Assembly _assembly = Assembly.GetExecutingAssembly();
objects[] _assemblyAttributes = _assembly.GetCustomAttributes(true);

I tried to print out the array of retrieved attributes. There are 15 items. I thought the order of these attributes are pre-defined. Then I wrote a class to get some of assembly information out by the index.

However, I run into a problem. When I changed some attributes, for example AssemblyCulture(), my class does not work any more. I finally found out that the order of attributes in the object array are changed! Before I realized this, I tried other ways to get AssebmlyIno. Here is another class by using FileVerionInfo class:
using System;
using System.Reflection;
using System.Diagnostics;
using System.Globalization;
//...
class AssemblyInfo1
{
private Assembly _assembly;
private FileVersionInfo _fileInfo;

public AssemblyInfo1()
{
_assembly = Assembly.GetExecutingAssembly();
_fileInfo = FileVersionInfo.GetVersionInfo(_assembly.Location);

}

public string Title
{
get
{
return _fileInfo.Comments;
}
}

public string Description
{
get
{
return _fileInfo.Comments;
}
}

public string Company
{
get
{
return _fileInfo.CompanyName;
}
}

public string Product
{
get
{
return _fileInfo.ProductName;
}
}

public string CopyRight
{
get
{
return _fileInfo.LegalCopyright;
}
}

public string Trademark
{
get
{
return _fileInfo.LegalTrademarks;
}
}

public string FileVersion
{
get
{
return _fileInfo.FileVersion;
}
}

public string InformationVersion
{
get
{
return _fileInfo.ProductVersion;
}
}

public Version Version
{
get
{
return _assembly.GetName().Version;
}
}

public CultureInfo Culture
{
get
{
return _assembly.GetName().CultureInfo;
}
}
}

The only problem is that I could not find a way to get AssemblyConfiguration() information. After I realized the order change in the object array retrieved from GetCustomAttributes(), I changed the class with string comparing to get assembly's information. My second class can get AssemblyConfiguration() information.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Globalization;
using System.Diagnostics;
// ...
public class AssemblyInfo2
{
private const string TITLE = "System.Reflection.AssemblyTitleAttribute";
private const string DESCRIPTION = "System.Reflection.AssemblyDescriptionAttribute";
private const string CONFIGURATION = "System.Reflection.AssemblyConfigurationAttribute";
private const string COMPANY = "System.Reflection.AssemblyCompanyAttribute";
private const string PRODUCT = "System.Reflection.AssemblyProductAttribute";
private const string COPYRIGHT = "System.Reflection.AssemblyCopyrightAttribute";
private const string TRADEMARK = "System.Reflection.AssemblyTrademarkAttribute";
private const string FILE_VERSION = "System.Reflection.AssemblyFileVersionAttribute";
private const string INFORMATION_VERSION = "System.Reflection.AssemblyInformationalVersionAttribute";

private Assembly _assembly;
private object[] _assemblyAttributes;

public AssemblyInfo2()
{
_assembly = Assembly.GetExecutingAssembly();
_assemblyAttributes = _assembly.GetCustomAttributes(true);
}

private T GetAttribute<T>(string typeString)
where T : class
{
List<object> objects = new List<object>(_assemblyAttributes);
object assemblyObj = objects.Find(
delegate(object item)
{
bool found = false;
if (item != null)
{
found = item.ToString().Equals(typeString);
}
return found;
});

return assemblyObj as T;
}

public AssemblyTitleAttribute Title
{
get
{
return GetAttribute<AssemblyTitleAttribute>(TITLE);
}
}

public AssemblyDescriptionAttribute Description
{
get
{
return GetAttribute<AssemblyDescriptionAttribute>(DESCRIPTION);
}
}

public AssemblyConfigurationAttribute Configuration
{
get
{
return GetAttribute<AssemblyConfigurationAttribute>(CONFIGURATION);
}
}

public AssemblyCompanyAttribute Company
{
get
{
return GetAttribute<AssemblyCompanyAttribute>(COMPANY);
}
}

public AssemblyProductAttribute Product
{
get
{
return GetAttribute<AssemblyProductAttribute>(PRODUCT);
}
}

public AssemblyCopyrightAttribute CopyRight
{
get
{
return GetAttribute<AssemblyCopyrightAttribute>(COPYRIGHT);
}
}

public AssemblyTrademarkAttribute Trademark
{
get
{
return GetAttribute<AssemblyTrademarkAttribute>(TRADEMARK);
}
}

public AssemblyFileVersionAttribute FileVersion
{
get
{
return GetAttribute<AssemblyFileVersionAttribute>(FILE_VERSION);
}
}

public AssemblyInformationalVersionAttribute InformationVersion
{
get
{
return GetAttribute<AssemblyInformationalVersionAttribute>(INFORMATION_VERSION);
}
}

public Version Version
{
get
{
return _assembly.GetName().Version;
}
}

public CultureInfo Culture
{
get
{
return _assembly.GetName().CultureInfo;
}
}
}

By the way, the Culture property is based on AssemblyCulture("") setting in AssebmlyInfo.cs. You just cannot set this information other than empty string "". I tried to change it to "en" or "en-US". The project just cannot be compiled. Actually, this is the property I tried to change and I tried to change project's property from "assembly information..." in Application tab, and then the order of attributes retrieved from GetCustomeAttributes() is changed. It's very interesting issue. I am not sure how the assembly attribute information are populated in the assembly.

Read More...

Friday, November 09, 2007

Blogger Template Issue

Today I tried to understand Blogger's template and then to reorganize my template for my blog. As I understand that the template is a html-like file. I can update this file so that I can add other elements such as RRS link I did previous.

Based on xml and html knowledge I have, I tried to change me template. Here is an example tags for blog title before my change:

<BlogItemUrl><a href="<$BlogItemUrl$>"
  title="external link"></BlogItemUrl>
  <$BlogItemTitle$>
<BlogItemUrl></a></BlogItemUrl>


These tags are not looks in right logic. Then I tried to change them as:

<BlogItemUrl><a href="<$BlogItemUrl$>"
  title="external link">
  <$BlogItemTitle$>
  </a>
</BlogItemUrl>


I thought the updated one is better. However, after I saved the new template, I lost my blog title! I had to change these codes back. Fortunately, I saved my template before I did the change. As Blogger suggested, Always back up your template before you make any changes!

By the way, this blog demonstrates an example of using a box for codes as seeing above. I introduced a css class "outlinebox" in my Blogger template:

/* Use this style as class ID for a box for some blocks such as div, p */
.outlinebox {
  width: 450px;
  border-color: #666666;
  border-style: solid;
  border-width: 1px;        /* sets border width on all sides */
  padding: 5px
  }

Then in my post, I used this class in a div like this:
<div class="outlinebox"><pre><code>
  some codes ...
</code></pre></div>

I also created another two css classes in my Blogger template: "outlinebox4wrappercodes" and "wrappercodes". These two css classes can be used in the same way to display additional horizontal scroll bar. Here is another example:
<div class="outlinebox4wrappercodes"><div class="wrappercodes"><pre><code>/* Use this style as class ID for a box for some blocks such as div, p */
.outlinebox {
  width: 450px;
  border-color: #666666;
  border-style: solid;
  border-width: 1px;        /* sets border width on all sides */
  padding: 5px
  }</code></pre></div></div>

Read More...