Thursday, December 26, 2013

Exception : Path is too long - RESOLVED


While working on a problem today, we were getting following exception. I agree that we were using very long path but we can't do anything with folder structure. It was a client share.

Exception:

The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.

   at System.IO.Path.SafeSetStackPointerValue(Char* buffer, Int32 index, Char value)
   at System.IO.Path.NormalizePathFast(String path, Boolean fullCheck)
   at System.IO.Path.NormalizePath(String path, Boolean fullCheck)
   at System.IO.Path.GetFullPathInternal(String path)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access)
     at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()


After searching on internet, I came to know that standard .NET API doesn't support that. Hence we moved  back to COM or WinAPI era i.e. full power to play with system. One of the API that suit our requirement is : 
CopyFile

This is how we fixed our issue:

1. Copy respective file using above API to a temporary location.
2. Performed required operations.
3. Delete file from temporary location.

As we have a .NET based application, following is the way to use this API:

Step 1:

Declare P/I invoke statement

 [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]

        static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);

where 

lpNewFileName - New path including file name.

lpExistingFileName - is a LONG file path including file name.  It can be of two types:

long path is always prefixed with : "\\?\"  when using above API.

a) In case of local drive:  \\?\D:\very long path

b) In case of network drive:

"\\?\" prefix is used with paths constructed according to the universal naming convention (UNC). To specify such a path using UNC, use the "\\?\UNC\" prefix. For example, "\\?\UNC\server\share", where "server" is the name of the computer and "share" is the name of the shared folder. These prefixes are not used as part of the path itself. They indicate that the path should be passed to the system with minimal modification, which means that you cannot use forward slashes to represent path separators, or a period to represent the current directory, or double dots to represent the parent directory. Because you cannot use the "\\?\" prefix with a relative path, relative paths are always limited to a total of MAX_PATH characters.

Example:
\\?\UNC\serverXYZ\Myfoldershare\very long path

Step 2:

Call above API as normal method.

 CopyFile(reallyLongPath, destination, false);

That's it.  File will copy to your destination path. Now, you can play as needed.

Note
Many but not all file I/O APIs support "\\?\"; you should look at the reference topic for each API to be sure.









Tuesday, September 3, 2013

Impersonation in Console/Windows Application in same and cross domain


After Go live of any application, there are issues which occurred for certain users sometimes. To nail down these issues, we need to execute code under that user i.e. impersonate user identities and run application under his user ID.

In Web application, this is quite straightforward and same can be achieved using web.config using location attribute like:


<location path="<Page/Service Path>">
    <system.web>
      <identity impersonate="true" />
    </system.web>
  </location>


<location path="<Page/Service Path>">
    <system.web>
      <identity impersonate="true" userName="<user Name>" password="<Password>"/>
    </system.web>
  </location>

Now, question comes, how to achieve same in console/Windows application if needed. so.. Here you go:

For impersonation in Console/Windows application, best and simple way is to use P/I invoke.  Following sample you can use for same:

Add following method at Class level:

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);  

In your method:

Same Domain

IntPtr tokenHandle = IntPtr.Zero;
            bool returnValue = LogonUser("<User Name>", "<Domain>", "<Password>", 2, 0, ref tokenHandle); 
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
            {
// Add code to be executed under that user
}


Cross Domain:

This is little tricky. Follow given steps: 

1. Create class Impersonation.cs

2. Add following P/I invokes and enums

[DllImport("advapi32.dll", SetLastError = true)]
        private static extern int LogonUser(
        string lpszUserName,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int DuplicateToken(
        IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(
        IntPtr handle);

        private const int LOGON32_LOGON_INTERACTIVE = 2;
        private const int LOGON32_PROVIDER_DEFAULT = 0;

        enum LogonType
        {
            Interactive = 2,
            Network = 3,
            Batch = 4,
            Service = 5,
            Unlock = 7,
            NetworkClearText = 8,
            NewCredentials = 9
        }
        enum LogonProvider
        {
            Default = 0,
            WinNT35 = 1,
            WinNT40 = 2,
            WinNT50 = 3

        }

3. Use following method

        public void ImpersonateUser(
        string userName,
        string domain,
        string password)
        {


            WindowsIdentity tempWindowsIdentity = null;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {
                if (RevertToSelf())
                {
                    if (LogonUser(
                    userName,
                    domain,
                    password,
                    (int)LogonType.NewCredentials,
                    (int)LogonProvider.WinNT50,

                    //LOGON32_LOGON_INTERACTIVE,
                        //LOGON32_PROVIDER_DEFAULT,
                    ref token) != 0)
                    {
                        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                        {
                            tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                            impersonationContext = tempWindowsIdentity.Impersonate();
                        }
                        else
                        {
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (token != IntPtr.Zero)
                {
                    CloseHandle(token);
                }
                if (tokenDuplicate != IntPtr.Zero)
                {
                    CloseHandle(tokenDuplicate);
                }
            }
        }


4. Create a instance of class as given below and add your code segment 

using (Impersonator imperso = new Impersonator("<userName>","<domain>","Password"))
{
 //Add your code here

}


That's it! ... You are all set to execute your code section under that user.

Thanks!

Friday, May 10, 2013

ActiveX Control not working .Net 4.5


We recently moved our application from .Net 4.0 to .Net 4.5. After that, our few component which were based on ActiveX component stopped working IE.

After a lot research , we found a solution. As with launch of .Net, Hosting managed controls inside IE is no longer supported out of the box.

Further Details : http://msdn.microsoft.com/en-us/library/hh367887.aspx

Here is one way that you can use if you want to load ActiveX Control in IE 10. 

Solution:


There is registry key called as "EnableIEHosting". This key needs to be added at two location:

  • HKLM\SOFTWARE\MICROSOFT\.NETFramework
  • HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework

Please create key of type "dword" and set its value to 1 e.g.

"EnableIEHosting"=dword:00000001

Thats it! You ActiveX controls are all set to load in IE 10 on any Windows operating system with .NET 4.5

You can create these entries either using code or simple run a batch file. 

Create Batch File :


REGEDIT4

; @ECHO OFF
; CLS
; REGEDIT.EXE /S "%~f0"
; EXIT

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
"EnableIEHosting"=dword:00000001


[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]
"EnableIEHosting"=dword:00000001

Pause

Copy this content and save it in file ".bat" extension.  No code needed :)

Hope it helps!





jQuery Autocomplete with image


Recently, we were working on requirement where we need a search result like "facebook friend search" which returns in following format:















To get it working, we used jQuery's Autocomplete feature. Three steps to implement same.

1. A Text box.

2. You need data to display. If you are searching using a service, you can  call using Ajax that returns a set of results and bind it.

3. Display in following a way is tricky :)  i'll explain in detail as given below.


Lets start :

Step 1. Create a Text-box as follows:

<body>
  <input id="friends" />
</body>

Step 2. Call service using Ajax and prepare data-set to bind.


  $("#friends").autocomplete({
                source: function (request, response) {

                    $.ajax({

                        url: '<Service Url>',
                        type: "GET",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8",
                        success: function (data) {
                            response($.map(data.Data, function (item) {
                                return {
                                    Name: item.Name,
                                    ImageUrl: item.ImageUrl
                                }
                            }));
                        }
                    });
                },
                minLength: 2,
                select: function (event, ui) {
                    log(ui.item ?
"Selected: " + ui.item.label :
"Nothing selected, input was " + this.value);
                },
                open: function () {
                    $(this).removeClass("ui-corner-all").addClass("ui-corner-top");
                },
                close: function () {
                    $(this).removeClass("ui-corner-top").addClass("ui-corner-all");
                }
            });

Notesource method is populated with results returned by service call. We have name and image url  there. It creates a a list of data as needed.

We can also assign some static data if needed like:


$(function() {
    var availableTags = [
      "x",
      "y",
      "y",
      "z"
    ];
    $( "#friends" ).autocomplete({
      source: availableTags
    });


Step 3: Create result like Facebook i.e. Image with text.

jQuery's data attribute and renderItem function finish that job for us.it can be used to create custom html in response flowing out as a result. We can apply required CSS as needed.


 $("#friends").autocomplete({
                source: function (request, response) {

                    $.ajax({
                        url: '<Service Url>',
                        type: "GET",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8",
                        success: function (data) {
                            response($.map(data.Data, function (item) {
                                return {
                                    Name: item.Name,
                                    ImageUrl: item.ImageUrl
                                }
                            }));
                        }
                    });
                },
                minLength: 2,
                select: function (event, ui) {
                    log(ui.item ?
"Selected: " + ui.item.label :
"Nothing selected, input was " + this.value);
                },
                open: function () {
                    $(this).removeClass("ui-corner-all").addClass("ui-corner-top");
                },
                close: function () {
                    $(this).removeClass("ui-corner-top").addClass("ui-corner-all");
                }
            }).data( "autocomplete" )._renderItem = function( ul, item ) {
        var inner_html = '<a><div class="list_item_container"><div class="image"><img src="' + item.ImageUrl + '"></div><div class="label">' + item.Name + '</div></div></a>';
        return $( "<li></li>" )
            .data( "item.autocomplete", item )
            .append(inner_html)
            .appendTo( ul );
    };
});


Thats it! Task done.. Thanks to jQuery and its power.. simply awesome.

Monday, March 18, 2013

Cache Multiple Versions of a user control using VaryByParam, VaryByControl ,and VaryByCustom


Recently, I was working on optimization of application performance where we were using some user control which was taking a lot of time to load. It contains a user role based information as authenticated and authorized users are able to see some links and portion of pages.

Initially, I thought, as it is user specific storage, storing Session will be good idea but didn't find any such way to store data in Session like OutputCache. Hence decided to use "User specific Control Cache" like Session.

Here are the following ways that we can achieve it:

VaryByParam:

VaryByParam works based on parameters passed in URL's query string. It gets a list of query string or form POST parameters that the output cache will use to vary the user control.

<%@ OutputCache Duration="120" VaryByParam="PageTitle" %>

Example and few points to note:

1. If you url is http://www.xyz.com?PageTitle=Home, it will cache control for that patricular url for specified duration.

2. If you move to anyother page like "Contact Us", it will cache different version of same user control.

3. If user has multiple parameters in query string/Form posting, again multiple version of control are cached. E.g if you want to cache control based on User ID, add parameter as follows:

<%@ OutputCache Duration="120" VaryByParam="PageTitle;UserID" %>

Note
You can pass authenticated token in query string as a part of UserID, as SessionID/ other user PII information is not recommended to pass in URL as a plain text.


VaryByControl:

You can cache multiple versions of a user control by simply declaring it in an .aspx file more than once. As with user controls that are not cached, you can include a cached user control in an ASP.NET page as many times as needed for your application. Unless you set the Shared property to true for the user control, multiple versions of the control output will be stored in the cache.

How to create:

1. Create a control that post-backs itself.

2. To cache the user control based on user control properties, you should specify the fully qualified name of the properties in the varyByControls section of the PartialCachingAttribute. Multiple properties if any should be separated by semi-colons.


<%@ Control Language="C#" AutoEventWireup="true" 
CodeFile="WebUserControl.ascx.cs" 
Inherits="WebUserControl" %>
<%@ OutputCache Duration="60" 
VaryByControl="WebUserControl.param1;WebUserControl.param2
VaryByParam="none" Shared="true" %>


or you can also include the PartialCache attribute for the user control:

[PartialCaching(60, null, "WebUserControl.param1;WebUserControl.param2", null, true)]
public partial class WebUserControl : System.Web.UI.UserControl
{
    public string param1 { get; set; }
    public string param2 { get; set; }
}


OR another way to cache the control on the combination of both values would be:

[PartialCaching(60, null, "WebUserControl.BothParams", null, true)]
public partial class WebUserControl : System.Web.UI.UserControl
{
    public string param1 { get; set; }
    public string param2 { get; set; }

    public string BothParams    
    {
        get { return String.Concat(param1, param2); }
    }

}

The last parameter (true) specifies shared. Duration is specified by 60. Refer to the link How to: Cache Multiple Versions of a User Control Based on Parameters

Assign it in the user control code behind:




[PartialCaching(60, null, "WebUserControl.BothParams", null, true)]
public partial class WebUserControl1 : System.Web.UI.UserControl
{
    ...
    protected void Page_Load(object sender, EventArgs e)
    {
        this.CachePolicy.Duration = new TimeSpan(0, 0, 60);
    }    
}
You can assign it in the code behind of the page where user control is referenced using the ID of the user control.

e.g. If the user control on the aspx is:

<mycontrols:control1 ID="ucControl1" runat="server" param1="15" param2="20" />

then in the code behind of aspx, you should write:

this.ucControl1.CachePolicy.Duration = new TimeSpan(0, 0, 60);


NOTE
if both the user control and page are cached: If the page output cache duration is less than that of a user control, the user control will be cached until its duration has expired, even after the remainder of the page is regenerated for a request. For example, if page output caching is set to 50 seconds and the user control's output caching is set to 100 seconds, the user control expires once for every two times the rest of the page expires.


VaryByCustom:

This one is my favorite. You can play with customized values. The key of creating a custom cache variance is understanding that ASP.NET uses a simple string comparison to determine if a cached result should be returned instead of processing the page. For example, say we want to cache a certain page by SessionID. We add the OutputCache directive like this:

<%@ OutputCache Duration=”60” VaryByParam=”None” VaryByCustom=”SessionID” %>


Now, in global.asax, we must override the GetVaryByCustomString method, like this:


Public override string GetVaryByCustomString(HttpContext context, string arg) 

  if(arg.ToLower() == “sessionid”) 
  { 
    HttpCookie cookie = context.Request.Cookies[“ASP.NET_SessionID”]; 
    if(cookie != null) 
      return cookie.Value; 
  } 
  return base.GetVaryByCustomString(context, arg); 
}

In case of multiple keys, you can supply multiple values separated by ';' and handle same in GetVaryByCustomString method as given below:

<%@ OutputCache Duration=”60” VaryByParam=”None” VaryByCustom=”SessionID;Key1” %>


public override string GetVaryByCustomString(HttpContext context, string custom)
        {
            string[] keys = custom.Split(new char[] { ';' });
            string result = string.Empty;
         
            foreach (string key in keys)
            {

                switch (key)
                {
                    case "Key1":
                        //Add page Url
                        result = //Add Key1 logic.
                        break;
                    case "SessionID":
                          HttpCookie cookie = context.Request.Cookies["ASP.NET_SessionID"];
                        result += cookie.Value;
                        break;
                }
            }

            if (!string.IsNullOrEmpty(result))
            {

                return result;
            }
            else
            {

                return base.GetVaryByCustomString(context, custom);
            }
        }



That’s it. Simple, elegant, beautiful :)


Wednesday, March 6, 2013

System.Web.HttpException: The remote host closed the connection. The error code is 0x80072746

Today we got an error when we are downloading huge file like in 1GB+ via http. Existing code was working fine for small sized file.

Exception:

System.Web.HttpException: The remote host closed the connection. The error code is 0x80072746
 at System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.FlushCore(Byte[] status, Byte[] header, Int32 keepConnected, Int32 totalBodySize, Int32 numBodyFragments, IntPtr[] bodyFragments, Int32[] bodyFragmentLengths, Int32 doneWithSession, Int32 finalStatus, Boolean& async)
   at System.Web.Hosting.ISAPIWorkerRequest.FlushCachedResponse(Boolean isFinal)
   at System.Web.Hosting.ISAPIWorkerRequest.FlushResponse(Boolean finalFlush)
   at System.Web.HttpResponse.Flush(Boolean finalFlush)
   at System.Web.HttpResponse.Flush()


This exception generally happens when you have download functionality in your Asp.net applications and when user starts the download process, but, the download process does not complete for one of the following reasons:
  • User cancels the download
  • User navigates to another page
  • User closes the page
  • The connection closes


We have a file download page that allows the user to download files of different sizes varying from few KBs to GBs. We also have multiple files bundled into a single .zip file.


Root Cause:

Default value of Response.Buffer  property is set to "true" and hence the Asp.net process writes data/output to the buffer, instead of the output stream. When the page is finished processing, the buffered output is flushed to the output stream and hence, sent to the browser. This is usually done to avoid multiple data transfer process over the network and hence optimize performance by sending the overall page output in a single Response.

But, when a page writes a file content (Or, a file) in the Response, usually, we used to flush the buffer immediately after we write the file content in the output stream, so that, the file content is sent to browser immediately, instead of waiting for the page to complete its' execution process process. In general, following is the code that we usually use.

Response.OutputStream.Write(buffer,0,buffer.length);
Response.Flush();
Response.End();

Now :

  • User started to download the file from the download prompt (As a result of the Response.OutputStream.Write()), data is stored in buffer and the buffer is overflown and a stream of binary data started to flow from server to client.
  • As file is large, based on executiontime configured in web.config, response timeout or the download process is cancelled.
  • Meanwhile, the Asp.net process tries to Flush() the buffer content to the output stream, that is already closed by the client.
  • Exception occurs.



Solution:

Buffering response at server may cause system degradation which can be worsen when multiple users are trying to hit files simultaneously. Hence just set

Response.Buffer = false;

Further, if you have large file like in GB, it is good idea not to buffer it for better performance. Just open the stream and start writing to response stream. As we are not buffering at server level, it directly opens to client and file downloaded started. Till now, I've successfully downloaded 1.5 GB file from server to client but I'm sure, this will work for jombo files too.


var resp = HttpContext.Current.Response;
        resp.Buffer = false;
 resp.AddHeader("Content-Disposition", "attachment; filename=test.txt");
            resp.ContentType = "application/zip";
            string strShowMsg = string.Empty;
             long start=0;

     using (FileStream rstream = new FileStream(@"C:\DownloadHuge.txt", FileMode.Open))
             {
                 int totalbytes = 0;

                 byte[] buffer = new byte[256];
                 int x = rstream.Read(buffer, 0, 256);
                 while (x > 0)
                 {
                     resp.OutputStream.Write(buffer, 0, x);                     
                     totalbytes += x;
                     x = rstream.Read(buffer, 0, 256);
                 }
            
             }

             resp.Flush();
             resp.End();

Although, this solution seems simple but it took a lot of time to figure out this issue.

Friday, March 1, 2013

Download large file using WCF


Recently, we were facing issues when we need to download more than 2GB file like 4GB, 5GB etc from WCF service.

It worked till 2GB by default because max value permitted in maxReceviedBufferSize=int32.MaxValue which is 2147483647 (1.99 GB). What if we need to download file more than 2GB??  If you try, you will get following error:

"The maximum message size quota for incoming messages (2147483647 ) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element"

After searching on internet, following options suggested but none worked:

1. Only change transfermode to Stream.
2. Set Max value of all elements like

<binding name="BasicHttpBinding_Service" messageEncoding="Mtom" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" transferMode="Streamed"  useDefaultWebProxy="true" sendTimeout="05:00:00" closeTimeout="05:00:00"/>

Finally, I got a way to work after tweaking the client config as follows:

<binding name="BasicHttpBinding_Service" messageEncoding="Mtom" maxReceivedMessageSize="4294967294" maxBufferPoolSize="65536" maxBufferSize="65536" transferMode="Streamed"  useDefaultWebProxy="true" sendTimeout="05:00:00" closeTimeout="05:00:00"/>

If you note here, we have change following property as follows:
1. maxReceivedMessageSize="4294967294"  which is 4 GB
2.  maxBufferPoolSize = "65536" which is 64 KB
3. maxBufferSize="65536" which is 64 KB
4. transferMode = "Streamed"

Restricting the maximum incoming message size is not enough in this case. The MaxBufferSize property is required to constrain the memory that WCF buffers. It is important to set this to a safe value (or keep it at the default value) when streaming. For example, suppose your service must receive files up to 4 GB in size and store them on the local disk. Suppose also that your memory is constrained in such a way that you can only buffer 64 KB of data at a time. Then you would set the MaxReceivedMessageSize to 4 GB and MaxBufferSize to 64 KB. Also, in your service implementation, you must ensure that you read only from the incoming stream in 64-KB chunks and do not read the next chunk before the previous one has been written to disk and discarded from memory

Note
1.As it opens Stream to your client, pay a special attention about writing this stream. I would recommend not to  use MemoryStream to consume same because it is a large file and use of MemoryStream will kill you box and effect system performance. If you are using a web application, multiple users can try to download same file which can exhaust your box.

2. As a solution, download file to a temporary location and play as per your need for better for performance.

3. If you have any requirement to download more than 4 GB, you can change to maxReceivedMessageSize value as per your need and let other values unchanged.

Hope this helps who have similar type of problem. I'll be happy to listen your feedback/comment that can help me to improve.

Thanks!
Tarun