Tip: How to Change the Default Save Directory for Attachments in Microsoft Outlook 2016 for Windows

Sometimes, you are surprised when functionality that should obviously have a configuration point does not. That is definitely the case when it comes to the default save path for Outlook attachments in Microsoft Outlook 2016

  • Open The Registry Editor
    • Hit the Windows key and type “regedit” to bring up the Registry Editor application
  • Navigate to HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Outlook\Options
  • Create a String Value called “DefaultPath” and make the value be the folder location you want

Below is a screenshot of what your registry editor should look like after you’ve added the path:

Thanks to user KevonaOne on the Microsoft forums for this tip: Q: Office Outlook 2016: Default Save Location for Email Attachments

Using DotNetZip with a Memory Stream

A colleague of mine recently asked about the possibility of adding a download button that would allow multiple files to be added to a single archive and downloaded. A quick google search lead me to the Nuget Package DotNetZip.

This is a great package and very intuitive to use. Within thirty minutes, I had it all wired into my application and it worked as expected. There were only a couple of minor details I had to work to get everything the way I wanted.

Here is the code:

public ExportDocumentResponse DownloadFilePathsAsZip(IEnumerable<string> filePaths)
{
	if (filePaths.Any())
	{
		using (IStream stream = IStreamFactory.Create(new IStreamParameters()
		{
			StreamType = StreamTypeEnum.MemoryStream
		}))
		{
			using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile())
			{
				foreach (var filePath in filePaths)
				{
					zipFile.AddFile(filePath, "");
				}
				zipFile.Save(stream.GetUnderlyingSource());
			}

			return new ExportDocumentResponse()
			{
				DocumentStream = stream,
				ContentType = "application/zip"
			};
		}
	}
	
	return null;
}

This method takes an IEnumerable of strings that are the full disk file paths of the files I want to add to the zip file. In my application, I have a database that stores references to these filenames, so I have some logic outside of this method that creates the full file paths that I want.

From there, it’s a matter of creating a MemoryStream, instantiating the ZipFile, adding each file to the Zip, and saving out the memory stream to the Zip File.

