My first x64 assembly code cooked by hand

This is my first hand made x64 assembly code.

extrn MessageBoxA:proc

.DATA
CONST SEGMENT
    msg DB "Hello World!", 0
CONST ENDS

.CODE
main PROC
    sub rsp, 28h
    xor rcx, rcx
    lea rdx, msg
    lea r8, msg
    xor r9, r9
    call MessageBoxA
main ENDP
END

To compile this code, in command prompt run

ml64 helloworld.asm /link /subsystem:windows /defaultlib:user32.lib /entry:main

The major different between x86 and x64 calling conventions. The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9, while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for floating point arguments. additional arguments are passed on the stack and the return value is stored in RAX.

Resize photo in C#

Today, my friend asked me to recommend a free resize photo tool, and he got lots of photos. I really don’t know. Normally I use MS Paint and select Stretch and Skew. If here are many photos, it would take ages. I then wrote a small tool for him using C#. The code is fairly simple cause .Net Framework provides huge library to use.

private void ResizePhoto()
{
	//find all jpg photos in given folder
	string[] jpgFiles = Directory.GetFiles(photoPath,"*.jpg");
	foreach(string file in jpgFiles)
	{
		//load orignial photo into memory
		Image image = Image.FromFile(file);
		//create new photo using new size from original photo
		Image newImage = new Bitmap(image,new Size(newWidth,newHeight));
		//give a new file name
		string newFileName = "newsize" + file;
		//save new photo
		newImage.Save(newFileName,System.Drawing.Imaging.ImageFormat.Jpeg);
		newImage.Dispose();
		image.Dispose();
	}
}

 

Add watermark in your photo using C#

Some of my photos need to be added watermark to prevent abusing. I just found a easy way to add watermark into your photo using C#. Here is the code to share.

private void button1_Click(object sender, System.EventArgs e)
{
	OpenFileDialog of = new OpenFileDialog();
	of.Filter = "Image Files (*.bmp;*.emf;*.exif;*.gif;*.jpg;*.png;*.tif;*.wmf)|*.bmp;*.emf;*.exif;*.gif;*.jpg;*.png;*.tif;*.wmf";

	if(of.ShowDialog(this) == DialogResult.OK)
	{
		Image image = Image.FromFile(of.FileName);
		Graphics g = Graphics.FromImage(image);

		// Create a solid brush to write the watermark text on the image
		Brush myBrush = new SolidBrush(Color.Black);
		Font myFont = this.Font;
		// Calculate the size of the text
		SizeF sz = g.MeasureString("https://www.nickdu.com", myFont);

		// drawing position (X,Y)
		float X =(image.Width - sz.Width)/2f;
		float Y = image.Height/2f;

		g.DrawString("https://www.nickdu.com",myFont,myBrush,X,Y);
		SaveFileDialog sf = new SaveFileDialog();
		sf.Filter = of.Filter;
		if(sf.ShowDialog(this) == DialogResult.OK)
		{
			image.Save(sf.FileName);
		}
		image.Dispose();
	}
}

This is the sample

watermark sample
watermark sample

Build your own network usage monitor in C# (raw socket)

Although there are lots of excellent network monitor available, e.g NetWorx, DU Meter, I just think to build my own network monitor. Firstly I can explore the socket programming, secondly it’s free of charge.

OK, let’s do it.

Create a new WinForm project from Visual Studio

Network monitor
Network monitor

 

 

 

 

