Amazon.com Widgets WilliaBlog.Net | I dream in code

WilliaBlog.Net

I dream in code

About the author

Robert Williams is an internet application developer for the Salem Web Network.
E-mail me Send mail
Code Project Associate Logo

Recent comments

Archive

Authors

Tags

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.


WINDOWS 7: Explorer Locks "thumbs.db" in most recently viewed folder

Problem

On windows 7, you navigate to a folder that contains images or video files that you have either added new content to or have simply never explored before, if you then back out of that folder and try to delete it, you are warned that "The action can't be completed because the file is open in Windows Explorer. Close the file and try again. Thumbs.db":

Clicking Try Again any number of times will not resolve the issue. All of the files within that folder (except Thumbs.db) do still get deleted. Usually, if you navigate to another folder for which a new Thumbs.db file is generated, then often the original problem folder can be deleted (though the most recently explored one can not), but sometimes a reboot is required. Even switching to another PC and trying to delete the folder through the network doesn't work. This was not an issue with Windows Vista.

Solution

It turns out that Windows 7 no longer relies on hidden Thumbs.db files to manage its thumbnails - that's now done with a central thumbnail cache - and it only generates these db files for backward compatability purposes. So, in my opinion, the best solution is to stop using Thumbs.db files entirely. Just to be clear, this does not mean that you will no longer see thumbnails of images and videos in windows explorer. Thumbnails will still be generated and your Windows Explorer experience as a user will be unchanged (except that you will be able to delete your folders more freely because these pesky thumbs.db files will no longer be an issue).

There are a number of ways to implement this fix and I believe they all require a reboot for the change to take effect. If you have a version Windows 7 that allows you to edit group policies (Windows 7 Professional, Ultimate or Enterprise) you can follow these steps to resolve the problem:

·    Click the Start orb

·    Enter gpedit.msc in the search box and hit Enter.

·    Expand User Configuration - Administrative Templates - Windows Components. 

·    Click on Windows Explorer.

·    Right-click the entry "Turn off the caching of thumbnails in hidden thumbs.db files" and choose Edit.

·    Enable the setting.

If you have Windows 7 Starter, Home Basic or Home Premium, or just want a quicker solution here's the .reg file equivalent of the group policy fix above.

(Paste the text below into Notepad, save it with a .reg extension, and then double-click the file.)


Windows Registry Editor Version 5.00

; Enables group policy: "Turn off the caching of thumbnails in hidden thumbs.db files"

[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Explorer]
"DisableThumbsDBOnNetworkFolders"=dword:00000001


Alternatively you could download a .reg file I made earlier, and simply double click on it:

ThumbsDbFix.reg (244.00 bytes)

Or:

Bring up a (administrator) CMD prompt and type (or paste):

  REG ADD "HKCU\Software\Policies\Microsoft\Windows\Explorer" /v "DisableThumbsDBOnNetworkFolders" /t REG_DWORD /d 1 /f

And you're done.


Tags:
Posted by Williarob on Wednesday, March 14, 2012 11:48 AM
Permalink | Comments (0) | Post RSSRSS comment feed

visualsvn server "'repositories' is not a valid short filename"

Problem

I had been trying to upgrade my standard edition Visual SVN Server for months now. Every time I received the email notification of a new version I would download the update, run it and the install would fail with the error "'repositories' is not a valid short filename". Google helped me find a handful of people seeing the same error, but no solutions. Ever hopeful that the next release would not have this issue, I tried again today with the latest version but the same thing happened. Then I had an idea:

Solution

Before you run the installer, open VisualSVN Server Manager and click the stop button. Then the install will complete without a hitch. Why the installer doesn't stop the service for you before it tries to install is something only the VisualSVN Team can answer.


