Breaking News

Thursday, December 31, 2009

Manual comma separation versus the cool CommaDelimitedStringCollection class!

Some time back while looking around for something else, I was stumbled upon this class, and I simply enjoy using it!

A "wow"-class; I would say. Though, not a lot of documentation is available, but it is worth using this class for those who frequently manage in-memory data with lists/arrays/etc.

A not-so-cool code, somewhere in the asp.net page, goes as follows:
ListBox ctl = ((ListBox)someControlHolder.FindControl("objControl" + i.ToString()));
for (int j = 0; j < ctl.Items.Count; j++)
{
   if (ctl.Items[j].Selected)
   str +=ctl.Items[j].Text + ",";//make it comma separated.
}
str.Remove(str.ToString().Length - 1, 1); //remove the last extra comma.

or And for oCommaDelimitedStringCollection reason, a cool/neat/clean is as follows:
ListBox ctl = ((ListBox)someControlHolder.FindControl("objControl" + i.ToString()));
CommaDelimitedStringCollection strCsv = new CommaDelimitedStringCollection();
for (int j = 0; j < ctl.Items.Count; j++)
{
  if (ctl.Items[j].Selected)
  {
   strCsv.Add(ctl.Items[j].Text);
  }
}

And yes, you are right, doing strCsv.ToString() will write the comma separated string (0:

Enjoy...!
Read more ...

Wednesday, December 30, 2009

Javascript: How to create ASP.NET Reset Button?

Wondering, how to create an ASP.NET based reset button; which is not an Html Reset(input type=reset)?

Type-in the following Javascript code into any ASP.NET control(Button, ImageButton, Link, etc) that exposes OnClientClick public property:

this.form.reset(); return false;

But why would I need this when there already is an Html based reset button available?

Sometimes you need this when you have a theme/skin applied over the page(that is, controls/buttons/textboxes/etc), and you want a button to be consistent with the site theme.

Note that you can append just about any Javascript code/function to objects and controls which then call straight up that Javascript function.
Read more ...

Thursday, December 24, 2009

ASP.NET - Crystal Reports: PrintControl.Cab ActiveX problem

Crystal Reports: PrintControl.Cab ActiveX problem

Problem:
I have a crystal report (some version) with asp.net (usually v2.0), I am using the ActiveX to print the report; I get an error with following symptoms.

-Upon print button click it shows the pop-up window and stop without continue the printing.
-It shows the small print window and vanishes away - nothing happens!
-Print works on some client machines and does not work on some machines.

Workaround:
Specify the version number of the print control in the web
site's web.config file; so, the first time it would download and rest of times it would reuse the print control.

In your web.config file, add the following section with the given url.










The print control is ActiveX and it requires to be downloaded and installed on the client browser. On the systems that still do not print, requires a check if they have enough privileges to install the ActiveX control? Or that they have any printer installed. If they don't then an error is always expected.

Please see this resource shared by Business Objects for more detailed reference.

And also, you may download the PrintControl.CAB file explicitly from this location.

Update: Adding/Configuring a Crystal Report Viewer in web.config.

Update 2011-MAY-11:
You can also try adding the link to "Trusted Sites"; the link from where you are pulling the reports.

Internet Explorer -> Tools ->Internet Options ->Security Tab ->Trusted Sites Security Zone -> Click Sites

and add the url.
Read more ...

Thursday, December 17, 2009

Pomodoro Technique

I reset the clock once again, and start to focus on an activity out of my todo list, then I feel hunger, then I realize I need to make an important call, and I also was just thinking that I want to check my favorite online forum, and then I must read my email and also reply "immediately"... and oh! it has been years I updated by blog - today is the day; and that’s not to mention the most recurring of all my instincts — refilling my tea cup whenever its empty... while 'shedding some light' about the daily-rumours!

Is it? Isn't it? So true... in this overwhelming ever expanding information-village.

So how could we manage time? So challenging!
Read more ...

Monday, December 14, 2009

LDAP: How to get the list of UserIDs from active directory

LDAP: How to get the sorted list all userids that exist on active directory. See following snip:

/// 
    /// List all UserIDs(not user names) that exist on active directory
    /// 
    /// 
    public static List GetAllDomainUsersEx()
    {
        string strLdap = Common.Utility.Constants.LDAP_URL;
        DirectoryEntry objOU = new DirectoryEntry(strLdap);

        //Properties that 
        string[] loadProps = new string[] { "cn"/*Canonical name*/, 
                                            "samaccountname"/*UserID*/, 
                                            "name"/*User Name*/, 
                                            "distinguishedname" /*LDAP unique path to the user*/
                                          };

        //Create a directory searcher object
        DirectorySearcher objUserSearcher = new DirectorySearcher(objOU, "(&(objectClass=user)(objCategory=person))", loadProps);
        SearchResultCollection objResults;

        //Filter criteria.
        objUserSearcher.Filter = "(objectClass=user)";

        //Find all records
        objResults = objUserSearcher.FindAll();

        List lstUsers = new List();
        
        //Loop through
        foreach (SearchResult objResult in objResults)
        {
            string str = objResult.Properties["samaccountname"][0].ToString();
            if(!str.Contains("$")) //remove system users, thats is, users with dollar sign
            {
                lstUsers.Add(str.ToLower());
            }
        }

        return lstUsers.Sort();
    }


Usage:
Following is how you would use; or bind it with a drop down list box.
private void ReloadDomainUserList()
{
List lstUsers = new List();
lstUsers = CLDAPManager.GetAllDomainUsersEx();

ddlADUsers.DataSource = lstUsers;
ddlADUsers.DataBind();
}
Read more ...

Wednesday, December 9, 2009

Microsoft Data Access Application Block

Microsoft Enterprise Library: Microsoft Application Blocks (Data Access)

I have been using Microsoft’s Data Access Application Block for windows forms; and this time I plan to use ‘em in my ASP.NET project. I tend to like application block framework, mostly because for me it saves a lot of time and it provides a bug free code.

I mean seldom you feel a need to write your own data layer unless otherwise is explicitly required/demanded. So I get more time to think about my applications’ logic.

Just for those who have no idea what a data access application block is, following would reveal the before-and-after scenarios; and how it can push you into the “flow” that we seek during our development phase.

1. Code - Without application block
Following the code that create, and opens a connection, make a query, load the data into result and then binds it to the grid.

// Open connection
SqlConnection theConnection = new SqlConnection("Server=(local);Database=TestDB;Integrated Security=True;");
theConnection.Open();

//Create stored procedure command object
SqlCommand theCommand = new SqlCommand("GetEmployeesByDepartment", theConnection);
theCommand.CommandType = CommandType.StoredProcedure;

//Add parameter for stored procedure
theCommand.Parameters.Add("@DepartmentID", SqlDbType.Int); 
theCommand.Parameters["@DepartmentID"].Value = 9; 

//create DataAdapter and DataSet objects
SqlDataAdapter theAdapter = new SqlDataAdapter(theCommand);
DataSet dsResult = new DataSet("Result");

//fill dataset
theAdapter.Fill(dsResult);

//databind the grid
DataGrid1.DataSource = dsResult;
DataGrid1.DataBind();

//clean up
theConnection.Close();

2. Code - With application block
Following does the same as above but with lesser code.

//Make connection
string theConnection = "Server=(local);Database=TestDB;Integrated Security=True;";

//Execute
DataSet dsResult = SqlHelper.ExecuteDataset(theConnection, CommandType.StoredProcedure,"GetEmployeesByDepartment", new SqlParameter("@DepartmentID", 9) );

//Assign and bind
dgvResult.DataSource = dsResult;

dgvResult.DataBind();

Data Access Application Block
The Data Access Application Block simplifies many common data access tasks such as reading data for display, passing data through application layers, and submitting changed data back to the database system. It includes support for both stored procedures and in-line SQL, and provides access to the most often used features of ADO.NET in simple-to-use classes.

Key Scenarios
The Data Access Application Block is suitable if you encounter any of the following
situations:
  • Using a DataReader or DataSet to retrieve multiple rows of data.
  • Executing a command and retrieve the output parameters or a single-value item.
  • Performing multiple operations within a transaction.
  • Retrieving XML data from a SQL Server.
  • Updating a database with data contained in a DataSet object.
  • Adding or extend implementations of database providers.
When to Use
The Data Access Application Block is ideal for addressing the following requirements:
  • You need simplicity and convenience while helping developers use the functionality provided by ADO.NET with best practices.
  • You need to reduce the requirement for boilerplate code to perform standard data access tasks.
  • You need to maintain consistent data access practices, both within an application and across the enterprise.
  • You need to make it easy to change the target database type through configuration, and reduce the amount of code that developers must write when they port applications to different types of databases.
  • You need to relieve developers from learning different programming models for different types of databases.
Considerations
The following considerations apply to using the Data Access Application Block:
  • The Data Access Application Block is a complement to ADO.NET; it is not a replacement.
  • If your application must retrieve data in a specialized way, or take advantage of features specific to a particular database, consider using ADO.NET directly.

Download; you may download the msi from here.

Reference to assembly; add reference to the Microsoft.ApplicationBlocks.Data.dll assembly and start using Microsoft.ApplicationBlocks.Data.

MSDN; Also go through the Data Access Application Block documentation at Microsofts website.

Btw, v5.0 is still(as of now); under construction.

Microsoft Application Architecture Guide - Second Edition 2009 (p489)
Read more ...

Monday, November 23, 2009

MS CRM: Enumerate or loop through Picklist

Following is one way of enumerating the Picklist items to find match on Picklist.Name public property.

The picklist values are in the retrievedAttributeMetadata.Options collection. To do this we will need to get the attribute metadata from the meta dataservice. This is a four step process.


STEP 1: Authenticate
STEP 2: Connect to CRM webservice
STEP 3: Retrieve
STEP 4: Iterate
Read more ...

Saturday, November 7, 2009

Out of my diary: Dil hi to hai

Some time ago... while life was trying to teach some lessons, I came up with following random 'lessons-learned' rants:

..:: Dil hi to hai ::..

koi na raha, sab hi gaye lekin ye dil to hai
vo raatein vo shamein na rahin... lekin ye dil to hai

rah-e-safer mein hamsafer jo banay.. koi ruk gaya.. koi roag gaye
vo hamsafer vo saathi na rahay... lekin ye dil to hai
Read more ...

Thursday, October 29, 2009

Unlucky nations: only "struggle" creates heroes not luck

"nations that wait for heroes to save them are the unluckiest – only struggle creates heroes not luck because luck is always with those who don’t include it in their plans, read history if you don’t believe it" - Abdul Basit
Read more ...

Wednesday, October 28, 2009

Crystal reports error: Maximum report processing jobs limit!

The maximum report processing jobs limit configured by your system administrator has been reached:

Fortunately a post threw me to a reference provided by Ludek Uher and Trevor Dubinsky; senior engineers with Technical Customer Assurance, SAP Business Objects. Though this seems to be an old problem hanging around Crystal Reports since version 10, I noticed that this was just acknowledge and workaround-ed couple of months ago(17th June, 2009) from now.

Click here to see the work around PDF; here is Microsoft Word based older version.
Read more ...

Friday, October 9, 2009

Process: The un/avoidable!

Why is it that in some organizations, staff do not follow(or should I say "avoid"?) the process/es defined and implemented by the management?

Well, think about it! I believe following three the major factors.
  1. Generally, a process is avoided because of its complexity. The more complex the process, the chances are that the process shall be avoided. For example, our Passport office; I would give my passport fee + services fee to a chacha sitting right in front of the passport office, but won't try to step into the passport office gate. This is a common, experience'able example.
Read more ...

Tuesday, October 6, 2009

[Part 1/2] ASP.NET: Dynamic Controls

Quickest way to generate a control dynamically is four step process:

STEP 1: Add
STEP 2: Create
STEP 3: Draw/redraw
STEP 4: Read
Read more ...

[Part 2/2] ASP.NET: Dynamic User Controls

How to dynamically generate controls including UserControls? Same as adding windows controls:

STEP 1: Add
STEP 2: Draw/Redraw
STEP 3: Read

Let’s say we have DataTable having following data in it:
ID           CAPTION             DATATYPE
1             Name                   Text
2             Sports                   List
3             Date                     Date

Based upon above DATATYPE field, we need to generate controls; and in case of Date, we have a user control (WebUserControl) called UCtlCalendar control that we need to generate and keep track of.
Read more ...

Friday, October 2, 2009

IE8 is whacked!

IE8 is whacked, they say!!!

And I just wonder if the “Microsoft-Programmers” are just another human-beings like us, delivering buggy releases but on-time? And then providing fixes/patches for the latest release? Well, that’s what I recently was hit upon…

For some reason while browsing a message was popped up asking if I would want to install the “latest-fastest-greatest” Microsoft Internet Explorer 8? Well, I thought for a while and then I clicked on the OK button.

It took around 10 minutes for the application to down and 10 more minutes to get the installation complete; but that not it, because it also requires you to restart your pc.
Read more ...

Saturday, September 19, 2009

CLR metadata myth in a managed module?

This answers questions:
  • What is a CLR metadata in a managed module?
  • How a .NET assembly works
  • Life cycle of a managed module
  • Win32 executable vs .NET executable
MythBusters certainly is contagious.

While I was browsing for the understanding of metadata in terms of .net technology, I wanted to know about the .net metadata myth; it is great to a grasp what the metadata is and the way a managed module is built, loaded and ran. I couldnt find much over bing/google. Except for a few blogs or that how the CLR loads objects; that were a bit challenging to understand.
Read more ...

Wednesday, June 17, 2009

C# data type corresponding to VARCHAR(MAX)

I stumbled into size limitations while specifying VARCHAR(MAX) in C# code.

Following two options can be the C# alternative to VARCHAR(MAX) or NVARCHAR(MAX) that I have tried for SQL Server 2005.

Lets go step by step and verify as well.

STEP 1: Create a table

CREATE TABLE TBL_MAXTEST (LARGE_DATA VARCHAR(MAX))

STEP 2: C# Code
Run the following locs.

2a) Set parameter size equal to minus one (-1)
//Create a brand new object
SqlParameter theParam = new SqlParameter();

//Fill in the public properties
theParam.ParameterName = "@LARGE_DATA";
theParam.SqlDbType = SqlDbType.VarChar;
theParam.Size = -1;
theParam.Value = strLargeData;
theParam.Direction = ParameterDirection.Input;

//Add and enjoy!
cmdSQL.Parameters.Add(theParam);


2b) Or donot specify any column size.
By default the length is assumed to be -1;




STEP 3: Verify data length
Verify the length of the data.

SELECT DATALENGTH(LARGE_DATA) FROM TBL_MAXTEST

The above shall give the length of the data. I have tried it with 7999 bytes, 8000, 8001, and 10000 bytes.

Basically, VARCHAR and NVARCHAR comes in Large-Value Data Types. NVARCHAR supports Unicode characters which means a wider "range" of characters/code pages. And since its Unicode therefore a single character is stored on two bytes; which inherently means NVARCHAR is twice the size of VARCHAR.

While VARCHAR stores one char per byte; and the problem with data types that use one byte to encode each character is that the data type can only represent 256 different characters.

The following table shows the relationship between the large -value data types and their counterparts from earlier versions of SQL Server.

Large-value data types Earlier versions (SQL Server 2000/etc)
varchar(max) text*

nvarchar(max) ntext*

varbinary(max) image



Note that, TEXT, NTEXT, and IMAGE data types are now a deprecated data types.

  • Use char when the sizes of the column data entries are consistent/same.
  • Use varchar when the sizes of the column data entries vary considerably; that is the max and minimum amount of length is known.
  • Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.