public MainForm()
{
	//
	// Required for Windows Form Designer support
	//
	InitializeComponent();
	//
	// Set your PC ip address
	//
	rs.CreateAndBindSocket(ipaddress);
	rs.Run();
}
//when main window showing up, we update meter text
private void ShowMainWindow(object sender, System.EventArgs e)
{
	todayUsage.Text = string.Format("Today's Usage: {0}",FormatSize((ulong)rs.TotalReceivedBytes));	
}
//define ip header
[StructLayout(LayoutKind.Explicit)]  
public   struct   IPHeader  
{  
	[FieldOffset(0)]    public  byte       ip_verlen;    
	[FieldOffset(1)]    public  byte       ip_tos;    
	[FieldOffset(2)]    public  ushort     ip_totallength;  
	[FieldOffset(4)]    public  ushort     ip_id;  
	[FieldOffset(6)]    public  ushort     ip_offset;    
	[FieldOffset(8)]    public  byte       ip_ttl;    
	[FieldOffset(9)]    public  byte       ip_protocol;    
	[FieldOffset(10)]   public  ushort   ip_checksum;    
	[FieldOffset(12)]   public  uint       ip_srcaddr;    
	[FieldOffset(16)]   public  uint       ip_destaddr;    
}  
//the core function - raw socket   
public   class   RawSocket  
{
	private		bool	error_occurred;  
	private	  int	totalReceivedBytes = 0;
	private	  const   int   BUFFER_SIZE = 8192;
	private	  byte []	receive_buf = new byte[BUFFER_SIZE];
	private	  Socket	socket   =   null;    
	const	  int   SIO_R = unchecked((int)0x98000001);  
	const	  int   SIO_1 = unchecked((int)0x98000002);  
	const	  int   SIO_2 = unchecked((int)0x98000003);  

	public   int   TotalReceivedBytes
	{  
		get	{return   totalReceivedBytes;}  
		set	{totalReceivedBytes = value;}
	}

	public   RawSocket() 
	{  
		error_occurred=false;   
	}  
       
	public   void   CreateAndBindSocket(string   IP)
	{  
		socket   =   new   Socket(AddressFamily.InterNetwork,   SocketType.Raw,   ProtocolType.IP);  
		socket.Blocking   =   false; 
		socket.Bind(new   IPEndPoint(IPAddress.Parse(IP),   0));  
		if   (SetSocketOption()==false)   
			error_occurred=true;  
	}  
   
	public   bool   ErrorOccurred  
	{  
		get  { return   error_occurred;}  
	}

	public   void   Shutdown()  
	{  
		error_occurred = true;
		if(socket   !=   null)  
		{  
			socket.Shutdown(SocketShutdown.Both);  
			socket.Close();  
		}  
	}  
  
	private   bool   SetSocketOption()  
	{  
		bool   ret_value   =   true;  
		try  
		{  
			socket.SetSocketOption(SocketOptionLevel.IP,   SocketOptionName.HeaderIncluded,   1);       
			byte   []IN   =   new   byte[4]{1,   0,   0,   0};  
			byte   []OUT   =   new   byte[4];      
			int   ret_code   =   socket.IOControl(SIO_R,   IN,   OUT);
			ret_code   =   OUT[0]   +   OUT[1]   +   OUT[2]   +   OUT[3];   
			if(ret_code   !=   0)   ret_value   =   false;  
		}  
		catch(SocketException)  
		{  
			ret_value   =   false;  
		}  
		return   ret_value;  
	}

	unsafe   private   bool   IsInternetTraffic(byte[] buf)  
	{  
		byte	temp_protocol=0;  
		uint	temp_version=0;  
		uint	temp_ip_srcaddr=0;  
		string  sourceIP;  
  
		fixed(byte   *fixed_buf   =   buf)  
		{  
			IPHeader   *   head   =   (IPHeader   *)   fixed_buf;  
			temp_protocol   =   head->ip_protocol;  
			temp_version   =(uint)(head->ip_verlen   &   0xF0)   >>   4;  
			temp_ip_srcaddr   =   head->ip_srcaddr;  
			sourceIP   =   new   IPAddress(temp_ip_srcaddr).ToString();  
		}
                //we only capture internet traffic  
		return !sourceIP.StartsWith("10.") 
			&& !sourceIP.StartsWith("172.16.") 
			&& !sourceIP.StartsWith("127.") 
			&& !sourceIP.StartsWith("192.168.");
	}  
       
	private void ReceiveCallback( IAsyncResult ar)
	{
		try
		{
			int received_bytes = socket.EndReceive(ar);
			if(received_bytes>0 && IsInternetTraffic(receive_buf))
			{
				totalReceivedBytes += received_bytes;
			}
		}
		finally
		{
			socket.BeginReceive(receive_buf,0,BUFFER_SIZE,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);
		}
	}
		
