Moving to new site
I'll be continuing my site off the blogger platform, but retaining this site for historical purposes. Please follow me to my new home at https://emil.lerch.org/. My first post goes into more detail about the move.
Probing support for architecture-specific native DLLs in .NET
Or...How I've tamed the Oracle beast
- Disassemble Oracle.DataAccess.dll with ildasm
- Remove the public key token and change the version number in the IL
- Reverse the branch logic where an exception is thrown if the assembly is "incompatible" with the native code
- Reassemble the library with ilasm
- Repeat for the other processor architecture
Visual Studio unit testing slow when NetBIOS over TCP/IP is enabled
According to MS Connect, this doesn't happen. People have reported it, however, in VS 2010, 2012, and I've experienced the problems in VS 2013. It's also listed at this StackOverflow question: http://connect.microsoft.com/VisualStudio/feedback/details/768230/slow-running-in-test-runner
I've now added this handy PowerShell command to my "initialize a new machine" setup PowerShell script:
# Disable NetBios over TCP/IP on all interfaces
# to prevent weird Visual Studio slowdowns during unit tests
Get-ChildItem hklm:system/currentcontrolset/services/netbt/parameters/interfaces | foreach{ $item = $_; Set-ItemProperty -Path ($item.ToString().Replace("HKEY_LOCAL_MACHINE", "hklm:")) -Name NetbiosOptions -Type DWord -Value 2 -ea "SilentlyContinue" }
REST Basics
Obligatory upper-right blog image |
- POST
- GET
- PUT
- DELETE
- PATCH
AngularJs HTML5 routing and IE9
We seem to be pushing the limits of AngularJS, and we've only started using it. The framework is very promising but definitely very new and a little rough in some areas.
We'd like to avoid hash urls in our solutions. The backend can respond to our routes just fine, and our backend framework is able to serve up the correct content (not necessarily the same content) for any area of the application. Using HTML5 mode and the history.pushState api, we can get clean URLs and no 404 errors on refresh...awesome.
Enter, IE9 (top light blue line). Our current browser standards are IE9+, Desktop/Android Chrome, FF, and IOS Safari. All browsers support history.pushState except IE9. The browser has had a recent significant drop in usage, but we can't ignore it quite yet.
At first, we thought we could set $locationProvider.html5Mode(true) and it would either a) break in IE9 calling a function that didn't exist (but we could define as window.location.assign(url)), or b) do a browser-based redirect based on window.location.assign. Well, RTFM: what it does is actually relatively painful. It falls back to hashbang syntax. This is similar to history.js, and IMHO, is broken in the same way as that library. Now you have URLs you can't share between browsers without crazy (and slow) workarounds.
I tried to work around the problem by providing a history.pushState implementation. This fooled Angular into thinking it was compliant, but caused some redirect loops and ultimately didn't work out. Next, I tried first intercepting routeChangeStart and calling event.preventDefault to no avail, then locationChangeStart also without success (I'm informed this does work, but for some reason I didn't see it, at least not in IE9).
Finally, it hit me...since we're not programmatically changing locations (just using anchor tags), we could simply determine if pushState is available and perform the following functions if it is not:
- Avoid defining any routes with the route provider
- Take our <script id='whateverroutewearecurrentlyon' type='text/ng-template'/> that is automatically generated on the backend for the current URL and manually add it to the ng-view element.
- Remove the ng-view attribute (for completeness) and add an ng-controller attribute pointing to the controller for the current route.
- Disable any pre-loading for pages other than the one on which we reside.
- Any URL used by history.pushState actually generates appropriate content from the server.
- The server at any URL provides the page layout and the application code. Providing the current page content in the script tag is nice (that's what we do) but it's not strictly required (#2 above could be done via Ajax).
- The application code is light enough that you (and your users) can put up with full page loads in non-compliant browsers until users upgrade. I wouldn't want to try this technique on gmail. ;-)
Enabling Quartz jobs in ASP.NET applications that will run despite restart
With IIS 7.5, you can now auto-start applications and have them continuously run. However, implementing System.Web.Hosting.IProcessHostPreloadClient means having a Process method with some significant restrictions. In a Spring.Net environment, the IOC Container's context is not yet started, and it will fail in rather spectacular ways if you try to crank it up with a hack. Even if you manage to do this, Quartz does not start up, so your jobs still will fail to run.
I spent some time on this problem and discovered that I do have access to System.Web.HttpRuntime.AppDomainAppVirtualPath, which allows us to automatically fire an initial request at the app automatically, if we're careful. This will crank up Application_Start and the rest of the Spring (and Quartz) machinery, allowing us to keep an app running 100% of the time. This initial request, however, will be thrown away, even if fired asynchronously, if initiated during the Preload() method. I ended up using a thread to get around this problem, allowing the requests to fire 500ms after the Preload method runs.
In certain cases, we can't just use http://localhost/yourvirtualpathhere. You might have multiple sites listening to different host headers, or you might be running SSL and don't want to hit the application using localhost. To cover those cases, I devised a web.config appSettings scheme where additional URLs can be applied to Preload (space delimited). You can also specify an IP address or machine name, so the web.config can stay static but different URLs can be applied as you move through environments. Here is the code:
public class Preloader : System.Web.Hosting.IProcessHostPreloadClient { public void Preload(string[] parameters) { var uris = System.Configuration.ConfigurationManager.AppSettings["AdditionalStartupUris"]; StartupApplication(AllUris(uris)); } public void StartupApplication(IEnumerable<Uri> uris) { new System.Threading.Thread(o => { System.Threading.Thread.Sleep(500); foreach (var uri in (IEnumerable<Uri>)o) { var client = new System.Net.WebClient(); client.DownloadStringAsync(uris.First()); } }).Start(uris); } public IEnumerable<Uri> AllUris(string userConfiguration) { if (userConfiguration == null) return GuessedUris(); return AllUris(userConfiguration.Split(' ')).Union(GuessedUris()); } private IEnumerable<Uri> GuessedUris() { string path = System.Web.HttpRuntime.AppDomainAppVirtualPath; if (path != null) yield return new Uri("http://localhost" + path); } private IEnumerable<uri> AllUris(params string[] configurationParts) { return configurationParts .Select(p => ParseConfiguration(p)) .Where(p => p.Item1) .Select(p => ToUri(p.Item2)) .Where(u => u != null); } private Uri ToUri(string value) { try { return new Uri(value); } catch (UriFormatException) { return null; } } private Tuple<bool string> ParseConfiguration(string part) { return new Tuple<bool, string>(IsRelevant(part), ParsePart(part)); } private string ParsePart(string part) { // We expect IPv4 or MachineName followed by | var portions = part.Split('|'); return portions.Last(); } private bool IsRelevant(string part) { var portions = part.Split('|'); return portions.Count() == 1 || portions[0] == System.Environment.MachineName || HostIpAddresses().Any(a => a == portions[0]); } private IEnumerable<string> HostIpAddresses() { var adaptors = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); return adaptors .Where(a => a.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up) .SelectMany(a => a.GetIPProperties().UnicastAddresses) .Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) .Select(a => a.Address.ToString()); } }
Installing Cyanogenmod 7.2 on Verizon Droid 2 Running 2.3.4
They said it couldn't be done. If you install 2.3.4 OTA update from Verizon, you were stuck with a version that was un-rootable. And rooting is the first step in getting a custom Android build on a phone. Specifically:
There is currently (as of May 2012) no way to root a Droid 2 with this system version.As a word of warning, the procedures for getting Cyanodenmod are as scary as the link above, and with 2.3.4, you're really in the deep end. Here's a summary of what I did. I'm not providing step-by-step directions for two reasons; first, if you can't get there from the summary, you're probably in over your head and you have a good chance of bricking your phone. Secondly, I am providing links to my source materials with more details. I'm also taking you through the method I went, which generally used tools rather than adb commands/doing it by hand. I purchased two of the tools...I think the $8 I spent was worth it, but go read the wiki if you want to do it the hard way.
Rooting
Custom Recovery ROM and Bootloader
- Bootloader is run, presumably by the hardware
- Bootloader looks around, checks out everything, and loads the ROM image. Depending on the environment it can run the recovery ROM or the normal boot ROM (your phone).
Prepping and Hacking the Cyanogen install
- Cyanogen latest version: http://wiki.cyanogenmod.org/wiki/Devices_Overview#Motorola_Droid_2. Right now, this is 7.2.0, available at: http://get.cm/get/jenkins/2824/cm-7.2.0-droid2.zip
- Google apps (gets you the market): http://goo-inside.me/gapps/gapps-gb-20110828-signed.zip. That is the mirror link as the primary didn't work for me. The cyanogenmod page on this is at: http://wiki.cyanogenmod.org/wiki/Latest_Version#Google_Apps
Installing Cyanogen
- Back up your existing ROM
- Select and install the Cyanogen ROM
- Select and install the Google Apps ROM (I think this just unpacks apks and puts them in a special directory, but I'm not sure). I thought it was way weird I was installing two ROMs.
- Lastly, wipe all cache/data. I don't remember if I performed factory reset - I think I did not. Apparently the phone will get into a "boot loop" unless this step is performed, but if you forget for some reason, you can get out of that problem: http://forum.xda-developers.com/showthread.php?t=1832130