A table can contain a maximum of 8,060 bytes per row; but this restriction does not apply to varchar(max), nvarchar(max), varbinary(max), text, image, or xml columns.

You can use the large-value data types, i.e. the MAX value, to store up to 2^31-1 bytes of data; precisely 2147483648 – 1 bytes; approximately 2 GB.

Read more ...

Monday, June 15, 2009

"Fail to plan, plan to fail"

The lifestyle and thoughts of Hazrat Ali has always been a source of motivation in my life; Probably mostly because I heard alot of stories of the Might, and Kindness, and Bravery, and his Thoughtfulness. There is this famous story that used to roam around us all the time when we were young: Once, a man came to Hazrat Ali and questioned that exactly how much control do we have on our lives? Hazrat Ali smiled thoughtfully, and asked him to stand and fold one of his leg; The man followed, and looked at Hazrat Ali as to how this is going to answer his question; Harzat Ali said; fold your other leg. The man wondered, puzzled, said he cannot do that. Thats about it, thats all the control that you have on your life.

This is a very well known story that most of us has heard, but I believe seldom are the people who would know the meaning of this story. And well being "smart" I believe I am able to "decypher" the meaning (0:

All it means, that everything, basically every "Result", is based upon effort. The more the effort, the better the effort, the more "Managed-Effort", the more well-planned-effort, the more predictable-obvious-desireable-result.

And if, after well planned effort, you did not get the desired-result, so according to Hazrat Ali, that is out of the control of our life. Thats where the "Fate" comes in. I donot have to do anything and that I "cannot-do" anything about it. Exactly like the man who couldnt fold the other leg at the same time, otherwise he would fall down. And the good thing is that atleast he tried; otherwise he wouldnt have known.

And I believe in effort. Result is the outcome of the effort. "Fail to plan, plan to fail".

Obviously I am talking about "normal" human beings, and not the especial-exceptional-cases.

thinking about this story some years back, following was the "aamad('synchronous-flow-of-incoming-thoughts' is how I would define it :0)" I had:

~ poochay hain kay maula, ye taqdeer kya bala?
kehtay hain kay koshish mein hai taqdeer bas chupi.

~ jana meray maulna nay, Rehman ko tabhi.
iraadon kay tootnay pay tadab'bur kiya jabhi!

The second verse is from a quote from Hazrat Ali, that I like, and that I "rendered" into a poetic-verse. (o:

I like this: "Most gulls don't bother to learn more than the simplest facts of flight—how to get from shore to food and back again. For most gulls, it is not flying that matters, but eating. For this gull, though, it was not eating that mattered, but flight. More than anything else, Jonathan Livingston Seagull loved to fly." — Richard Bach, 'Jonathan Livingston Seagull'
Read more ...

Saturday, June 13, 2009

WebBrowser Control: Bypass integrated authentication


How bypass integrated authentication using WebBrowser control?

Well, sometime back, I was hosting the WebBrowser control over WinForm and I wanted to access a remote/secure(https) website; The website was throwing a windows authentication dialog box whenever the url was typed in.

Fig1: The well known network credentials popup dialog box

This started bugging me and my program as well; since I wanted my program to automate this behavior and provide the credentials “hautomatically” (handle-automatically :0) So I told myself, issues are sweet, that’s how you get to learn more; Lets get sweetened!

Therefore, with absolute positive energy, I dived into google, and my “at-the-moment-favourite” – overflowing stack

Now the question was how to provide integrated authentication while having no access to the site, that is, my integrated windows credentials are not valid for the remote site; So how to force a different credential(other than the logged in one) plus override the WebBrowser credentials popup prompt.

Note, for those, who are in a hurry and running after the unstretchable deadline, please close your eyes - copy and paste the "The Code" section in your .NET WinForm application... and follow following steps:

STEP 1: Create a plain windows form application, double click over the form in design more to implement its OnLoad event.
STEP 2: Copy paste all of the code in the FormTest.cs file

...and well, hit the Run!

-- UPDATE 2011/04/09 --

Please download the zipped solution (.SLN) from here. Thanks to Eduardo for his feedback.

Rest, please follow.

There are four types of authentication, Windows, Forms, Passport, and None. Usual use is of the forms based authentication and integrated windows authentication (IWA). My scenario obviously had an integrated authentication system; and the logged in user is popped up with a window for required credentials.

Fig 2: [Illustration by Vesku’s blog]

Following is somewhat, in most cases, a generic site authentication process:

Fig 3: [Illustration by IBM] A generic authentication flow.

For this, following interfaces needed implementation. IAuthenticate, IServiceProvider, IOleClientSite;

IAuthenticate, is used by the WebBrowser object to automatically retrieve the user credentials and provides it to the website upon request.

Urlmon.dll uses the QueryInterface method on the client application's implementation of IBindStatusCallback to get a pointer to the client application's IAuthenticate interface.
If the client application is hosting Mshtml.dll, Mshtml.dll requests a pointer to the client application's implementation of IAuthenticate interface by calling QueryInterface on the client application's IServiceProvider interface.
IOleClientSite, is used to notify the WebBrowser about the website that is going to require the credentials.

Within a compound document, each embedded object has its own client site — the place where it is displayed and through which it receives information about its storage, user interface, and other resources. IOleObject::SetClientSite is the only method enabling an embedded object to obtain a pointer to its client site.

A container can notify an object of its client site either at the time the object is created or, subsequently, when the object is initialized.
The IServiceProvider interface is a generic access mechanism to locate a GUID-identified service that is provided through a control or any other object that the service can communicate with. For example, an embedded object (such as an OLE control) typically communicates only with its associated client site object in the container through the IOleClientSite interface that is supplied by using IOleObject::SetClientSite. The embedded object must ask the client site for some other service that the container supports when that service might not be implemented in the client site.

The client site must provide a means by which the control that is managed by the site can access the service when necessary. For example, the IOleInPlaceSite::GetWindowContext function can be used by an in-place object or control to access interface pointers for the document object that contains the site and the frame object that contains the document.

Because these interface pointers exist on separate objects, the control cannot call the site's QueryInterface to obtain those pointers. Instead, use the IServiceProvider interface.

The IServiceProvider interface has only one member, QueryService, through which a caller specifies the service ID (SID, a GUID), the IID of the interface to return, and the address of the caller's interface pointer variable.
The Code

Following is how IOleObject is going to be implemented into your windows form;
[ComImport,
    Guid("00000112-0000-0000-C000-000000000046"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IOleObject
    {
    void SetClientSite(IOleClientSite pClientSite);
    void GetClientSite(IOleClientSite ppClientSite);
    void SetHostNames(object szContainerApp, object szContainerObj);
    void Close(uint dwSaveOption);
    void SetMoniker(uint dwWhichMoniker, object pmk);
    void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk);
    void InitFromData(IDataObject pDataObject, bool
    fCreation, uint dwReserved);
    void GetClipboardData(uint dwReserved, IDataObject ppDataObject);
    void DoVerb(uint iVerb, uint lpmsg, object pActiveSite,
    uint lindex, uint hwndParent, uint lprcPosRect);
    void EnumVerbs(object ppEnumOleVerb);
    void Update();
    void IsUpToDate();
    void GetUserClassID(uint pClsid);
    void GetUserType(uint dwFormOfType, uint pszUserType);
    void SetExtent(uint dwDrawAspect, uint psizel);
    void GetExtent(uint dwDrawAspect, uint psizel);
    void Advise(object pAdvSink, uint pdwConnection);
    void Unadvise(uint dwConnection);
    void EnumAdvise(object ppenumAdvise);
    void GetMiscStatus(uint dwAspect, uint pdwStatus);
    void SetColorScheme(object pLogpal);
    }

IOleClientSite requires following implementation:
[ComImport,
    Guid("00000118-0000-0000-C000-000000000046"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IOleClientSite
    {
    void SaveObject();
    void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk);
    void GetContainer(object ppContainer);
    void ShowObject();
    void OnShowWindow(bool fShow);
    void RequestNewObjectLayout();
    } 

IServiceProvider has following:
[ComImport,
    GuidAttribute("6d5140c1-7436-11ce-8034-00aa006009fa"),
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown),
    ComVisible(false)]
    public interface IServiceProvider
    {
    [return: MarshalAs(UnmanagedType.I4)]
    [PreserveSig]
    int QueryService(ref Guid guidService, ref Guid riid, out IntPtr
    ppvObject);
    } 

[ComImport, GuidAttribute("79EAC9D0-BAF9-11CE-8C82-00AA004BA90B"),
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown),
    ComVisible(false)]
    public interface IAuthenticate
    {
    [return: MarshalAs(UnmanagedType.I4)]
    [PreserveSig]
    int Authenticate(ref IntPtr phwnd,
    ref IntPtr pszUsername,
    ref IntPtr pszPassword
    );
    } 

You may implement your form class like following:
public partial class FrmTest : Form, IOleClientSite, IServiceProvider, IAuthenticate
    {
    public static Guid IID_IAuthenticate = new Guid("79eac9d0-baf9-11ce-8c82-00aa004ba90b");
    public const int INET_E_DEFAULT_ACTION = unchecked((int)0x800C0011);
    public const int S_OK = unchecked((int)0x00000000);


    public FrmTest()
    { InitializeComponent(); }


    private void button1_Click(object sender, EventArgs e)
    {
    string oURL = this.textBox1.Text;
    webBrowser1.Navigate(oURL);
    }

Note, that now, when we host the WebBrowser control and implement the IAuthenticate interface to programmatically bypass the authentication process, sometimes Internet Explorer does not call the Authenticate function, which causes an authentication dialog to appear. So, this is somewhat buggy.
So, the workaround is to; "first" move to "about:blank" i.e.: Navigate("about:blank"), before moving to the secure site.

private void FrmTest_Load(object sender, EventArgs e)
    {
    //Navigate to about:blank
    string oURL = "about:blank";
    webBrowser1.Navigate(oURL);

    //Notify the WebBrowser object about the client site.
    //The client site, informs an embedded object of its display location
    object obj = webBrowser1.ActiveXInstance;
    IOleObject oc = obj as IOleObject;
    oc.SetClientSite(this as IOleClientSite);
    } 

IOleClientSite member definitions:
public void SaveObject()
    {
    // TODO: Add FrmTest.SaveObject implementation
    }


    public void GetMoniker(uint dwAssign, uint dwWhichMoniker, object
    ppmk)
    {
    // TODO: Add FrmTest.GetMoniker implementation
    }


    public void GetContainer(object ppContainer)
    {
    ppContainer = this;
    }


    public void ShowObject()
    {
    // TODO: Add FrmTest.ShowObject implementation
    }


    public void OnShowWindow(bool fShow)
    {
    // TODO: Add FrmTest.OnShowWindow implementation
    }


    public void RequestNewObjectLayout()
    {
    // TODO: Add FrmTest.RequestNewObjectLayout implementation
    } 

Interface IServiceProvider explicit implementation:

public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
    {
    int nRet = guidService.CompareTo(IID_IAuthenticate); // Zero returned if the compared objects are equal
    if (nRet == 0)
    {
    nRet = riid.CompareTo(IID_IAuthenticate); // Zero returned if the compared objects are equal
    if (nRet == 0)
    {
    //This method returns an interface pointer that represents the requested interface on the specified object. It is particularly useful if you have an unmanaged method that expects to be passed an interface pointer; Add to the COM interface reference count.
    ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IAuthenticate));
    return S_OK;
    }
    }
    ppvObject = new IntPtr();
    return INET_E_DEFAULT_ACTION;
    }

The main, IAuthenticate implementation
public int Authenticate(ref IntPtr phwnd, ref IntPtr pszUsername,
    ref IntPtr pszPassword)
    {
    //Copies the contents of a managed String to a block of memory allocated from the unmanaged COM task allocator.
    IntPtr strUser = Marshal.StringToCoTaskMemAuto(txtUserID.Text);
    IntPtr strPassword = Marshal.StringToCoTaskMemAuto(txtPassword.Text);


    pszUsername = strUser;
    pszPassword = strPassword;
    return S_OK;
    }

    }