	public   void   Run()    
	{  
		socket.BeginReceive(receive_buf,0,BUFFER_SIZE,SocketFlags.None,new AsyncCallback(ReceiveCallback),null);
	}
}

Exchange Web Services (EWS) programming with Exchange Server 2010 in C#

I have an application to sniff email notification from Exchange Server and automatically insert data record to SQL database. Because recently our IT department upgraded MS Exchange Server to 2010 and Exchange Server 2010 does not support WebDAV any more, my robust WebDAV program is dead 🙁

To revive my application, I have to migrant WebDAV to EWS, because WebDAV has been replaced by Exchange Web Services (EWS). EWS is relatively new, and there is no too much information I can use. Here to share my EWS programming experience.

private void ReceiveEmail(object sender, EventArgs e)
{
    ExchangeServiceBinding esb = new ExchangeServiceBinding();
    esb.Credentials = new NetworkCredential(username, password, domain);
    ServicePointManager.ServerCertificateValidationCallback =
      delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
      {
          // trust any certificate
          return true;
      };

    esb.Url = @"https://exchangeserver/EWS/Exchange.asmx";

    FindItemType findRequest = new FindItemType();
    findRequest.ItemShape = new ItemResponseShapeType();
    findRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;

    DistinguishedFolderIdType inbox = new DistinguishedFolderIdType();
    inbox.Id = DistinguishedFolderIdNameType.inbox;
    findRequest.ParentFolderIds = new []{inbox};
    findRequest.Traversal = ItemQueryTraversalType.Shallow;


    findRequest.ItemShape.BodyType = BodyTypeResponseType.Text;
    findRequest.ItemShape.BodyTypeSpecified = true;

    FindItemResponseType response = esb.FindItem(findRequest);
    FindItemResponseMessageType responseMessage =
      response.ResponseMessages.Items[0]
          as FindItemResponseMessageType;
    ArrayOfRealItemsType items = responseMessage.RootFolder.Item as ArrayOfRealItemsType;


    foreach (MessageType item in items.Items)
    {
         MessageType msg = (MessageType) item;
         string emailBody = GetMessageBody(esb, msg);
         //delete email
         this.DeleteMessage(esb,msg);
    }
}

private string GetMessageBody(ExchangeServiceBinding binding, MessageType message)
{
    string messageBody = string.Empty;
    MessageType temp = null;

    // Call GetItem on each ItemId to retrieve the
    // item’s Body property and any AttachmentIds.
    //
    // Form the GetItem request.
    GetItemType getItemRequest = new GetItemType();

    getItemRequest.ItemShape = new ItemResponseShapeType();
    // AllProperties on a GetItem request WILL return
    // the message body.
    getItemRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;

    getItemRequest.ItemIds = new ItemIdType[1];
    getItemRequest.ItemIds[0] = (BaseItemIdType)message.ItemId;

    // Here is the call to exchange.
    GetItemResponseType getItemResponse = binding.GetItem(getItemRequest);

    // We only passed in one ItemId to the GetItem
    // request. Therefore, we can assume that
    // we got at most one Item back.
    ItemInfoResponseMessageType getItemResponseMessage =
        getItemResponse.ResponseMessages.Items[0]
        as ItemInfoResponseMessageType;

    if (getItemResponseMessage != null)
    {
        if (getItemResponseMessage.ResponseClass ==
            ResponseClassType.Success
            && getItemResponseMessage.Items.Items != null
            && getItemResponseMessage.Items.Items.Length > 0)
            {
                temp = (MessageType)getItemResponseMessage.Items.Items[0];

                if (temp.Body != null)
                    messageBody = temp.Body.Value;
            }
    }
    return messageBody;
}

private void DeleteMessage(ExchangeServiceBinding binding, MessageType message)
{
    // Call DeleteItem on each ItemId to delete the message
    // Form the DeleteItem request.
    DeleteItemType delItemRequest = new DeleteItemType();

    delItemRequest.ItemIds = new ItemIdType[1];
    delItemRequest.ItemIds[0] = (BaseItemIdType)message.ItemId;
    
    // Here is the call to exchange.
    DeleteItemResponseType delItemResponse = binding.DeleteItem(delItemRequest);
}

Dynamically update parity bit during serial communication in C#