Categories: SVN
Posted by Williarob on Monday, March 12, 2012 12:45 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Generic AddOrUpdate Extension for IDictionary (C#)

I recently came across a situation where the properties of a class were adding the value to an internal dictionary of key value pairs which made it easy to loop through and return a JSON string. This worked great until we later ran into a situation where we needed to update a property after it had already been set, which of course generated a "System.ArgumentException: An item with the same key has already been added". While it is easy enough to add a check to see if the dictionary already contains the key you are attempting to add, doing this for every property would be way too much code repetition so an Extension Method seemed appropriate.

Add the following extenstion method to a static class in your solution, then add that namespace to the list of namespaces you are "using" at the top of any class that might need it, and your Dictionary will have a .AddOrUpdate(key, value) method:

        public static IDictionary<TKey,TValue> AddOrUpdate<TKey,TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
        {
            if(dictionary.ContainsKey(key))
            {
                dictionary[key] = value;
            }
            else
            {
                dictionary.Add(key, value);
            }

            return dictionary;
        }

 


Categories: C#
Posted by Williarob on Tuesday, August 30, 2011 8:54 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Printable HBO Game of Thrones Viewer's Guide

Like many people, after thoroughly enjoying the first season of Game of Thrones on HBO, my wife and I started reading the books. Even with the show fresh in our minds, we found ourselves getting confused Jory/Jorah, Tyrion/Tywin so we went online looking for a nice cheat sheet. If you haven't already been there, take a look at the official HBO viewer's guide. It's presented beautifully, and easy to follow. As a web developer, I can appreciate the hard work that went into this great site.

However, thinking that we didn't want to keep going online to check every time we needed to, we thought it would be great to print it all out. What a mistake that was. All formatting is gone, and this is the result:

With the layout all messed up, the printed pages are useless!

So, with a little help from FireBug for Firefox and Adobe Photoshop, I have created a nice printable version of the site:

Download it here in Adobe PDF Format:

HBO Game of Thrones Guide to Houses.pdf (2.27 mb)


Posted by Williarob on Monday, July 25, 2011 2:10 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Moving Files in Visual Studio and SVN

I'm writing this down because (1) I've had to figure it out a few times already and don't want to figure it out again, and (2) it may save you some snortin' and cussin' if you run across it yourself. It is astonishingly annoying.

If you have an MVC 3 project in Visual Studio 2010 and while refactoring you move a file - for example you move a view model to the shared area, you may suddenly encounter a compile error in some random temporary file like the one below:

The type or namespace name 'xxx' does not exist in the namespace 'xxx' (are you missing an assembly reference?)    c:\Users\[user name]\AppData\Local\Temp\Temporary ASP.NET Files\temp\eab6c63b\948de17e\App_Web_hadnllup.0.cs

Cleaning the solution and/or deleting the temporary files will not resolve the problem. This can happen whether you simply drag and drop the file to the new location, then change the namespace yourself, or if you right click on the file and choose Refactor > Move and have Visual Studio move the file and update the namespaces for you. The latter process (in theory) will update all references to the file within your project automatically, while with the former, you would typically try to compile the project and then fix all the broken items that now appear in the Error list as a result of the namespace change. However, unless you do a global find and replace, you will probably still end up getting the cryptic error above because chances are that the @model declaration in one or more of your Views is still pointing to the old namespace for that viewmodel file. Update the view(s) and the error will go away and all will be fine.

Now, if you use SVN, things are a little more complicated: If you move the file using either of the techniques described above, what will happen is SVN will delete the original file and create a new one for the new location, which is fine, unless you were hoping to preserve the full subversion history of that file. If you want to preserve the file history in SVN and move the file, this is how you do it: in Windows Explorer, right-click and drag the file from its old location to its new location, then select "SVN move versioned item" from the context menu. This will not only move the actual file itself, but it will also make sure that all the file history stays with it after you check in your changes. Back in Visual Studio, use the Solution Explorer in VS2010 to "exclude from project" the (now-missing) copy of the file in its old location, and then "include in project" the file in its new location. You may need to refresh the view in solution explorer and/or make sure you are viewing all the files by clicking the "Show all Files" icon at the top (next to the refresh icon) in order to see these files.

After you update the namespace to reflect the new location, I recommend using a global find and replace before you try to compile to save yourself a lot of trouble.

To summarize, if you find yourself getting obsolete, broken references in auto-generated files that you can't permanently delete, look in your Views folder for the bad references.


Posted by Williarob on Wednesday, May 25, 2011 7:33 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Firefox 4 doesn't save tabs

Problem: I've downloaded Firefox 4 and now I am no longer able to save my tabs. How can I fix that?

Solution: By default this function has been disabled. To enable it, do the following:

1. In the address line, type in "about:config" (without quotation marks). Click on "I'll be careful, I promise!"

2. In the filter field, type in browser.showQuitWarning. This should filter out all other options

3. Double click on the option that is left in the list. The value should switch from "false" to "true".

4. Close about:config tab and it should work


Posted by Williarob on Friday, May 06, 2011 1:53 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Using System.Threading.Tasks and BlockingCollections to FTP multiple Files at the same time

I recently needed to write an application that would loop through a queue of files and FTP them to our Content Delivery Network for streaming. Users upload files, and our administrators can mark some of them as urgent. Urgent ones need to jump to the front of the queue, otherwise everything should be orderd by broadcast date. My initial code was basically a loop that looked something like this:

While (GetFtpQueue().Count > 0)
{
    // Gather all the info from the db,  Ftp the file, then clean up (move the source file, update the db, etc.

}

It worked beautifully while we were just uploading small audio files, but as soon asl we started adding a lot of video files to the queue it became so slow that it might take 2 hours or more to upload a single video file. So, we experimented with Filezilla to see how many concurrent uploads we could add before the overall speed of each upload started to drop. We found that at our location, 4 simultaneous FTP uploads seemed to hit the sweet spot: instead of uploading 1 file at 500 kb/s we could upload all four and each one would still be at that speed, quadrupling our throughput.

I read up on using the new Threading classes in .Net 4.0, and began refactoring my FTP application. I decided to use the Task Factory to manage the threads, in conjunction with a BlockingCollection to create a classic Producer/Consumer pattern. My first attempt looked a lot like this:

int maxThreads = 4;
var filesToFtp = new BlockingCollection<FtpItem>(maxThreads);
var processedFiles = new BlockingCollection<FtpItem>();

// Stage #1: Producer
var initFtp = Task.Factory.StartNew(() =>
{
    try
    {
        While (GetFtpQueue().Count > 0)
        {
            // Gather all the info from the db and use it to create FtpItem objects
            // Add them to list of filesToFtp, which only allows maxThreads in at a time (this allows us to have an urgent item jump to the top while current items are still FTPing)
            filesToFtp.Add(new FtpItem { ... };
        }
    }
    finally { filesToFtp .CompleteAdding(); }
});

// Stage #2 Consumer of initFtpTask and Producer for Cleanup Task
var process = Task.Factory.StartNew(() =>

{
    try
    {
        foreach(var file in filesToFtp.GetConsumingEnumerable()
        {
            // Ftp the file
            // Add to list of processedFiles
            processedFiles.Add(file);
        }
    }
    finally { processedFiles.CompleteAdding(); }
});

// Stage #3
var cleanup = Task.Factory.StartNew(() =>
{
    foreach(var file in processedFiles.GetConsumingEnumerable()
    {
        // Clean up (move the source file, update the db, etc.
    }
});

Task.WaitAll(initFtp, process, cleanup);

Initially, this looked quite promising. I wrote a bare bones version of it like the one above that just did thread.sleep to simulate work and iterated through a list of ints. I was ablt to verify that each "stage" was running on it's own thread, that it never allowed more than 4 items through at a time, that I could add items to the front of the queue and get them processed next, and that it never tried to 'cleanup' a file until that file had passed through both stage 1 and stage 2. However, I did notice that the elapsed time was the same as when I ran a similar unit test in a simple while loop. It might be obvious to you why this is, but at the time I put it down to a limitation of the unit test and pushed my new code to production. The first thing I noticed was that it wasn't any faster. Not even slightly. It took me hours of staring at the code to finally figure out why my multi threaded code was not running any faster, but the answer is simple: I only created one consumer of filesToFtp. I had incorrectly assumed that because I was creating up to 4 ftpItems at a time, and the ftp process was running on it's own thread, that it would consume as many as it could, but the reality is that in the code above, while each of the three stages are running on their own thread, the whole process was still happening in series, since stage 1 doesn't create 4 items at once, it creates them one after the other, stage 2 does begin working before stage 1 is complete (as soon as there is an item to consume), but then it will be busy Ftping that first item until that item is fully uploaded, only then will it grab the next file.

To resolve this problem, I simply wrapped stage 2 in a for loop, and created a IList of Tasks to wait on:

int maxThreads = 4;
var filesToFtp = new BlockingCollection<FtpItem>(maxThreads);
var processedFiles = new BlockingCollection<FtpItem>();
IList<Task> tasks = new List<Task>();

// Stage #1: Producer
tasks.Add(Task.Factory.StartNew(() =>
{
    try
    {
        While (GetFtpQueue().Count > 0)
        {
            // Gather all the info from the db and use it to create FtpItem objects
            // Add them to list of filesToFtp, which only allows maxThreads in at a time (this allows us to have an urgent item jump to the top while current items are still FTPing)
            filesToFtp.Add(new FtpItem { ... };
        }
    }
    finally { filesToFtp .CompleteAdding(); }
}));

// Start multiple instances of the ftp process
for (int i = 0; i < maxThreads; i++)
{
    // Stage #2 Consumer of initFtpTask and Producer for Cleanup Task
    tasks.Add(Task.Factory.StartNew(() =>
    {
	try
	{
		foreach(var file in filesToFtp.GetConsumingEnumerable()
		{
			// Ftp the file
			// Add to list of processedFiles
			processedFiles.Add(file);
		}
	}
	finally { processedFiles.CompleteAdding(); }
	}));
}

// Stage #3
tasks.Add(Task.Factory.StartNew(() =>
{
	foreach(var file in processedFiles.GetConsumingEnumerable()
	{
		// Clean up (move the source file, update the db, etc.
	}
}));

Task.WaitAll(tasks.ToArray());

I reran the unit test and it was faster! Very nearly 4 times faster in fact. Wahoo! I updated the code, published my changes and sat back. Sure enough, the Ftp process finally started to make up some ground. In the mean time, I went back to my unit test and began tweaking. The first thing I noticed was that sometimes I would get a "System.InvalidOperationException: The BlockingCollection<T> has been marked as complete with regards to additions." Luckily, this didn't take a lot of head scratching to figure out: the first thread to reach the 'finally' clause of  stage 2 closed the processedFiles collection, leaving the other three threads hanging. A final refactoring resolved the issue:

int maxThreads = 4;
var filesToFtp = new BlockingCollection<FtpItem>(maxThreads);
var processedFiles = new BlockingCollection<FtpItem>();
IList<Task> tasks = new List<Task>();

// maintain a seperate list of wait handles for the FTP Tasks, 
// since we need to know when they all complete in order to close the processedFiles blocking collection
IList<Task> ftpProcessTasks = new List<Task>();

// Stage #1: Producer
tasks.Add(Task.Factory.StartNew(() =>
{
	try
	{
		While (GetFtpQueue().Count > 0)
		{
			// Gather all the info from the db and use it to create FtpItem objects
			// Add them to list of filesToFtp, which only allows maxThreads in at a time (this allows us to have an urgent item jump to the top while current items are still FTPing)
			filesToFtp.Add(new FtpItem { ... };
		}
	}
	finally { filesToFtp .CompleteAdding(); }
}));

// Start multiple instances of the ftp process
for (int i = 0; i < maxThreads; i++)
{
	// Stage #2 Consumer of initFtpTask and Producer for Cleanup Task
	ftpProcessTasks.Add(Task.Factory.StartNew(() =>
	{
		try
		{
			foreach(var file in filesToFtp.GetConsumingEnumerable()
			{
				// Ftp the file
				// Add to list of processedFiles
				processedFiles.Add(file);
			}
		}
	}));
}

// Stage #3
tasks.Add(Task.Factory.StartNew(() =>
{
	foreach(var file in processedFiles.GetConsumingEnumerable()
	{
		// Clean up (move the source file, update the db, etc.
	}
}));


// When all the FTP Threads complete
Task.WaitAll(ftpProcessTasks.ToArray());

// Notify the stage #3 cleanup task that there is no need to wait, there will be no more processedFiles.
processedFiles.CompleteAdding();

// Make sure all the other tasks are complete too.
Task.WaitAll(tasks.ToArray());

Download a working example (Just enter your FTP Server details prior to running):

ProducerConsumer.zip (11.18 mb)


Posted by Williarob on Monday, April 18, 2011 11:47 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Visual Studio C# Statement Collapsing

Many seasoned developers would argue that if your method is so long, and contains so many nested blocks of code in braces that you need to be able to collapse the sections to make better sense of your code, that you should refactor that code! And of course, they are right. This can be done either by moving sections out to new methods, or perhaps inverting your if statements to reduce nesting. However, lets be honest, when you initially write something, it often starts out as a single really long method and in order to refactor it you still need to identify where each block starts and ends. And of course sometimes you have to work with code other developers wrote. Wouldn't it be nice to be able to collapse a loop, or a try catch block just by clicking on a little minus sign? True you could wrap these sections with regions, but to do that you would have to figure out where the section starts and ends to insert the region statements and by doing that you wouldn't really need them anymore. If you are working in C++ in Visual Studio, you can collapse code blocks, but this functionality is not part of the C# options. Fortunately, there is a Visual Studio 2010 extension you can install to add this functionality to C#.


Posted by Williarob on Friday, April 15, 2011 1:51 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Working with Metafile Images in .Net

What is a Metafile Image?

The Windows Metafile (WMF) is a graphics file format on Microsoft Windows systems, originally designed in the 1990s.

Internally, a metafile is an array of variable-length structures called metafile records. The first records in the metafile specify general information such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of any metafile, correspond to the graphics device interface (GDI) functions required to draw the picture. These records are stored in the metafile after a special metafile device context is created. This metafile device context is then used for all drawing operations required to create the picture. When the system processes a GDI function associated with a metafile DC, it converts the function into the appropriate data and stores this data in a record appended to the metafile.

After a picture is complete and the last record is stored in the metafile, you can pass the metafile to another application by:

  • Using the clipboard
  • Embedding it within another file
  • Storing it on disk
  • Playing it repeatedly

A metafile is played when its records are converted to device commands and processed by the appropriate device.

There are two types of metafiles:

I had worked with Metafiles in Visual Basic 6 many years ago, when I worked for Taltech.com, a company that strives to produce the highest quality barcode images that Windows can create. As I remember it, this involved making lots of Windows API calls, and something called "Hi Metric Map Mode" (MM_HIMETRC). "Basically, the mapping mode system enables you to equate an abstract, logical drawing surface with a concrete and constrained display surface.  This is good in principle but GDI had a major drawback inasmuch as the logical drawing area coordinates were based upon signed integers.  This meant that creating drawing systems based upon some real-world measurement system such as inches or millimeters required you to use a number of integer values to represent a single unit of measure for example, in the case of MM_LOMETRC mapping there are ten integer values to each linear millimeter and in the case of MM_LOENGLISH there are 100 integer values to each linear inch." - Bob Powell. Bob has written a great article: Comparing GDI mapping modes with GDI+ transforms for anyone wanting to learn more about this.

Bob goes on to say that "Given the fact that matrix transformations have been recognized as the only sensible method to manipulate graphics for many years, GDI mapping modes were a very limited alternative and always a bit of a kludge", and he's probably right. To be honest, all that matrix stuff went way over my head. Luckily, today, the simplicity of matrix transformations is built into GDI+, and most of those API calls have been integrated into the System.Drawing Namespaces of the .Net Framework. Having already found a way to draw a barcode as a bitmap using the .Net Framework, I wanted to see how easy it would be to create a barcode as a metafile, since bitmaps are a lossy format, and barcodes need to be as high quality as possible to ensure that the scanners read them correctly.

You might think that creating a metafile would be as easy as using the Save() Method of System.Drawing.Image and giving the file a .wmf or .emf extension, but sadly this is not the case. If you do that, what you actually get, is a Portable Network Graphics (PNG) file, with a .wmf or .emf extension. Even if you use the ImageFormat overload, and pass in the filename and ImageFormat.Emf or ImageFormat.Wmf, you still end up with a PNG. It doesn't matter whether you create a Bitmap and call Save() or you go to the trouble of creating an in memory Metafile (more on that later) and then call Save(), you will never get a true Metafile. If you visit the MSDN documentation on the Metafile Class, you can see under 'Remarks' it casually states:

When you use the Save method to save a graphic image as a Windows Metafile Format (WMF) or Enhanced Metafile Format (EMF) file, the resulting file is saved as a Portable Network Graphics (PNG) file instead. This behavior occurs because the GDI+ component of the .NET Framework does not have an encoder that you can use to save files as .wmf or .emf files.

This is confirmed in the documentation for the System.Drawing.Image.Save Method:

If no encoder exists for the file format of the image, the Portable Network Graphics (PNG) encoder is used. When you use the Save() method to save a graphic image as a Windows Metafile Format (WMF) or Enhanced Metafile Format (EMF) file, the resulting file is saved as a Portable Network Graphics (PNG) file. This behavior occurs because the GDI+ component of the .NET Framework does not have an encoder that you can use to save files as .wmf or .emf files.

Saving the image to the same file it was constructed from is not allowed and throws an exception.

In order to save your in memory metafile as a true metafile, you must make some old fashioned API calls, and I will show you how to do this in due course, but first you need to know how to create an in memory Metafile. Let's assume that, like me, you already have some code that generates a bitmap image which looks just the way you want it. Here is some sample code distilled from a nice BarCode Library project written by Brad Barnhill

        static void Main(string[] args)

        {

            int width = 300;

            int height = 100;

 

            Bitmap b = new Bitmap(width, height);

            int pos = 0;

            string encodedValue =

                "1001011011010101001101101011011001010101101001011010101001101101010100110110101010011011010110110010101011010011010101011001101010101100101011011010010101101011001101010100101101101";

            int barWidth = width / encodedValue.Length;

            int shiftAdjustment = (width % encodedValue.Length) / 2;

            int barWidthModifier = 1;

 

            using (Graphics g = Graphics.FromImage(b))

            {

                // clears the image and colors the entire background

                g.Clear(Color.White);

 

                // lines are barWidth wide so draw the appropriate color line vertically

                using (Pen pen = new Pen(Color.Black, (float)barWidth / barWidthModifier))

                {

                    while (pos < encodedValue.Length)

                    {

                        if (encodedValue[pos] == '1')

                        {

                            g.DrawLine(

                                pen,

                                new Point(pos * barWidth + shiftAdjustment + 1, 0),

                                new Point(pos * barWidth + shiftAdjustment + 1, height));

                        }

 

                        pos++;

                    } // while

                } // using

            } // using

 

            b.Save(@"d:\temp\test.png", ImageFormat.Png);

        }

As you can see, this code creates a new Bitmap image, creates a Graphics object from it, draws on it using the Pen class then saves it as a .png. The resulting image looks like this:

So far so good. As we have already established, simply rewriting the last line as

b.Save(@"d:\temp\test.emf", ImageFormat.Emf);

is not enough to convert this image to a metafile. Sadly, substituting the word "Metafile" for "Bitmap" is not all it takes to create an in memory metafile. Instead, you will need to have a device context handle and a stream handy. If you are working on a Windows Forms application you can create a Graphics object easily by simply typing Graphics g = this.CreateGraphics(); but if you are writing a class library or a console application you have to be a bit more creative and use an internal method (FromHwndInternal) to create the Graphics object out of nothing:

            Graphics offScreenBufferGraphics;

            Metafile m;

            using (MemoryStream stream = new MemoryStream())

            {

                using (offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))

                {

                    IntPtr deviceContextHandle = offScreenBufferGraphics.GetHdc();

                    m = new Metafile(

                        stream,

                        deviceContextHandle,

                        EmfType.EmfPlusOnly);

                    offScreenBufferGraphics.ReleaseHdc();

                }

            }

OK, so now your code looks like this:

        static void Main(string[] args)

        {

            int width = 300;

            int height = 100;

 

            Graphics offScreenBufferGraphics;

            Metafile m;

            using (MemoryStream stream = new MemoryStream())

            {

                using (offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))

                {

                    IntPtr deviceContextHandle = offScreenBufferGraphics.GetHdc();

                    m = new Metafile(

                        stream,

                        deviceContextHandle,

                        EmfType.EmfPlusOnly);

                    offScreenBufferGraphics.ReleaseHdc();

                }

            }

 

            int pos = 0;

            string encodedValue =

                "1001011011010101001101101011011001010101101001011010101001101101010100110110101010011011010110110010101011010011010101011001101010101100101011011010010101101011001101010100101101101";

            int barWidth = width / encodedValue.Length;

            int shiftAdjustment = (width % encodedValue.Length) / 2;

            int barWidthModifier = 1;

 

            using (Graphics g = Graphics.FromImage(m))

            {

                // clears the image and colors the entire background

                g.Clear(Color.White);

 

                // lines are barWidth wide so draw the appropriate color line vertically

                using (Pen pen = new Pen(Color.Black, (float)barWidth / barWidthModifier))

                {

                    while (pos < encodedValue.Length)

                    {

                        if (encodedValue[pos] == '1')

                        {

                            g.DrawLine(

                                pen,

                                new Point(pos * barWidth + shiftAdjustment + 1, 0),

                                new Point(pos * barWidth + shiftAdjustment + 1, height));

                        }

 

                        pos++;

                    } // while

                } // using

            } // using

 

            m.Save(@"d:\temp\test2.png", ImageFormat.Png);

         }

But wait, what happened to my barcode? It's all off center, yet the code used to draw it hasn't changed:

Luckily this is easy to fix. We need to use a different overload when creating the metafile, so that we can specify a width and height, and a unit of measure:

            Graphics offScreenBufferGraphics;

            Metafile m;

            using (MemoryStream stream = new MemoryStream())

            {

                using (offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))

                {

                    IntPtr deviceContextHandle = offScreenBufferGraphics.GetHdc();

                    m = new Metafile(

                        stream,

                        deviceContextHandle,

                        new RectangleF(0, 0, width, height),

                        MetafileFrameUnit.Pixel,

                        EmfType.EmfPlusOnly);

                    offScreenBufferGraphics.ReleaseHdc();

                }

            }

 

Now it looks the same when saved as a .png, but it may still look all wrong (and more importantly be completely unreadable by a barcode scanner) if printed and the resolution of the printer does not match that of your desktop when you created the metafile. Furthermore, if I save this as a real EMF file and email it to you, when you view it you may see a different rendering, because the desktop I created it on has a resolution of 1920x1080, but if your desktop has a higher or lower resolution it will affect how it is displayed. Remember a metafile is a stored set of instructions on how to render the image and by default it will use the stored resolution for reference. To correct this, we have to add some additional code to the Graphics object to ensure this doesn't happen (thanks go to Nicholas Piasecki and his blog entry for pointing this out):

 

                MetafileHeader metafileHeader = m.GetMetafileHeader();

                g.ScaleTransform(metafileHeader.DpiX / g.DpiX, metafileHeader.DpiY / g.DpiY);

                g.PageUnit = GraphicsUnit.Pixel;

                g.SetClip(new RectangleF(0, 0, width, height));

So how can we save it as a real Metafile anyway?

Well, first we need to declare some old fashioned Win API calls:

        [DllImport("gdi32.dll")]

        static extern IntPtr CopyEnhMetaFile(  // Copy EMF to file

            IntPtr hemfSrc,   // Handle to EMF

            String lpszFile // File

        );

 

        [DllImport("gdi32.dll")]

        static extern int DeleteEnhMetaFile(  // Delete EMF

            IntPtr hemf // Handle to EMF

        );

Then we can replace the m.Save(...); line with this:

            // Get a handle to the metafile

            IntPtr iptrMetafileHandle = m.GetHenhmetafile();

 

            // Export metafile to an image file

            CopyEnhMetaFile(iptrMetafileHandle, @"d:\temp\test2.emf");

 

            // Delete the metafile from memory

            DeleteEnhMetaFile(iptrMetafileHandle);

and finally we have a true metafile to share. Why Microsoft failed to encapsulate this functionality within the framework as an image encoder is a mystery. Windows Metafiles, and Enhanced Metafiles are after all their own creation. So our final version of the code looks like this:

        static void Main(string[] args)

        {

            int width = 300;

            int height = 100;

 

            Graphics offScreenBufferGraphics;

            Metafile m;

            using (MemoryStream stream = new MemoryStream())

            {

                using (offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))

                {

                    IntPtr deviceContextHandle = offScreenBufferGraphics.GetHdc();

                    m = new Metafile(

                        stream,

                        deviceContextHandle,

                        new RectangleF(0, 0, width, height),

                        MetafileFrameUnit.Pixel,

                        EmfType.EmfPlusOnly);

                    offScreenBufferGraphics.ReleaseHdc();

                }

            }

 

            int pos = 0;

            string encodedValue =

                "1001011011010101001101101011011001010101101001011010101001101101010100110110101010011011010110110010101011010011010101011001101010101100101011011010010101101011001101010100101101101";

            int barWidth = width / encodedValue.Length;

            int shiftAdjustment = (width % encodedValue.Length) / 2;

            int barWidthModifier = 1;

 

            using (Graphics g = Graphics.FromImage(m))

            {

                // Set everything to high quality

                g.SmoothingMode = SmoothingMode.HighQuality;

                g.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g.PixelOffsetMode = PixelOffsetMode.HighQuality;

                g.CompositingQuality = CompositingQuality.HighQuality;

 

                MetafileHeader metafileHeader = m.GetMetafileHeader();

                g.ScaleTransform(

                    metafileHeader.DpiX / g.DpiX,

                    metafileHeader.DpiY / g.DpiY);

 

                g.PageUnit = GraphicsUnit.Pixel;

                g.SetClip(new RectangleF(0, 0, width, height));

 

                // clears the image and colors the entire background

                g.Clear(Color.White);

 

                // lines are barWidth wide so draw the appropriate color line vertically

                using (Pen pen = new Pen(Color.Black, (float)barWidth / barWidthModifier))

                {

                    while (pos < encodedValue.Length)

                    {

                        if (encodedValue[pos] == '1')

                        {

                            g.DrawLine(

                                pen,

                                new Point(pos * barWidth + shiftAdjustment + 1, 0),

                                new Point(pos * barWidth + shiftAdjustment + 1, height));

                        }

 

                        pos++;

                    } // while

                } // using

            } // using

 

            // Get a handle to the metafile

            IntPtr iptrMetafileHandle = m.GetHenhmetafile();

 

            // Export metafile to an image file

            CopyEnhMetaFile(iptrMetafileHandle, @"d:\temp\test2.emf");

 

            // Delete the metafile from memory

            DeleteEnhMetaFile(iptrMetafileHandle);

        }

There is one more Metafile Gotcha I'd like to share. As part of my original Bitmap generating code, I had a boolean option to generate a label, that is the human readable text that appears beneath the barcode. If this option was selected, before returning the bitmap object I would pass it to another method that looked something like this:

        static Image DrawLabel(Image img, int width, int height)

        {

            Font font = new Font("Microsoft Sans Serif", 10, FontStyle.Bold); ;

 

            using (Graphics g = Graphics.FromImage(img))

            {

                g.DrawImage(img, 0, 0);

                g.SmoothingMode = SmoothingMode.HighQuality;

                g.InterpolationMode = InterpolationMode.HighQualityBicubic;

                g.PixelOffsetMode = PixelOffsetMode.HighQuality;

                g.CompositingQuality = CompositingQuality.HighQuality;

 

                StringFormat f = new StringFormat();

                f.Alignment = StringAlignment.Center;

                f.LineAlignment = StringAlignment.Near;

                int LabelX = width / 2;

                int LabelY = height - font.Height;

 

                //color a background color box at the bottom of the barcode to hold the string of data

                g.FillRectangle(new SolidBrush(Color.White), new RectangleF((float)0, (float)LabelY, (float)width, (float)font.Height));

 

                //draw datastring under the barcode image

                g.DrawString("038000356216", font, new SolidBrush(Color.Black), new RectangleF((float)0, (float)LabelY, (float)width, (float)font.Height), f);

 

                g.Save();

            }

 

            return img;

        }

When passing the bitmap, this works great, but when passing the metafile, the line using (Graphics g = Graphics.FromImage(img)) would throw a System.OutOfMemoryException every time. As a workaround, I copied the label generating code into the main method that creates the barcode. Another option might be to create a new metafile (not by calling m.Clone() - I tried that and still got the out of memory exception), send that to the DrawLabel() method, then when it comes back, create a third Metafile, and call g.DrawImage() twice (once for each metafile that isn't still blank) and return this new composited image. I think that will work, but I also think it would use a lot more resources and be grossly inefficient, so I think copying the label code into both the DrawBitmap() and DrawMetafile() methods, is a better solution.


Categories: C# | CodeProject | Windows
Posted by Williarob on Monday, April 04, 2011 6:51 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Prevent Skype calls from adjusting the volume of other applications

Problem

You are listening to music and receive a skype call. You pause the music, take the call and after you hang up you restart your music only to find that the volume has been turned way down and only closing the app (be it Winamp, Windows Media Player, Media Player Classic, etc,) and restarting it can the volume be restored.

Solution

Control Panel > Sound > Communications > Select "Do Nothing"

 

By default, Windows 7 will automatically reduce the volume of other applications by 80% when a skype (or other) call is detected, but it often fails to restore the volume when the call is completed.


Posted by Williarob on Tuesday, December 14, 2010 12:22 PM
Permalink | Comments (0) | Post RSSRSS comment feed