Here is how to pass the proxy credentials in WebBrowser control:
Uri uri = new Uri("http://www.somewheresurl.com");

//Alternatively, you may use System.Text.UnicodeEncoding.UTF8.GetBytes()
string additionalHeaderInfo = "Authorization: Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("MyUsername" + ":" + "MyPassword")) + System.Environment.NewLine;

webBrowser1.Navigate(uri, null, null, additionalHeaderInfo);
This is for me, and for those who came across similar sweet issue. Hope this helps.

Read more ...

Thursday, May 21, 2009

What is SaaS?

What is SaaS?

Some time back I came across this question in an interview; with less knowledge I guessed its “its and bits”. After diving in to several articles, groups, forums, discussions, reviews, and thoughts; and an “attempt” to provide my own SaaS based custom email app made me learn a lot. Following are the few points which are based upon my short experience over SaaS and thoughts from the industry experts.

SaaS is a new delivery model where companies pay for using the software. Usually, SaaS applications are hosted by the vendor itself. This means, lower total cost of owner-ship and quick/rapid return on investment. Its multi-tenant architecture, is like living in an apartment with your own private space.

Some of the features that SaaS offers:

  • SaaS eliminates the effort associated with updates and maintenance.
  • Software is distributed over a server, and is access through a web browser.
  • There are no software updates to install, and no backup plans are required: the SaaS vendor maintains redundant backups of all systems and data.
  • SaaS is pay per user per month(or year) model, that usually resides on Cloud Platform. Note that, SaaS is not Cloud, but a Cloud can contain a SaaS app.
  • Provides the level of granularity in options/customization.
  • Though “On-Premise” is not truly a SaaS application, the software installations provide the ability to customize an application for a customer’s specific needs.
  • The medium of communication is the internet, so the “performance-hit” is inherent. But this really depends upon the physical network and the type internet service provider being used.
  • Usually a SaaS vendor maintains servers(farm) as part of their network operation services; and therefore the application processing is distributed throughout the network and is optimized, this gives efficiency, higher up-time, and dependable performance.
  • Rapid and responsive development and introduced the methodology of rapidly releasing functionality, that utilizes Agile or XP etc; this enables to be more responsive to customer requests and technological advancements.
  • Since the services are bought, therefore this lowers the total cost of ownership; and reduces unpredictable costs, meaning less risk for the customer. As a result, risks can be avoided, risk is reduced, or transferred.
  • SaaS apps are by nature centrally managed, which means no more patch installations.
Illustration by a glance to cloud


Who needs SaaS?

Since SaaS’s primary platform is Cloud; the architecture of cloud apps better suits to enterprises.


Who are SaaS Providers?

Some that I can count on fingers are,

* Salesforce.com and SugarCRM are practical running application of SaaS apps.
* Linkedin is another application based upon SaaS model.
* Cleartext Systems have their products Clearspace 2.5/Jive SBS, and Axigen - Secure Mail Server.
* And Ubuntu’s Enterprise Cloud (UEC Blog)

Other well known SaaS providers are Google, Microsoft, QuickBooks Online Edition, and WebEx. Some are providing installable SaaS application cloud platform

Concerns:

Two of the concerns that I believe are associated with SaaS; One, the security is the major concern in SaaS applications; a SaaS vendor only knows what's "generally" best; that is, one customer's definition of secure may be another's wide open to attack. And secondly, not all vendors provide service level agreements.

I believe good/emerging SaaS providers should add a trial period before purchase of the service; so a customer would know that the application is already running on the vendor servers; those who do not plan provide such trials should provide a solid explanation.

The multi-tenant architecture of the on-demand model means that everyone runs on the same code base, and often the same instance. This would inherently force the vendors to offer a variety of configuration options so that customers can adapt their view of the application to their customized business needs.

Good service delivery management infrastructure is important, because this requires a need for robust, mature network/application server setup.

And basic/pre-requisite services like providing user provisioning, service level monitoring, reporting, ticketing and tracking, versioning, server load balancing, then not only the vendor is not serious about software-as-a-service, but also is not in a position to guarantee quality of service.

It should provide APIs that comply with web services standards; so that it would be extendable, scalable, designed to make sense from a business point of view.

While reading I came across ZDNet’s Phil Wainewright who seems pretty much optimistic about SaaS and its future:

I can see parallels with a 128k hobbyist microcomputer that a large computer company introduced twenty-five years ago called the IBM Personal Computer. It was targeted at small businesses and hobbyists and was expected to sell just a hundred thousand units or so. Instead, it sold millions and spawned an industry. Sometimes the stuff you build for the little guys turns out to be just as useful for all the big guys too.


References:
* SaaS Google Group

* AlertLogic Blog
* OnDemand BusinessObjects
* SaaS Subscriptions

Read more ...

Tuesday, May 19, 2009

DTD v/s XSD: And What is DSD?

DTD v/s XSD:

Since I have looking into the XML schemas and validators, I was wondering about the differences between a DTD and XSD. Which one would we use when? And why?
Following are a couple of differences that I am able to conclude.

Sample Bookstore.XML

Data type definition(DTD):
• DTD provides a basic grammar for defining a document(HTML/XML/etc); its elements, attributes, their nesting and ordering.
• Namespaces cant be defined in DTD
• Hard to extend once written
• The language to define a DTD is called MNF; I forgot but it’s like Markus-Naum language.
• Requires a specific parser
• No builtin data types
• Matured/proven industry standard and is been in the industry for a while now
• Freely available for re-use

Bookstore.DTD

XML Schema definition(XSD):

• Includes full capabilities provided by DTD
• Is Namespace'able
• Since the document is based upon xml standards, the normal xml parse is used to parse the xsd document.
• Can define data types, elements, attributes, nestings, orderings, etc; and what the data can/cannot contain.
• Easily extendable
• Since its based upon XML, I believe its easily learnable.
• Contains builtin data types
• XML document has an XML Namespace to refer to and an XML DTD to define it. This becomes overhead when a parser examines the document, it may have to link this all in, interpret the DTD for the schema, load the namespace, and validate the schema; all before it can parse the actual XML document; this overhead may seriously degrade performance or system availability.

Bookstore.XSD



If you like to look more, the Document Structure Description(DSD) defines the next generation schema languages, see examples.
Read more ...

Thursday, April 16, 2009

Design pattern: Chain of responsibility (COR)



Design pattern: Chain of responsibility (COR)


COR is a software coding practice used by the "Gang of Four". It comes under the behavioral category since for each request the behavior of the object would be different.


A good example could be a Help software module; For instance there are three buttons, red blue and green , on a user interface. And for each button-click the system shall show a message box with the related helpful text. So, when red button is click the a help command is routed to each of the object on the user interface (all of the buttons and UI itself in this case) and that object is going to decided if it has to process the request or pass it on to the next object.


FIG 1: Chain of responsibility design


Similar would be the case when the UI will be clicked; the message will route to all of the objects and would eventually be handled by the UI object.


This promotes loose coupling between sender of a request and its receiver by giving more than one object an opportunity to handle the request.

Main program calls the chain as follows:

int nRequest = 1;//Could be 1 = multiply, 2 = divide, 3= add, etc.
(new CSubtractChn(new CAddChn(new CMultiplyChn(new CNoopChn())))).HandleRequest(1);


Following handles the multiply request,

