Tuesday, January 25, 2011

ILMerge to combine multiple DLLs

I have a solution with several projects and some third party DLLs referenced on the side. One of the projects is a sample windows forms application and will be provided to the client as is (including source). For the other projects the client will only be given files to reference from their projects.

ILMerge is a command line tool that can be called as a post-build step to combine the bin output of all the Class Library projects into a single DLL (and PDB).

I added a post-build step to the last class library that gets built before the forms application. This combines all the library DLLs into a single file that is then referenced by the forms application. Note that I put the ILMerge.exe in a path within the solution.

..\..\..\References\ILMerge\ILMerge.exe /out:..\..\..\References\SingleFileToBeReferencedBySampleFormsApp.dll
 ClassLibrary1Output.dll ClassLibrary2Output.dll ClassLibrary3Output.dll ThirdPartyDotNetLibrary.dll

Friday, January 21, 2011

Creating ico (icon) files with Paint.NET

Natively Paint.NET doesn't support icon files (.ico). Adding a third party plugin ICO Plugin by Evan Olds adds support.

The IcoCur.dll application extension should be stored in the Paint.NET/FileTypes directory. See Icon, Cursor, and Animated Cursor Format v3.7 (May 2010)

Monday, January 17, 2011

Chrome "Resource interpreted as stylesheet but transferred with MIME type text/plain."

Chrome (8.0.522.237) isn't rendering a linked CSS file from an ASP.NET WebResource.axd (an embedded resource).

The following warning appears in Developer Tools - Resources:

Resource interpreted as stylesheet but transferred with MIME type text/plain.

In this case the issue was with the WebResource reference in the AssemblyInfo.cs

[assembly: WebResource("SampleNamespace.SampleCss.css", "text-css", PerformSubstitution = true)]

Should have been:

[assembly: WebResource("SampleNamespace.SampleCss.css", "text/css", PerformSubstitution = true)]

Tuesday, January 11, 2011

Removing superstition with brute force - 5 fridays, saturdays and sundays in a month

Came across the following Facebook status update and thought it would be a simple thing to prove wrong.

I'm not superstitious, but this year July has 5 Fridays, 5 Saturdays and 5 Sundays.
This happens once every 823 years.

Or the alternative version:

Money bags

This year, July has 5 Fridays, 5 Saturdays and 5 Sundays. This happens
once every 823 years. This is called money bags. So, forward this to
your friends and money will arrive within 4 days. Based on Chinese
Feng Shui. The one who does not forward.....will be without money. 
  static void Main(string[] args)
  {
   int occuranceCount = 0;

   // Change to suit month of interest (1 to 12)
   int monthToTest = 10;

   for (int year = 1; year <= 2011; year++)
   {
    int fridayCount = 0;
    int saturdayCount = 0;
    int sundayCount = 0;

    for (int dayOfMonth = 1; dayOfMonth <= DateTime.DaysInMonth(year, monthToTest); dayOfMonth++)
    {
     DateTime dateToTest = new DateTime(year, monthToTest, dayOfMonth);
     if (dateToTest.DayOfWeek == DayOfWeek.Friday) { fridayCount++; }
     if (dateToTest.DayOfWeek == DayOfWeek.Saturday) { saturdayCount++; }
     if (dateToTest.DayOfWeek == DayOfWeek.Sunday) { sundayCount++; }
    }

    if (fridayCount == 5 && saturdayCount == 5 && sundayCount == 5)
    {
     occuranceCount++;
     Console.WriteLine(occuranceCount + " " + year);
    }

    
   }
   Console.ReadLine();
  }

Recent July's that meet the criteria: 2011, 2005, 1994, 1988, 1983, 1977, 1966, 1960, 1955, 1949, 1938, 1932, 1927, ...

This is the only time you see this phenomenon in your life.

Calendar August 2014  
 
August, next year, will have 5 Fridays, 5 Saturdays and 5 Sundays.  This happens only once every 823 years.  The Chinese call it 'Silver pockets full.  " So: send this message to your friends and in four days money will surprise you.  Based on Chinese Feng Shui.  Whoever does not transmit the message ... may find themselves poor
 
I obeyed (you never know).     

Recent August's that meet the criteria: 2008, 2003, 1997, 1986, 1980, 1975, 1969, 1958, 1952, 1947, 1941, 1930, 1924, ...

Thursday, December 16, 2010

Sending a Sitecore MediaStream through a HttpResponse