The ExportDocumentResponse object is a custom object I have that allows me to pass this object back to my view (in this application, I’m using an MVP pattern where object is passed back to the View, which sets up the HttpResponse headers and copies the memory stream to the HttpResponse object (via the CopyTo method). That logic looks something like:

public void HandleResponse(IHttpResponse response, ExportDocumentResponse exportResponse, string fileNameWithExtension)
{
	response.Clear();
	response.ContentType = exportResponse.ContentType;
	response.AddHeader("content-disposition", String.Format("attachment;filename={0}", fileNameWithExtension));
	exportResponse.DocumentStream.Position = 0;
	exportResponse.DocumentStream.CopyTo(exportResponse.OutputStream);
	response.End(); //ThreadAbortException will be handled by presenter
}

Also note that I have some interfaces with wrappers around common objects for the implementations (IHttpResponse, IStream) so that I can unit test these methods. Replacing IStream with a System.IO.MemoryStream instance and IHttpResponse with whatever HttpResponse object is part of your web environment (depends on whether you are using WebForms, MVC, etc) should make this solution usable without my implementations. One final note is that my IStream has a method called GetUnderlyingSource which returns a System.IO.Stream object – if replacing my code with an actual Stream implementation, the call can be simplified to just zipFile.Save(stream)

My favorite part is that the code for creating the Zip file is incredibly minimal – there are a total of four lines here that are dedicated to the library and the rest is all logic to support it and return the result to the user in my application. The one “configurable” part here is the second parameter to the AddFile method. I have input an empty string here, because I want all of the files to be placed at the root of the Zip File. When I used the variant of this method with a single parameter, the Zip File would save a folder structure that looked something like the actual file structure from where the files were located. Here is the definition of the AddFile method that pops up from intellisense:

fileName (string): The name of the file to add. The name of the file may be a relative path or a fully-qualified path.

directoryPathInArchive (string): Specifies a directory path to use to override any path in the fileName. This path may, or may not, correspond to a real directory in the current filesystem. If the files within the zip are later extracted, this is the path used for the extracted file. Passing null (Nothing in VB) will use the path on the fileName, if any. Passing the empty string (“”) will insert the item at the root path within the archive.

Troubleshooting Ubiquiti UniFi Access Points when Connecting to a Windows-based Controller

I’m a big fan of Ubiquiti’s UniFi access points. I think it’s generally a good idea to divorce the wireless capabilities from the router because you are more able to adapt when new wireless standards come out, and you don’t end up needing to replace a perfectly good 1Gbps router when that happens.

I love the level of insight and control of Ubiquiti’s devices (and the overall user interface – let’s gloss over the fact that it still relies on a Java plugin to run), but getting them to connect to a controller on Windows can be tricky. Here are some recommendations:

  • Assign your UniFi device a static IP address. Do the same to the machine where you install the controller software, if you can
  • Make sure you can ping your UniFi Access Point. If not, you have a problem with your network configuration
  • If you’re getting a disconnections, try and SSH directly in to the device
    • Windows now offers an optional install of SSH as an add on module to Windows, but I wasn’t able to connect to my UniFi because I received the following message:

      Unable to negotiate with 192.168.xxx.xxx port 22: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,kexguess2@matt.ucc.asn.au

      This means that the SSH installed via Windows couldn’t be used since it didn’t have one of those three key exchange methods available (note that after upgrading my UniFi firmware, the message changed, as it appears they are using more secure key exchanges now: ssh-rsa,ssh-dss).

    • Since I couldn’t use SSH via Powershell, I downloaded good old PuTTY and connected to my UniFi access point that way
      • Run “info” once connected via PuTTY to look for clues regarding your disconnected controller. The “Status” line shows whether or not the device is currently connected. If you see status Unknown[11] (http://192.168.xxx.xxx), then you know there is an issue connecting to your controller. At least this message tells you whether or not your device is trying to communicate with the IP address where your controller resides
        • Run “set-inform http://192.168.xxx.xxx:8080/inform” if your controller isn’t the IP displayed in the info box
      • If you SSH’d in from a machine other than your controller, run a ping command to your controller to make sure that the access point can communicate with your controller
  • Check your Windows Firewall rules
    • On one controller, the connection to my access point was restored almost immediately as soon as I disabled Windows Firewall. Check that the rules created by the installation of the UniFi Controller software apply to all network types (public, private, domain) that you use to communicate with the outside world
      • In my case, the problem was Java was being blocked from private network connections. Once I added private networks to the “Allow” rules, the connection restored almost immediately
    • Check out this post if you’re having trouble identifying what program is blocking your connection: https://superuser.com/questions/1130078/how-to-tell-which-windows-firewall-rule-is-blocking-traffic

 

How to Fix: Inaccessible Boot Device Error Caused by January/February 2018 Windows Update(s)

“Fix” is a bit of a loose term for this solution because it will restore your computer to a workable state without losing any files or applications, but you have to disable Windows Updates so the affected update doesn’t continue to be downloaded over and over again.

Update: There is now a patch out to fix this: https://support.microsoft.com/en-us/help/4090913/march5-2018kb4090913osbuild16299-251. Note that when I checked for updates using Windows Update, it downloaded the bad patch again, so I uninstalled that and downloaded the patch linked from the article above (Actual link to download the update is http://www.catalog.update.microsoft.com/Search.aspx?q=KB4090913).

If you encounter the blue screen of death with the message “Inaccessible Boot Device” and a frowny face, you’ve probably just updated Windows and the computer attempted to reboot.

I will not take any credit for this fix, as I spent several days looking for ways to resolve this problem when it affected machines in my office. A huge thanks goes to reddit user zosan for providing this excellent, step-by-step guide:

How-To: Fix "INACCESSIBLE_BOOT_DEVICE" caused by Win10 Spectre builds 16299.125 or 16299.192. from pcgaming

Hopefully Microsoft resolves this sometime soon so updates don’t have to be disabled too long.

 

Converting Large Integers Stored as Floats to NVarChars in SQL (without Scientific Notation)

Converting SQL data types can be a bit finicky, and, at least for this guy, converting a stored, large integer value to a string is not intuitive at all.

I mostly run into this when I import values from some data source like an Excel sheet that stores values like tracking numbers as a float. From there, I usually write a cursor to update tables in my system with these values, and when those tables use a column type of varchar or nvarchar, you have to convert from float type to varchar

One would think that using CAST(varchar(50), TrackingNumber) would do the trick, but when this cast is made, the value is stored in scientific notation.

The real trick is to first convert the int value to a bigint and THEN convert it to a varchar, as shown below:

CONVERT(varchar(50), CONVERT(bigint, TrackingNumber))

Preloading Websites on IIS 7.5

Earlier this year, I built a .Net Core Web Application and deployed it on IIS 7.5. I noticed right away that it was extremely slow on initial load. This was confusing because I tested deployment to Azure and the performance was great. I struggled to figure out why it loaded so slowly but all of my .Net Framework sites ran quickly.

I struggled to find an answer for a long time, but while reading through documentation while setting up another application I came across this bit of information:

http://cdn.rssbus.com/help/ARC/mft/pg_serveriis7dot5.htm

Preloading Applications

You can make use of the preloading feature to have applications running before users connect. In your ApplicationHost.config, add the preloadEnabled attribute to the <application> element associated with the application. The application node is a child element of the sites node:

<sites>
<site name="Default Web Site" id="1">
  <application path="/rssbus" applicationPool="DefaultAppPool"  preloadEnabled="true">
  
  ...

When PreloadEnabled is set to true, IIS will simulate a user request to the default page of the website or virtual directory so that the application initializes.

While it technically is a bit of a workaround since it doesn’t solve the root problem I experience with preloading, it has kept my application loading quickly since I enabled it.