public class CMultiplyChn : CChain
{
public CMultiplyChn() { }
public CMultiplyChn(CChain nextChain)
{
AddNextInChain(nextChain); //Add the next in chain
}

public override void HandleRequest(int nRequest)
{
CHelper.Msg("Checking multipy");
switch (nRequest)
{
case 1:
CHelper.Msg("Found request 1, processing... multiply done.");
break;
default:
if (m_NextInChain != null)
{
CHelper.Msg("Nexting...");
m_NextInChain.HandleRequest(nRequest);
}

break;

}
}

Following is the base chain class,

public abstract class CChain
{
protected CChain m_NextInChain;

///
/// Adds the next in chain
///

///
public void AddNextInChain(CChain theChain)
{
m_NextInChain = theChain;
}

///
/// Abstract: Must implement all derived classes
///

///
public abstract void HandleRequest(int nRequest);

}
Read more ...

Thursday, March 26, 2009

Calculate meta length!

How to calculate meta length in characters:

private static string GetMetaLengthBin(int nDataLength)
{
//Steps to calculate the meta length in characters
//STEP 1: Get the div part. Divide data length by 256
//STEP 2: Get the Modulus part. Take modulus of Data length by 256
//STEP 3: Convert into characters, concat and return. 

string strLengthInChars = string.Empty;
int nPartDiv = nDataLength / 256;
int nPartMod = nDataLength % 256;
char chDiv = Convert.ToChar(nPartDiv);
char chMod = Convert.ToChar(nPartMod);

strLengthInChars = chDiv.ToString() + chMod.ToString();

return strLengthInChars;
}
Read more ...

Tuesday, March 24, 2009

HSM Simulator

Released on Mar 8th, an excellent resource was forwarded by a friend, is a software based open implementation of HSM, Hardware security module. The HSM simulator is a library that provides almost all of the functionalities offered by Thales HSM.

Almost all means, that several commands were not implemented fully (like the PA-Load Formatting Data whose sole implementation task is to respond to the host application) or not at all (like commands that have to do with the IBM verification method).

Thales Simulator Library
Read more ...

Tuesday, February 24, 2009

Blogging and the freedom of expression?

While wikipedia explains what could be the freedom of expression, India, the "largest-democracy" seems like is unable to handle the truth; the Indian court holds 19 years old boy for exercising the right of freedom of expression on his web blog.

On the other hand it really gets controversial when what could be the limit the freedom of expression should be exercised; and a fine line between hurting poeples' feelings and handling the truth. Well, for you to decide!
Read more ...

Thursday, February 19, 2009

Corrupt MAPI32.DLL or MSMAPI32.DLL?

For some reason, today, installed this MS Word 2007; and zap...! My MS Outlook 2003 stopped working. I was continuously getting this error
Cannot start Microsoft Office Outlook. MAPI32.DLL is corrupt or the wrong version. This could have been caused by installing messaging software. Please reinstall Outlook


After almost a day's worth of trying different things from google comments/pointers(from installing to reinstalling to reparing to installing 2007 and then uninstalling 2007/etc); even there is a fix called fixmapi.exe in \Windows\System32\FixMapi.exe provided by Microsoft that didn't work at all. But well... finally I am able to get it to run. I am posting this so it would of help if in future I or someone like me encounter similar lucky issues, I would say (0:


1.Rename MSMAPI32.DLL to MSMAPI32.BAK in folder C:\Program Files\Common Files\System\Msmapi\1033
2.Start Outlook 2003.
Read more ...

Tuesday, February 17, 2009

Understanding ISO8583 Message Type

Understanding ISO8583 Message Type

The following visual "reveal"(I would say) the meaning and composition of an ISO8583 message type. First bit tells the version of the ISO8583, second defines the class, third is for function to be performed(request/response/etc), and the last one shows origin. See following fig-1.


FIG 1: Message type definitions of each bit.

Here(Mitug) is a resource that helped me a lot to get started, that was referred to me by my friend Syed Kamran. In some places Mitug website doesn't work, atleast a couple of my friends from different locations of the world map reported the website is not opening the page. So for friends who are unable to open the same, rightclick and save as this [PDF] layman's guide to understanding the ISO8583 financial transaction messages.


Read more ...

Monday, February 16, 2009

USPS & Mailing SCM Industry: Introduction

USPS & Mailing SCM Industry: Introduction

I was thinking about the rag’ra (pain) I had when I started learning about a domain that was entirely new me. United States mailing supply chain industry has a very complex business process. And it would take several years for a newbie to understanding the ups and downs by leaps and bounds of this industry, in specific United States Postal Services aka USPS.

Since I was hired to develop a solution that could ease the mailing industry business process, and help large and small mailers as well as consolidators. Software that would provide the ease of scheduling appointments with USPS with a click of a mouse; which I delivered gracefully.

The day I left Assurety Consulting, I decided to add my 5 pints and document all of my mailing industry knowledge that I learned while developing solution, so that it could be helpful to someone who is in need.

The reason business is so complex is its obscurity. You find almost no information how the mailing supply chain management business work in United States. You sure will need a seasoned “he” or “she” having more than 15 years of experience to guide you through the process; and wait, sometimes the seasoned-ones also stop and think and discuss and then reveals the business process. And when you get to learn these things, you find a lot of loops and holes along with a full throttle/burst of ideas/suggestions/fixes that sometimes are, again, bottle-necked by the process that this business industry follows for requesting a change.

Although the process followed by MTAC for the change request is not bad; but what lacks is the interaction with small mailers/consolidators. They could, I believe, play a vital role to ease the business process and their requirements.

I have been through the FAST Certification process several times; and we found a great deal of misleading in documentation and actual implementation while conducting USPS FAST CAT tests for TM v2.*. We even compiled a list of issues and non conformance and handed over the FAST.

I shall write more on the mailing industry as I get time and share more of my knowledge and experience with the industry.
Read more ...

Monday, February 9, 2009

Banking Industry - Default Account Marking Business Process

Card Default Account Marking Business Process

This is for those who had problems understanding a generic default account marking business process. I had a problem understanding the same, so I came up with a visual that helped a lot.

Business rules:
Rule 1:
At any time only 1 account can be marked as "O"
Rule 2:
If there is a "O" type account in one category then no "Y" type account is required in that category
Rule 3:
If there is a "Y" type account in one category then all other accounts should be of "N" type.



Following is the simple code for the above flow.

private void ApplyRulesEx(DataTable dtAtmCards)
{
//Pseudo:
//STEP 1: Demark old account status to N
//STEP 2: Mark the new account status to O or Y
// 

bool bStatus = false;

try
{
string strRequestCategory = m_strCategory;//"10";//
string strRequestAccountNumber = m_strAccountNumber;//"0949011001576";//
string strRequestMarkAs = m_strFlagToBeMarked; //"Overall Default";// //mark as O, overall default

for (int nRowNumber = 0; nRowNumber < dtAtmCards.Rows.Count; nRowNumber++) 
{ 
//1. Parse through each db record, get the status and category 
//2. if dbCategory = requestCategory) then mark all O and Y's as N 
//3. Else, its a different category, so mark all O as Y. 
// make it default for that type only 
//4. Send 

string dbAccountStatus = dtAtmCards.Rows[nRowNumber]["Default_Account"].ToString(); 
if (dbAccountStatus.Equals("")) dbAccountStatus = "Non Default"; 
string dbAccountNumber = dtAtmCards.Rows[nRowNumber]["Account_No"].ToString(); 
string dbAccountCategory = dtAtmCards.Rows[nRowNumber]["Account_Type"].ToString(); 

if (dbAccountCategory == strRequestCategory) 
{ 
if (dbAccountNumber == strRequestAccountNumber) 
{ 
//the request account status change is same as the current overall default account. 
//Lets change that directly. Only one call to phoenix. 
//this means, that the requested account number and category is same as 
//the db account number and category, so, no need to send any "N" statuses, 
//lets do nothing. and break; 

CLogger.getInstance().Log("Try marking account: [" + dbAccountNumber + ":" + dbAccountCategory + "] as: [" + strRequestMarkAs + "]", System.Diagnostics.TraceLevel.Info); 
bStatus = MarkAs(strRequestMarkAs, dbAccountCategory, dbAccountNumber); 
} 

else 
{ 
//2. if dbCategory = requestCategory) then mark all O and Y's as N 
//means, the requested category is same as the this account's category, 
//so this means if the current db status is overall or yes, 
//we need to make it N 

if (dbAccountStatus == AccountStatuses.Overall || dbAccountStatus == AccountStatuses.Yes) 
{ 
//Make it "N", which means No. 
CLogger.getInstance().Log("Try marking account: [" + dbAccountNumber + ":" + dbAccountCategory + "] as: [" + AccountStatuses.No.ToString().Substring(0, 1).ToString() + "]", System.Diagnostics.TraceLevel.Info); 
bStatus = MarkAs(AccountStatuses.No.ToString().Substring(0, 1), dbAccountCategory, dbAccountNumber); 
} 
} 
} 
else 
{ 
//3. else, its a different category, so mark all O as Y. 
// make it default for that type only 
if (dbAccountStatus == AccountStatuses.Overall) 
{ 
//Change any overall status from Overall to Yes, send Y. Static/hardcoded. 
//TODO: Put Y in app.config for ready change. 
CLogger.getInstance().Log("Try marking account: [" + dbAccountNumber + ":" + dbAccountCategory + "] as: [" + "Y" + "]", System.Diagnostics.TraceLevel.Info); 
bStatus = MarkAs("Y", dbAccountCategory, dbAccountNumber); 
} 
} 
} 
} 
catch (Exception exc) 
{ 
CLogger.getInstance().Error("CAccountHandler", "ApplyRules", exc.Message); 
} 
} 

Hope this helps.
Read more ...

Mailing Industry Supply Chain Management Business Process [Exposed]

USPS/Mailing Industry Supply Chain Management Business Process [Exposed]

The supply chain management in United States has the following business process, that incorporates United States Postal Services(USPS), and small/medium/large mailers/consolidators. This is to help understand the SCM business process that is too complex, and requires alot of thoughts and ideas.

Read more ...

Friday, January 30, 2009

3M Window flims

3M, a leader in technology brings together to create finest window flims products, see attached advertisement.

Read more ...

Thursday, January 29, 2009

Producer consumer architecture

Typical example; Producer- consumer multi threaded architecture.

Couple of months ago I came across a problem that I solved by using this architecture. Following is the code that dealt with the following solution,

For example, there are two guys and a list; guy1 requests for the appointment scheduling for probably a doctor, request gets added to a list/queue, and then the guy2 in turn looks for the appointment requests in that list/queue and sends them for processing one by one accordingly.

I’ll add a visual soon to provide a better understanding. This is upon my friend, Talpur’s, request.


/* *********************************************************************************************************
*
* Class Name : CApptScheduler
* Filename : CApptScheduler.cs
*
* Author : Kamran Khan - KAMRANHK_AT_gmail.com
* Date : Snuday, Mar 23, 2008
*
* Copyright : (c) Copy left.
* http://izlooite.blogspot.com
*
* Disclaimer : THE INFORMATION FROM OR THROUGH THE SITE IS PROVIDED “AS IS,” “AS AVAILABLE,” AND ALL WARRANTIES,
* EXPRESS OR IMPLIED, ARE DISCLAIMED (INCLUDING, BUT NOT LIMITED TO, THE DISCLAIMER OF ANY IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE).
* THE INFORMATION AND SERVICES MAY CONTAIN BUGS, ERRORS, PROBLEMS OR OTHER LIMITATIONS.
* ALL RESPONSIBILITY OR LIABILITY FOR ANY DAMAGES CAUSED BY VIRUSES CONTAINED WITHIN THE ELECTRONIC
* FILE CONTAINING THE FORM OR DOCUMENT IS DISCLAIMED.
*
*
* Type : Backend processing, the thread.
* Description : This object should be able to handling the scheduling of appointment, without
* a glitch over front end. The reason of this object is to keep the appointment
* scheduling entirely separate from the gui. Just the click of the button should
* enqueue the plan in this object, and this object will handle the scheduling of
* appointment.
*
* References :
*
* Révision History :
* -----------------------------------------------------------------------------------------------
* DATE OWNER DESCRIPTION
* -----------------------------------------------------------------------------------------------
* Mar-23-07 KHK -Added the class
* -Added methods, the skeleton code
* -Add the queue manager, that will handle the tasks, the Plan objects.
*
*/


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Business.Controls.Planning
{
public class CApptScheduler : IDisposable
{
private object theLocker = new object(); //Locking mechanism
private Queue m_theQueue = new Queue();//List that would contain the jobs
Thread[] m_theWorkers;//Worker threads

///
/// Schedules the appointment; the producer.
///

/// Worker count, to start with
public CApptScheduler(int nWorkerCount)
{
m_theWorkers = new Thread[nWorkerCount];

for (int i = 0; i < nWorkerCount; i++)
{
(m_theWorkers[i] = new Thread(Consume)).Start();
}

}

///
/// Schedules an appointment. Consumer
///

private void Consume()
{
while (true)
{
//CPlan thePlan = null;
lock (theLocker)
{
while (m_theQueue.Count == 0) Monitor.Wait(theLocker);
//here perform the appointment scheduling.
{
//here look for the appointment type and schedule accordingly.
//regular, create update cancel
//content, create update cancel.
ScheduleThis(m_theQueue.Dequeue());
}
}
if (m_theQueue.Peek() == null) //nothing found.
return; //exit!
}

}

///
/// Schedules the appointment; the one that gets called during consumption.
///

/// The plan.
private void ScheduleThis(string thePlan)
{
throw new Exception("The method or operation is not implemented.");
}

///
/// Enqueues the appt.
///

/// The plan.
public void EnqueueAppt(string thePlan)
{
lock (theLocker)
{
m_theQueue.Enqueue(thePlan);//Add the plan into list
Monitor.PulseAll(theLocker);
}
}

#region IDisposable Members

public void Dispose()
{
foreach (Thread worker in m_theWorkers) EnqueueAppt(null);
foreach (Thread worker in m_theWorkers) worker.Join();
}

#endregion

#region IDisposable Members

void IDisposable.Dispose()
{
throw new Exception("The method or operation is not implemented.");
}

#endregion


}
}
Read more ...

Wednesday, January 28, 2009

Google Ajax language apis

Google ajax language api, an excellent online tool for myself for webdevs.

http://code.google.com/apis/ajaxlanguage/documentation/#Examples
Read more ...

Sunday, January 4, 2009

Fax related resource

An excellent fax related resource that I came across while surfing the net.
Read more ...
Designed By Published.. Blogger Templates