Today boss asked me to implement a serial com port simulator. I used MSCommLib COM library in C# to develop this simulator.

The serial protocol I am working on is a bit strange:

The message synchronization is achieved via a technique utilizing the parity bit of serially of transmitted data. The parity bit sent with each byte no longer denotes the byte’s parity, but the parity bit is used instead to indicate the start of new messages.  In this mode, the parity bit is often referred to as the “wake-up bit”.  The parity bit is now used in the following manner:  when the parity bit is set, this denotes the start (i.e. the first byte) of a new message. , the parity bit (or wake-up bit), is only ever set on the first byte (i.e. the address byte) of a message.  The remainder of the message has parity forced to zero.

Therefore, this protocol needs dynamically update the parity bit settings during the message sending. There are many articles about generic serial com programming. However,  it’s hard to find the parity bit toggling. After few hours exploring, I found a way to achieve this.

Like other serial protocol, we have to initialize com port

public void InitComPort(int portNumber)
{
	// Set the com port to be 1
	m_ComPort.CommPort = (short)portNumber;

	// This port is already open, close it to reset it.
	if (m_ComPort.PortOpen)
		m_ComPort.PortOpen = false;

	// Trigger the OnComm event whenever data is received
	m_ComPort.RThreshold = 1;  

	//e, Even. m, Mark. m, (Default) None. o, Odd. s, Space
	// Set the port to 19200 baud, even parity bit, 8 data bits, 1 stop bit (all standard)
	m_ComPort.Settings = "19200,e,8,1";

	// Force the DTR line high, used sometimes to hang up modems
	m_ComPort.DTREnable = true;

	// No handshaking is used
	m_ComPort.Handshaking = MSCommLib.HandshakeConstants.comNone;

	// Use this line instead for byte array input, best for most communications
	m_ComPort.InputMode = MSCommLib.InputModeConstants.comInputModeBinary;

	// Read the entire waiting data when com.Input is used
	m_ComPort.InputLen = 0;

	// Don't discard nulls, 0x00 is a useful byte
	m_ComPort.NullDiscard = false;

	// Attach the event handler
	m_ComPort.OnComm += new MSCommLib.DMSCommEvents_OnCommEventHandler(OnComm);

	m_ComPort.ParityReplace = "0";

	// Open the com port
	m_ComPort.PortOpen = true;
}

Here is the way to toggle the parity bit during sending message

private void SendThread()
{
	while(m_ComPort.PortOpen)
	{
		m_ComPort.PortOpen = false;
		// Change port setting, toggle the parity bit to 1
		m_ComPort.Settings = "19200,m,8,1";
		m_ComPort.PortOpen = true;
		m_ComPort.Output = addr;

		// wait 10 ms to toggle parity bit
		Thread.Sleep(10);
		m_ComPort.PortOpen = false;
		// Change port setting, toggle the parity bit to 0
		m_ComPort.Settings = "19200,s,8,1";
		m_ComPort.PortOpen = true;
		m_ComPort.Output = packet;
	}
}

Edit password protected MS Word document

Some Microsoft Word document has editing restrictions, which is protected by password. If you want to edit the content of the document, you need the password.

Protect Word document
Protect Word document
Password protected
Password protected

What you need to do is to save the document as html format

Save as html
Save as html

 

Open html by using Notepad and delete the password inside of <w:UnprotectPassword> section.

Remove password protection
Remove password protection

Re-open the document by using MS Word. You can freely edit the Word document without inputting any password.

 

Cyclic Redundancy Check (CRC) in C#

Microsoft .Net Framework is so powerful, and it provides so many common libraries to use. However, recently I am working on a legacy project to work out CRC32, and I can find no where. OK, I have to DIY. If you need it by chance, here are the source code.

/// 
/// Calculates a 32bit Cyclic Redundancy Checksum (CRC) using the
/// same polynomial used by Zip.
/// 
public class CRC32 : HashAlgorithm
{

    private static readonly UInt32[] crc32Table =
    {
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
	0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
	0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
	0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
	0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
	0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
	0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
	0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
	0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
	0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
	0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
	0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
	0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
	0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
	0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
	0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
	0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
	0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
	0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
	0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
	0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
	0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
	0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
	0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
	0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
	0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
	0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
	0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
	0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
	0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
	0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
	0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
	0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
	0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
	0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
	0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
	0x2d02ef8d
    };

    private UInt32 hash;
    private UInt32 seed;
    private const UInt32 DefaultSeed = 0xffffffff;

    /// 
    /// Construct an instance of the CRC32 class, pre-initialising the table
    /// for speed of lookup.
    /// 
    public CRC32()
    {
        this.seed = DefaultSeed;
        Initialize();
    }

    public override int HashSize
    {
        get
        {
            return 32;
        }
    }

    public CRC32(UInt32 seed)
    {
        this.seed = seed;
        Initialize();
    }

    protected override byte[] HashFinal()
    {
        hash = ~hash;
        this.HashValue = new byte[] {
						(byte)((hash>>24) & 0xff),
						(byte)((hash>>16) & 0xff),
						(byte)((hash>>8) & 0xff),
						(byte)(hash & 0xff)
						};
        return HashValue;
    }

    protected override void HashCore(byte[] buffer, int start, int length)
    {
        for (int i = start; i>8) 
                          ^ crc32Table[(buffer[i]) ^ ((hash) & 0x000000FF)];
            }
        }
    }

    public override void Initialize()
    {
        hash = seed;
    }
}

Domino’s Pizza $5.95 mobile ordering site exploit

Domino’s Pizza has new mobile ordering site, and any value or traditional pizza only $5.95 each pick up.
This deal is for mobile user only. It will re-direct non-mobile user to normal online ordering site, and the price jumps up to $7.95.

From technical perceptive, how does Domino’s server determine a mobile user? Normally a web request contains “User-Agent” to help web server tell who is visiting. This is a typical example of web request. If we change the “User-Agent” content, we can cheat domino’s web server and order $5.95 pizza.

GET / HTTP/1.1
Host: www.dominos.com.au
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive

OK, let’s do it!

  1. Download user agent switcher for your Firefox and install.
  2. Change user agent to iPhone
  3. Star ordering from http://dominos.com.au/mobile.aspx and enjoy $5.95 pizza

 

Zipping the download files on the fly ASP.Net (real-time zip and download file from server)

I am currently working on an ASP.Net web project, which allows client to download a well structured submission package. Due to security reason, web server can not create folder and write file on client side. Web server can generate a well structured zipped file and push to the client.

There could be two ways to achieve that;

Fist way:

  1. Create folders and copy files into each folder
  2. Use 3rd party tool, eg winrar, 7zip to zip the whole folder to one zipped file
  3. Send back download web address, and let client download the zipped file

If the zipped file is small and web server is powerful enough, client won’t feel any delay. However if the file is big, client side will get no any response until zip file is ready. We can put similar message, eg “Please wait… ”. But client side still don’t know how long would take. If zip file is not ready within time out period, server will get reset. This is a really awful user experience.

Second way:

  1. Create folder and zip the files on the fly (on web server’s memory)
  2. Send back zipped stream to client through Response
  3. Client download  zipping file one by one

Client can clearly feel the progress of downloading.

Here is the code for 2nd solution:

//prepare response header
Response.Clear();
//set Response Content Type
Response.ContentType = "application/x-zip-compressed";
Response.AddHeader("Content-Disposition", "attachment; filename=Download.zip");
ZipOutputStream zipOutput = new ZipOutputStream(Response.OutputStream);
try
{
            zipOutput.IsStreamOwner = false;
            //set compress level
            zipOutput.SetLevel(9);

            ArrayList fileList = functions.GetFileList();
            foreach (string fileName in fileList)
            {
                string folderName = "folder1" + @"";

                zipEntry = new ZipEntry(folderName + fileName);
                zipOutput.PutNextEntry(zipEntry);
                            
                byte[] file = functions.GetFile(fileName);
                //zip file
                zipOutput.Write(file, 0, file.Length);
                 //send to client for downloading
                Response.Flush();
            }
        }
        catch (Exception ex)
        {
            Response.Write(ex.ToString());
        }
        finally
        {
            zipOutput.Finish();
            zipOutput.Close();
            Response.End();
        }