The following code can be used to stream Sitecore media back to the client without using the configured MediaRequestHandler.

 public class MediaDownload
 {
  public static void ProcessRequest(Page page, Media media)
  {
   Assert.ArgumentNotNull(page, "page");
   Assert.ArgumentNotNull(media, "media");
   if (!DoProcessRequest(page, media))
   {
    page.Response.StatusCode = 404;
    page.Response.ContentType = "text/html";
   }
  }

  private static bool DoProcessRequest(Page page, Media media)
  {
   Assert.ArgumentNotNull(page, "page");
   Assert.ArgumentNotNull(media, "media");
   
   if (media != null)
   {
    return DoProcessRequestToStream(page, media);
   }

   return false;
  }

  private static bool DoProcessRequestToStream(Page page, Sitecore.Resources.Media.Media media)
  {
   Assert.ArgumentNotNull(page, "context");
   Assert.ArgumentNotNull(media, "media");
   if (Modified(page, media) == Tristate.False)
   {
    SendMediaHeaders(media, page);
    page.Response.StatusCode = 304;
    return true;
   }
   MediaStream stream = media.GetStream();
   if (stream == null)
   {
    return false;
   }
   
   SendMediaHeaders(media, page);
   SendStreamHeaders(stream, page);
   DownloadHeaders.AddDownloadResponseHeader(page.Response, media.MediaData.MediaItem.Name, media.MimeType, media.Extension);
   using (stream)
   {
    WebUtil.TransmitStream(stream.Stream, page.Response, Settings.Media.StreamBufferSize);
   }
   return true;
  }

  private static Tristate Modified(Page page, Sitecore.Resources.Media.Media media)
  {
   DateTime time;
   string ifNoneMatch = page.Request.Headers["If-None-Match"];
   if (!string.IsNullOrEmpty(ifNoneMatch))
   {
    return Tristate.True;
   }
   string ifModifiedSince = page.Request.Headers["If-Modified-Since"];
   if (!string.IsNullOrEmpty(ifModifiedSince) && DateTime.TryParse(ifModifiedSince, out time))
   {
    return MainUtil.GetTristate(time != media.MediaData.Updated);
   }
   return Tristate.Undefined;
  }

  

  private static void SendMediaHeaders(Sitecore.Resources.Media.Media media, Page page)
  {
   DateTime updated = media.MediaData.Updated;
   if (updated > DateTime.Now)
   {
    updated = DateTime.Now;
   }
   HttpCachePolicy cache = page.Response.Cache;
   cache.SetLastModified(updated);
   cache.SetETag(media.MediaData.MediaId);
   cache.SetCacheability(Settings.MediaResponse.Cacheability);
   TimeSpan maxAge = Settings.MediaResponse.MaxAge;
   if (maxAge > TimeSpan.Zero)
   {
    if (maxAge > TimeSpan.FromDays(365.0))
    {
     maxAge = TimeSpan.FromDays(365.0);
    }
    cache.SetMaxAge(maxAge);
    cache.SetExpires(DateTime.Now + maxAge);
   }
   Tristate slidingExpiration = Settings.MediaResponse.SlidingExpiration;
   if (slidingExpiration != Tristate.Undefined)
   {
    cache.SetSlidingExpiration(slidingExpiration == Tristate.True);
   }
   string cacheExtensions = Settings.MediaResponse.CacheExtensions;
   if (cacheExtensions.Length > 0)
   {
    cache.AppendCacheExtension(cacheExtensions);
   }
  }

  private static void SendStreamHeaders(MediaStream stream, Page page)
  {
   stream.Headers.CopyTo(page.Response);
  }


 }

Wednesday, December 1, 2010

Using a DllImport referenced DLL indirectly from a TestTools.UnitTesting.TestClass

Project A uses DllImport to invoke code in a third party DLL. Project B uses Microsoft.VisualStudio.TestTools.UnitTesting to run some test cases on Project A.

The automated tests in project B were failing as the DLL required by project A wasn't being copied into the TestResults directory used by the tests.

Adding the DeploymentItem attribute to the test classes in Project B with the path to the DLL revolved the issue.

Friday, November 19, 2010

Invoking a public Salesforce Web Service Method from a force.com site

Typically when dealing with a Salesforce hosted web service you need to first authenticate to the login site to get a sessionId and Url of the server that will process the requests.

It turns out that it is possible to invoke a web service without this authentication step provided it is configured in a certain way.

Step 1

Mark the class as global and the web service as public. E.g.

 /* 
 * Test to see if the web service can be connected to and used 
 * without having to log in as there is no api sObject use here.
 */
global class HelloWorld
{
    WebService public static String GetMessage()
    {
        return 'Hello world';
    }
}

Step 2

In Salesforce, it is important to give public access to the class (Adding the class under Settings > Develop > Sites > Public Access Settings)

Step 3

Download and import the WSDL from the Apex class.

Step 4

Once you have imported the WSDL you will need to use the public URL of the web service rather than the standard version which requires a session to be established first.

E.g. The domain will change to use the a pattern like:

https://<yourcompanyinsites>.<sandboxname>.<sandbo​xinstance>.force.com/<sitename>
https://<yourcompanyinsites>.secure.force.com/<sitename>

Step n+1

One of the key aspects when calling it from C# is setting ServicePointManager.Expect100Continue before creating the web service proxy. E.g.

System.Net.ServicePointManager.Expect100Continue = false;

See: