<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel xml:base="https://brutaldev.com/">
    <title>Brutal Developer</title>
    <description>Latest blog posts</description>
    <a10:link href="https://brutaldev.com/" />
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/brutal-devs-guide-to-klapping-boet-fighter</guid>
      <link>https://brutaldev.com/post/brutal-devs-guide-to-klapping-boet-fighter</link>
      <a10:author>
        <a10:name>werner</a10:name>
      </a10:author>
      <title>Brutal Dev's Guide to Klapping Boet Fighter</title>
      <description>&lt;span style="color:rgb(0,0,0)"&gt;Many okes like myself find &lt;a href="https://store.steampowered.com/app/1108350/Boet_Fighter/"&gt;Boet Figther&lt;/a&gt; incredibly difficult (as it was intended). So flippin tough, that okes have been klapping the game for several hours and only getting to the third bladdy level! This grinding stops being fun after a while and I don't have a lot of time to drib millions of dooses in the face over and over for hours just to check some kiff videos clips and have a laugh, so I needed to find a way to win...&lt;br /&gt;&lt;br /&gt;Don't get me wrong, I really enjoy playing this game, but I enjoy moering okes more without getting moered back. This game is very entertaining and very much worth the money for the solid work the &lt;a href="http://califourways.com/our-okes/"&gt;developers&lt;/a&gt; put into it and there crazy fast turnaround with bug fixes and features. Hard Eddy isn't the hardest charna in town if he keeps getting bliksemmed so easily, I wanted a &lt;i&gt;Harder&lt;/i&gt; Eddy, the &lt;i&gt;Hardest&lt;/i&gt; Eddy of them all, &lt;i&gt;Invincible&lt;/i&gt; Eddy! &lt;br /&gt;&lt;br /&gt;&lt;u&gt;This is cheating&lt;/u&gt;, but nothing that the likes of GameGenie never did for us in the 80's, so if you don't like it then get outta here before you get offended.&lt;br /&gt;&lt;/span&gt;&lt;h2&gt;&lt;span style="color:rgb(0,0,0)"&gt;Unlocking All Levels&lt;/span&gt;&lt;/h2&gt;&lt;font color="#000000"&gt;Even with the new checkpoints, it's still crazy hard to get past levels and bosses. I was getting bored stuck on the second level fighting the zombies, I wanted to just explore some of the other levels at least and appreciate the artwork without having to spend many hours grinding my way through. So first off, I wanted to unlock all the levels.&lt;/font&gt;&lt;br /&gt;&lt;span style="color:inherit;font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.1em;font-weight:bold"&gt;&lt;br /&gt;Looking For Save Games&lt;/span&gt;&lt;br /&gt;&lt;font color="#000000"&gt;A quick file/folder search for "Boet Fighter" and I discovered the save game file in your user's &lt;i&gt;\AppData\LocalLow\Califourways\Boet Fighter&lt;/i&gt; directory. Simply opening up in Notepad reveals that it's just serialized .NET classes, the important bit looking like this:&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;b&gt;&lt;i&gt;Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null...DataState...Level1...Level2&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Assembly-CSharp.dll&lt;/i&gt; is a typical file name for Unity games, all the game code is bundled in there and the class being serialized here is &lt;i&gt;&lt;b&gt;DataState&lt;/b&gt;.&lt;br /&gt;&lt;/i&gt;So spin up a quick console app, add a reference to &lt;i&gt;Assembly-CSharp.dll&lt;/i&gt; (found in &lt;i&gt;\steamapps\common\Boet Fighter\Boet Fighter_Data\Managed&lt;/i&gt;) and see if we can read and write save game files to add all the levels.&lt;br /&gt;&lt;br /&gt;To avoid wasting time I could be using playing the game, I decompiled the game assembly and traced back where this class was being used, and how the file was getting read/written when you pass levels. In short, this was the code I wrote based on my existing saved game (with only two levels that I passed) to add in the other levels very much like the game itself would do it, using the games own code from it's assembly.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:inherit;font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.1em;font-weight:bold"&gt;Custom Save Game Code&lt;/span&gt;&lt;br /&gt;&lt;pre style="margin:0;overflow:auto;background-color:#ffffff"&gt;&lt;code style="font-family:Consolas,&amp;quot;Courier New&amp;quot;,Courier,Monospace;font-size:10pt;color:#000000"&gt;&lt;span style="color:#0000ff"&gt;var&lt;/span&gt; allAccess = &lt;span style="color:#0000ff"&gt;new&lt;/span&gt; DataState();
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level1"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level2"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level3"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level4"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level5"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level6"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
allAccess.AddItem(&lt;span style="color:#0000ff"&gt;new&lt;/span&gt; SaveData(&lt;span style="color:#a31515"&gt;"Level7"&lt;/span&gt;, &lt;span style="color:#a31515"&gt;"\u001b"&lt;/span&gt;));
SerializatorBinary.SaveBinary(allAccess,
   Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
                @&lt;span style="color:#a31515"&gt;"AppData\LocalLow\Califourways\Boet Fighter\Savegame.bin"&lt;/span&gt;));&lt;/code&gt;&lt;/pre&gt;&lt;font color="#000000"&gt;&lt;br /&gt;Open up the game and check that I have access to all level, flippin A boet! Keeping the image small below as to not spoil it for the okes that don't want to know what's coming up in the later stages.&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;img src="/posts/files/3ae0108e-b879-40a0-8c1f-c49f3a86bbdd.png" alt="" /&gt;&lt;font color="#000000"&gt;&lt;br /&gt;&lt;/font&gt;&lt;span style="color:rgb(0,0,0);font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.1em;font-weight:bold"&gt;&lt;br /&gt;Download Save Game&lt;/span&gt;&lt;font color="#000000"&gt;&lt;br /&gt;&lt;/font&gt;&lt;font color="#000000"&gt;The save game is generic (at the time of writing) so anyone could just copy the save game file to &lt;i&gt;C:\Users\YOUR_USER_NAME\AppData\LocalLow\Califourways\Boet Fighter&lt;/i&gt; to unlock the levels, if that's your vibe, just download a copy here (no warranties, always make a backup) and overwrite your one to unlock all the levels: &lt;a href="/download/BF/Savegame.bin"&gt;Savegame.bin&lt;/a&gt;&lt;br /&gt;&lt;/font&gt;&lt;span style="color:rgb(0,0,0);font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.3em;font-weight:bold"&gt;&lt;br /&gt;Invincible Eddy&lt;br /&gt;&lt;/span&gt;So having all the levels is cool enough, but the game is still too hard for me to get very far and I reaaaalllly like the cut scenes (they're awesome) so I reaaalllly want to get past these stages even if it means making it a zero challenge game. The answer, patching the game to make Eddy never get hurt, ever.&lt;br /&gt;&lt;span style="color:rgb(0,0,0);font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.1em;font-weight:bold"&gt;&lt;br /&gt;Finding The Health Meter&lt;br /&gt;&lt;/span&gt;&lt;font color="#000000"&gt;The most obvious way Eddy gets killed is his health meter running out (duh) so the most obvious way to patch the game would be to just stop that from happening. Back to the decompiler, there are a number of decompilers you can use for Unity games, for this I just used trusty old &lt;a href="http://reflector.net"&gt;Reflector&lt;/a&gt;.&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font color="#000000"&gt;In actual fact, I debugged the game first to test some theories of resetting health among other things, and found the easiest spot in the &lt;b&gt;&lt;i&gt;HeatlhManager&lt;/i&gt;&lt;/b&gt; class's &lt;i&gt;Update&lt;/i&gt; (game loop) method. I won't get into the boring research stuff, but this was the result of my findings:&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;img src="/posts/files/23dca6c9-2424-454f-90e5-786de17e8030.png" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;I imagine this logic here is for when picking up health tubs and shakes to avoid your health going over the max, so if we simply switch it from &amp;gt;= to &amp;lt;= then any time you health drops below 200, it will get reset to 200. This is the smallest possible change for someone lazy like me.&lt;br /&gt;&lt;span style="color:inherit;font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.1em;font-weight:bold"&gt;&lt;br /&gt;Patching The Game&lt;/span&gt;&lt;br /&gt;For something like this I'll turn to &lt;a href="http://reflexil.net/"&gt;Reflexil&lt;/a&gt;, since there isn't much to tweak here you can avoid full IL dumping of the entire thing and trying to recompile again safely, so it's a useful shortcut to use.&lt;br /&gt;&lt;br /&gt;The OpCode we want to change here is from &lt;i&gt;blt.s&lt;/i&gt; to &lt;i&gt;bgt.s&lt;/i&gt; (less than to greater than). I know it sounds backwards, but in IL land, constants usually get stacked first so this would technically be &lt;i&gt;&lt;b&gt;200 &amp;lt;= this.CurrentHealth&lt;/b&gt;&lt;/i&gt; and we're changing it to &lt;i&gt;&lt;b&gt;200 &amp;gt;= this.CurrentHealth&lt;/b&gt;.&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;img src="/posts/files/63e79bcf-90ec-4a92-b4af-7154ad152720.png" alt="" /&gt;&lt;br /&gt;&lt;font color="#000000"&gt;&lt;br /&gt;Save this patched assembly and replace the original. We can verify that the patching worked by decompiling the new assembly and going back to check the expression is what we expect:&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;img src="/posts/files/d699f230-e30d-47d1-a79e-bc945460732a.png" alt="" /&gt;&lt;br /&gt;&lt;font color="#000000"&gt;&lt;br /&gt;So now if your current health drops below 200, it goes right back up to 200, &lt;a href="/download/BF/InvincibleEddy.mp4"&gt;Invincible Eddy&lt;/a&gt;, the way he would be in real laaf! &lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;img src="/posts/files/c1bfcfed-1fc9-44b9-b3cd-cefe282fc4ca.png" alt="" /&gt;&lt;font color="#000000"&gt;&lt;br /&gt;&lt;br /&gt;Not going to share the patched DLL here obviously, you gotta buy the game if you want that. This was just for me to have a jol and moer the game properly in it's face and watch all the videos. if it's too hard to patch things yourself well then it's like the game, flippin hard boet, and you're going to have to play it the hard way! The next update (very quick at this rate cause they listen to feedback a lot) will lose this change but that's cool, I'll keep coming back to check out what's changed and give it another go because I also like the technical aspect.&lt;br /&gt;&lt;/font&gt;&lt;span style="color:rgb(0,0,0);font-family:Raleway,&amp;quot;Helvetica Neue&amp;quot;,Helvetica,Arial,sans-serif;font-size:1.3em;font-weight:bold"&gt;&lt;br /&gt;Go Buy Boet Figher&lt;/span&gt;&lt;font color="#000000"&gt;&lt;br /&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Go grab a copy of &lt;a href="https://store.steampowered.com/app/1108350/Boet_Fighter/"&gt;Boet Fighter&lt;/a&gt;&lt;/font&gt; &lt;font color="#000000"&gt;now on Steam, it's only 200 South African Rond which is basically for nothing given the hours of entertainment you get from it. It's a great, simple game and lots of fun, but if you get too annoyed and getting dribbed constantly (or maybe you just kak at playing like me) at least you can cheat a bit to get all the entertainment value this game has to offer without any of the frustration.&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;All images etc are copyright of the owners and all that kak.&lt;br /&gt;</description>
      <pubDate>Sat, 12 Oct 2019 21:03:01 Z</pubDate>
      <a10:updated>2021-12-20T18:35:57Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/the-vast-data-leak</guid>
      <link>https://brutaldev.com/post/the-vast-data-leak</link>
      <a10:author>
        <a10:name>werner</a10:name>
      </a10:author>
      <title>The VAST data leak</title>
      <description>&lt;b&gt;February 27, 2017&lt;/b&gt;&lt;br /&gt;Earlier this month it was brought to my attention that &lt;a href="http://www.vast.network/"&gt;VAST&lt;/a&gt;, a prominent WiFi provider in South Africa, &lt;b&gt;had&lt;/b&gt; private user information exposed on publicly visible pages on &lt;a href="https://portal.vast.directory/"&gt;their complimentary internet access portal&lt;/a&gt;. The problem was fixed within an hour of being reported to the CTO and went above and beyond by performing a full audit with &lt;a href="https://sensepost.com/" style="background-color:rgb(255,255,255)"&gt;SensePost&lt;/a&gt; to ensure that their customer's data is protected.&lt;br /&gt;&lt;br /&gt;All pages on the portal could be accessed through a node number, in a URL format like this: &lt;a href="https://portal.vast.directory/node/1234567"&gt;https://portal.vast.directory/node/1234567&lt;/a&gt;. As you can see, you are not authorized to see this page any more, but before a fix was applied it looked something like this:&lt;br /&gt;&lt;div style="text-align:center"&gt;&lt;img src="/posts/files/3d99f39d-dcab-4a0b-8bf0-f6b6be71bf36.png" alt="" style="font-size:1.8em" /&gt;&lt;/div&gt;&lt;br /&gt;In a nutshell, when you signed up for free/complimentary WiFi on the &lt;a href="https://portal.vast.directory/"&gt;VAST portal&lt;/a&gt;, a new page would be created with the information that you used to sign up. This user provided information included your name, email address, mobile number, verification OTP number (only necessary at certain hotspots), your device MAC address, current hotspot location and gender.&lt;br /&gt;&lt;br /&gt;A basic manual enumeration in the browser (adding or subtracting one from the ID portion of the URL) didn't really yield much information so on the surface this did not look significant at all.&lt;br /&gt;&lt;br /&gt;At the time this was a gimmick because you could sign on with a fake phone number, get to the page that was generated for you to view the OTP and get free timed access. This escalated when it was realised that you can use the MAC address as both the username and password to create a login session, effectively being able to login as anyone who ever signed up. Using the same MAC address, you could also log anyone off the network, causing quite an annoying denial of service. Many of these exploits were being discussed on security forums and chats as early as the 7th of February. This highlights that there could certainly be some security flaws in the implementation, but I was more interested in the amount of private user information that was accessible.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.vast.network/" style="background-color:rgb(255,255,255)"&gt;VAST&lt;/a&gt; runs some well known WiFi offerings so I expected to see quite a lot of data. The earliest user account found in the node pages was created in April 2016, so anyone signed up in the last ten months at certain sites could have had their personal information seen by a third party. Of the over 14 million possible node pages, just over 10% actually contained user content, which is still a lot... &lt;br /&gt;&lt;br /&gt;In total there are 881,974 unique email addresses and 856,648 unique mobile phone numbers. Some of this information is of course fake, but most people generally don't provide fake data when signing up for these things. Mobile numbers are mostly South African, but there is a fair share of international dialing codes so this would have impacted people who visited South Africa as well and used WiFi at the airport.&lt;br /&gt;&lt;br /&gt;If people were using these WiFi hotspots regularly, it was easy to start tracking their movements based on the sites, which flights they were on, where they were travelling, where they often had lunch or restaurants they work from. This was no hack, no technical vulnerability, just a simple page enumeration due to lack of access control to pages containing user information.&lt;br /&gt;&lt;br /&gt;I must applaud &lt;a href="http://www.vast.network/" style="background-color:rgb(255,255,255)"&gt;VAST&lt;/a&gt;'s response and reaction time to this, one of the fastest I've ever experienced by patching this up in under an hour after being notified. Furthermore, they have engaged with &lt;a href="https://sensepost.com/"&gt;SensePost&lt;/a&gt; to review the client implementation and more than likely preventative measures to avoid this in future.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The page data did not contain any passwords so there is no reason to panic.&lt;/li&gt;&lt;li&gt;None of this data was indexed on any search engines.&lt;/li&gt;&lt;li&gt;This data is not available for public download or being sold anywhere that I know of.&lt;/li&gt;&lt;li&gt;There is little reason to believe anyone's details were in fact compromised while the exposed pages were visible.&lt;/li&gt;&lt;li&gt;No, I will not tell you if you are/were in the data, use Troy Hunt's "&lt;a href="https://haveibeenpwned.com/"&gt;Have I Been Pwned&lt;/a&gt;" service.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;</description>
      <pubDate>Mon, 27 Feb 2017 12:08:15 Z</pubDate>
      <a10:updated>2017-08-05T14:42:21Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/bank-grade-security---south-african-bank-edition</guid>
      <link>https://brutaldev.com/post/bank-grade-security---south-african-bank-edition</link>
      <a10:author>
        <a10:name />
      </a10:author>
      <title>Bank Grade Security - South African bank edition</title>
      <description>&lt;style&gt;.bank-table{border-collapse:collapse;width:100%}.bank-table a{color:black;vertical-align:bottom;font-weight:bold}.bank-table th{white-space:nowrap;text-align:center}.bank-table th.bank{text-align:left}.bank-table td{width:50px;border:1px solid #ccc;padding:3px;white-space:nowrap}.grade{text-align:center;font-weight:bold}.a-rating{background-color:lawngreen}.b-rating{background-color:orange}.c-rating{background-color:yellow}.f-rating{background-color:red}.fail,.pass,.partial{width:50px;text-align:center;color:white}.fail{background-color:red}.pass{background-color:green}.partial{background-color:orange}&lt;/style&gt;&lt;p&gt;After reading an interesting post by &lt;a href="http://www.troyhunt.com/2015/05/do-you-really-want-bank-grade-security.html"&gt;Troy Hunt about so called "bank grade" security on Aussie internet banking sites&lt;/a&gt; I was curious to see what the results would be for South African banks and more specifically how my bank stacks up against the competition. The results are similar with some very surprising names scoring quite low using &lt;a href="https://www.ssllabs.com/ssltest"&gt;Qualys SSL Server Test&lt;/a&gt;.&lt;/p&gt;&lt;table class="bank-table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th class="bank"&gt;Bank&lt;/th&gt;&lt;th&gt;Grade&lt;/th&gt;&lt;th&gt;SSL3&lt;/th&gt;&lt;th&gt;SHA1&lt;/th&gt;&lt;th&gt;TLS 1.2&lt;/th&gt;&lt;th&gt;RC4&lt;/th&gt;&lt;th&gt;FS*&lt;/th&gt;&lt;th&gt;POODLE&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="a-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=www.africanbank.co.za&amp;amp;s=196.15.233.78"&gt;African Bank&lt;/a&gt;&lt;/td&gt;&lt;td class="grade a-rating"&gt;A-&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="a-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=ibank.sasfin.com"&gt;Sasfin&lt;/a&gt;&lt;/td&gt;&lt;td class="grade a-rating"&gt;A-&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="b-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=secure.bidvestbank.com"&gt;Bidvest&lt;/a&gt;&lt;/td&gt;&lt;td class="grade b-rating"&gt;B&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="b-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=www.fnb.co.za&amp;amp;s=196.11.125.149"&gt;First National Bank&lt;/a&gt;&lt;/td&gt;&lt;td class="grade b-rating"&gt;B&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="b-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=netbank.nedsecure.co.za"&gt;Nedbank&lt;/a&gt;&lt;/td&gt;&lt;td class="grade b-rating"&gt;B&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="b-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=login.secure.investec.com"&gt;Investec&lt;/a&gt;&lt;/td&gt;&lt;td class="grade b-rating"&gt;B&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="b-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=online.grindrodbank.co.za"&gt;Grindrod&lt;/a&gt;&lt;/td&gt;&lt;td class="grade b-rating"&gt;B&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="b-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=direct.capitecbank.co.za"&gt;Capitec&lt;/a&gt;&lt;/td&gt;&lt;td class="grade b-rating"&gt;B&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="partial"&gt;PASS-&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="c-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=https%3A%2F%2Fwww22.encrypt.standardbank.co.za"&gt;Standard Bank&lt;/a&gt;&lt;/td&gt;&lt;td class="grade c-rating"&gt;C&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="f-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=https%3A%2F%2Fib.absa.co.za%2F"&gt;ABSA&lt;/a&gt;&lt;/td&gt;&lt;td class="grade f-rating"&gt;F&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="f-rating"&gt;&lt;a href="https://www.ssllabs.com/ssltest/analyze.html?d=netbanking.imperialbank.co.ke"&gt;Imperial Bank&lt;/a&gt;&lt;/td&gt;&lt;td class="grade f-rating"&gt;F&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="partial"&gt;PASS-&lt;/td&gt;&lt;td class="pass"&gt;PASS&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;td class="partial"&gt;PASS-&lt;/td&gt;&lt;td class="fail"&gt;FAIL&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;* Forward Secrecy&lt;/p&gt;&lt;p&gt;The most surprising to me was &lt;a href="http://www.standardbank.co.za/"&gt;Standard Bank&lt;/a&gt; and &lt;a href="http://www.absa.co.za/"&gt;ABSA&lt;/a&gt;, the top two largest banks in the country, are failing dismally on their SSL implementation. &lt;a href="https://www.imperialbankgroup.com/"&gt;Imperial Bank&lt;/a&gt; is frightening and vulnerable to both POODLE and FREAK attacks. The smallest banks seem to score the highest and a number of South African banks don't even have internet banking such as &lt;a href="http://www.ubank.co.za/"&gt;uBank&lt;/a&gt; and &lt;a href="http://www.postbank.co.za/Questions/internetbanking.html"&gt;Postbank&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Although the grades look decent overall, the amount of &lt;span style="color:red"&gt;red&lt;/span&gt; is quite concerning.&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2015 07:29:53 Z</pubDate>
      <a10:updated>2025-09-09T13:48:41Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/net-assembly-strong-name-signer</guid>
      <link>https://brutaldev.com/post/net-assembly-strong-name-signer</link>
      <a10:author>
        <a10:name>Werner</a10:name>
      </a10:author>
      <title>.NET Assembly Strong-Name Signer</title>
      <description>&lt;p&gt;-- &lt;strong&gt;&lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Download (v3.6.5.0)&lt;/a&gt;&lt;/strong&gt; --&lt;br /&gt;-- &lt;strong&gt;&lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_NuGet'])" href="https://www.nuget.org/packages/Brutal.Dev.StrongNameSigner/" target="_self"&gt;NuGet Package&lt;/a&gt;&lt;/strong&gt; --&lt;/p&gt;&lt;p&gt;Automatic strong-name signing of referenced assemblies. Build tasks and utility software to strong-name sign .NET assemblies, including assemblies you do not have the source code for. If you strong-name sign your own projects you may have noticed that if you reference an unsigned third party assembly you get an error similar to “&lt;em&gt;Referenced assembly 'A.B.C' does not have a strong name&lt;/em&gt;”. If you did not create this assembly, you can use this tool to sign the assembly with your own (or temporarily generated) strong-name key. The tool will also re-write the assembly references (as well as any &lt;i&gt;InternalsVisibleTo&lt;/i&gt; references) to match the new signed versions of the assemblies you create.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Why?&lt;/h2&gt;&lt;p&gt;I decided to write this tool because I have needed to sign assemblies myself a number of times. When contacting the developer fails, it leaves you little choice. You either leave your projects unsigned which is often not an option, or find a way around the problem.&lt;/p&gt;&lt;p&gt;I first learnt how to do this trick from &lt;a href="http://buffered.io/posts/net-fu-signing-an-unsigned-assembly-without-delay-signing/" target="_blank"&gt;OJ Reeve’s article on signing and unsigned assembly&lt;/a&gt;. The information is somewhat dated and doesn’t mention using the &lt;em&gt;/TYPELIST&lt;/em&gt; option which is crucial to make the round-trip in some scenarios. There are some other tools out there that do this but are also out of date, lack features, are difficult to configure and sometimes create unpredictable results.&lt;/p&gt;&lt;p&gt;Developers still struggle to overcome this problem so I wanted to create a really simple UI, command-line and API to simplify the process. I used the existing tools for inspiration and made sure that &lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Strong-Name Signer&lt;/a&gt; overcame all the issues that they currently have.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Strong-Name Signer&lt;/h2&gt;&lt;p&gt;This application is the answer to your assembly signing needs. Whether you prefer to use an GUI, the command-line or an API, &lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Strong-Name Signer&lt;/a&gt; covers all those bases.&lt;/p&gt;&lt;h5&gt;Features&lt;/h5&gt;&lt;ol&gt;&lt;li&gt;Supports strong-name signing assemblies built in v1.0 to v4.5 of the .NET framework.&lt;/li&gt;&lt;li&gt;Accessible via GUI, command-line or programmatic API.&lt;/li&gt;&lt;li&gt;Round-trips assembly attributes (32-bit preferred, x64 only etc.) correctly.&lt;/li&gt;&lt;li&gt;No need for an existing SNK file, one can be generated for you.&lt;/li&gt;&lt;li&gt;Automatically backs up your files before signing.&lt;/li&gt;&lt;li&gt;Does not require specific command-prompts or framework/SDK tools.&lt;/li&gt;&lt;li&gt;Update BAML resources with new references.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;h5&gt;User Interface&lt;/h5&gt;&lt;p&gt;Drag-and-drop files or directories and .NET assemblies will be detected. Kept it very simple so it’s accessible and understandable by any user.&lt;/p&gt;&lt;p&gt;&lt;a href="/posts/files/StrongNameSigner_UI.png"&gt;&lt;img style="border-top:0;border-right:0;border-bottom:0;border-left:0;display:inline" border="0" alt="" src="/posts/files/StrongNameSigner_UI_thumb.png" width="660" height="437" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h5&gt;Console&lt;/h5&gt;&lt;p&gt;Simple command-line options to automate from script files. At minimum you can provide an assembly file, the rest will be done automatically.&lt;/p&gt;&lt;p&gt;&lt;a href="/posts/files/StrongNameSigner_Console.png"&gt;&lt;img title="StrongNameSigner_Console" style="border-top:0;border-right:0;border-bottom:0;border-left:0;display:inline" border="0" alt="StrongNameSigner_Console" src="/posts/files/StrongNameSigner_Console_thumb.png" width="660" height="327" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h5&gt;API&lt;/h5&gt;&lt;p&gt;A public API is exposed you can reference &lt;em&gt;Brutal.Dev.StrongNameSigner.dll&lt;/em&gt; and make use of the static methods on &lt;em&gt;SigningHelper&lt;/em&gt; to perform strong-name signing tasks from code or PowerShell scripts.&lt;/p&gt;&lt;p&gt;&lt;a href="/posts/files/StrongNameSigner_Help.png"&gt;&lt;img title="StrongNameSigner_Help" style="border-top:0;border-right:0;border-bottom:0;border-left:0;display:inline" border="0" alt="StrongNameSigner_Help" src="/posts/files/StrongNameSigner_Help_thumb.png" width="660" height="420" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;-- &lt;strong&gt;&lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Download (v3.6.5.0)&lt;/a&gt;&lt;/strong&gt; --&lt;br /&gt;-- &lt;strong&gt;&lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_NuGet'])" href="https://www.nuget.org/packages/Brutal.Dev.StrongNameSigner/" target="_self"&gt;NuGet Package&lt;/a&gt;&lt;/strong&gt; --&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Other Existing Tools&lt;/h2&gt;&lt;p&gt;I investigated the following tools when developing &lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Strong-Name Signer&lt;/a&gt;. I want to give the authors credit since it was the shortcomings in these tools that were the basis for a lot of the features in &lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Strong-Name Signer&lt;/a&gt;.&lt;/p&gt;&lt;h4&gt;&lt;a href="https://as.codeplex.com" target="_blank"&gt;Assembly Signer&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This tool was last released in 2010 and contains some bugs that don’t make it a very reliable option.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Manual configuration to identify external tools location.&lt;/li&gt;&lt;li&gt;Doesn’t handle spaces in assembly paths.&lt;/li&gt;&lt;li&gt;Fails on certain round-trips (not using &lt;em&gt;/TYPELIST&lt;/em&gt;).&lt;/li&gt;&lt;li&gt;Requires an existing SNK file.&lt;/li&gt;&lt;li&gt;Can only sign DLL files and not EXE files (which are also assemblies).&lt;/li&gt;&lt;li&gt;Requires user input from the command-line / cannot be easily automated.&lt;/li&gt;&lt;li&gt;Can cause problems for a 2.0 assembly if the wrong tools are used.&lt;/li&gt;&lt;li&gt;Fails to recompile 64-bit platform targeted assemblies.&lt;/li&gt;&lt;li&gt;No graphical user interface.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;&lt;a href="https://signer.codeplex.com/" target="_blank"&gt;Signer&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This tool appears to be the most promising but has been abandoned since early 2007. It does most things correctly but has a number of &lt;a href="https://signer.codeplex.com/workitem/list/basic" target="_blank"&gt;open bugs&lt;/a&gt; that &lt;a onclick="_gaq.push(['_trackPageview','StrongNameSigner_Setup.exe'])" href="/download/StrongNameSigner_Setup.exe" target="_self"&gt;Strong-Name Signer&lt;/a&gt; fixes. I had the option of contributing to this project but I wanted to change the architecture in a way that I could unit test the functionality easily.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Requires running in Visual Studio 2005 command-prompt (2010 does not work).&lt;/li&gt;&lt;li&gt;Does not support .NET 4.x+ assemblies.&lt;/li&gt;&lt;li&gt;Requires an existing SNK file.&lt;/li&gt;&lt;li&gt;Requires special handling to recompile 64-bit platform targeted assemblies.&lt;/li&gt;&lt;li&gt;Performs manual re-writing of IL that are not required anymore with newer tools.&lt;/li&gt;&lt;li&gt;No graphical user interface.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Open Source&lt;/h2&gt;&lt;p&gt;The source code for this project is &lt;a href="https://github.com/brutaldev/StrongNameSigner/" target="_blank"&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Fri, 18 Oct 2013 17:28:00 Z</pubDate>
      <a10:updated>2026-03-11T10:41:58Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/error-reporting-with-pushingbox</guid>
      <link>https://brutaldev.com/post/error-reporting-with-pushingbox</link>
      <a10:author>
        <a10:name>werner</a10:name>
      </a10:author>
      <title>Error reporting with PushingBox</title>
      <description>&lt;p&gt;One of my users let me know they were using a cloud service called &lt;a href="http://pushingbox.com/" target="_blank"&gt;PushingBox&lt;/a&gt; to be notified of events in &lt;a href="http://www.deventerprise.net/DirectoryMonitor" target="_blank"&gt;Directory Monitor&lt;/a&gt;. I investigated what it can do and immediately thought of a couple of interesting use cases for this service. One of those use cases being the ability to send errors to yourself from your own software in the wild. I’ll explain a little about this &lt;u&gt;free&lt;/u&gt; service and show you some sample code of how to integrate it into your applications or automation scripts using Powershell.&lt;/p&gt;  &lt;h3&gt;The PushingBox service&lt;/h3&gt;  &lt;p&gt;&lt;a href="http://pushingbox.com/" target="_blank"&gt;PushingBox&lt;/a&gt; is a free service to anyone with a Google account. You can login and it will generate a demo email scenario for you automatically. There are however many more services you can link up to including popular iPhone and Android notification apps and even Twitter. The &lt;a href="http://www.pushingbox.com/api.php" target="_blank"&gt;API&lt;/a&gt; is simply an HTTP &lt;em&gt;GET&lt;/em&gt; or &lt;em&gt;POST&lt;/em&gt; using the “DeviceID” assigned to your scenario and optional parameters that you can use to augment your push messages.&lt;/p&gt;  &lt;h3&gt;Setting up a basic scenario&lt;/h3&gt;  &lt;p&gt;To get an email for errors in your application, setup a simple email scenario in &lt;a href="http://pushingbox.com/" target="_blank"&gt;PushingBox&lt;/a&gt; (My Scenarios –&amp;gt; Create scenario). You can use parameter names enclosed in $ signs for replacement when you make a call to the service (remember this isn’t limited to just email):&lt;/p&gt;  &lt;p&gt;&lt;a href="/posts/files/pushingbox_1.png"&gt;&lt;img title="PushingBox scenrio" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="PushingBox scenrio" src="/posts/files/pushingbox_1_thumb.png" width="444" height="352" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now you can use parameters with the same name in the URL or in your &lt;em&gt;POST&lt;/em&gt; and they will be replaced with those values when the email arrives in your inbox. When you’ve setup the scenario, remember to grab the “DeviceID”, you’ll need that to make a request to the service.&lt;/p&gt;  &lt;h3&gt;Making the service request in C#&lt;/h3&gt;  &lt;p&gt;The code to make a HTTP &lt;em&gt;POST&lt;/em&gt; in C# (or .NET in general) is very easy. Here is a basic static method (&lt;em&gt;using&lt;/em&gt; statements left out for brevity) that will &lt;em&gt;POST&lt;/em&gt; an exception to the service, remember to use your own devid in place of “YOUR_DEV_ID_HERE”.&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; PushingBoxHelper
{
  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Task PushError(&lt;span class="kwrd"&gt;string&lt;/span&gt; version, Exception error)
  {
    Contract.Requires(!&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(version));
    Contract.Assume(error != &lt;span class="kwrd"&gt;null&lt;/span&gt;);

    &lt;span class="rem"&gt;// Fire and forget but return in case you want to do something with it.&lt;/span&gt;
    &lt;span class="kwrd"&gt;return &lt;/span&gt;Task.Factory.StartNew(() =&amp;gt;
    {
      &lt;span class="kwrd"&gt;try&lt;/span&gt;
      {
        var post = &lt;span class="kwrd"&gt;new&lt;/span&gt; NameValueCollection();
        post.Add(&lt;span class="str"&gt;&amp;quot;devid&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;YOUR_DEV_ID_HERE&amp;quot;&lt;/span&gt;);
        post.Add(&lt;span class="str"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;, version);
        post.Add(&lt;span class="str"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;, error.ToString());

        &lt;span class="kwrd"&gt;using&lt;/span&gt; (var wc = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebClient())
        {
          wc.UploadValues(&lt;span class="str"&gt;&amp;quot;http://api.pushingbox.com/pushingbox&amp;quot;&lt;/span&gt;, post);
        }
      }
      &lt;span class="kwrd"&gt;catch&lt;/span&gt; (WebException we)
      {
        Log.Error(&lt;span class="str"&gt;&amp;quot;Failed to submit error information to PushingBox.&amp;quot;&lt;/span&gt;, we);
      }
    });
  }
}&lt;/pre&gt;

&lt;p&gt;Now whenever you get an error or unhandled exception in your application that you want to get notified about, just call the &lt;em&gt;PushingBoxHelper.PushError&lt;/em&gt; method and it will quietly try to push the information to &lt;a href="http://www.pushingbox.com/api.php" target="_blank"&gt;PushingBox’s API&lt;/a&gt; so you will get an email or a push to any other service you have configured.&lt;/p&gt;

&lt;h3&gt;Making the service request in Powershell&lt;/h3&gt;

&lt;p&gt;If you do not want to write any code or just need to push a notification from a script, you can easily use any of the examples on the &lt;a href="http://www.pushingbox.com/api.php" target="_blank"&gt;API section of their site&lt;/a&gt;. I opted for a Powershell script since I wouldn’t need any other dependencies such as Python, cURL or PHP.&lt;/p&gt;

&lt;p&gt;Be careful of doing a &lt;em&gt;GET&lt;/em&gt; since you will need to encode the parameters correctly otherwise data may get lost. This is the Powershell code to make a &lt;em&gt;GET&lt;/em&gt; request:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;##############&lt;/span&gt;
&lt;span class="rem"&gt;# GET Method #&lt;/span&gt;
&lt;span class="rem"&gt;##############&lt;/span&gt;
&lt;span class="kwrd"&gt;param&lt;/span&gt; (
  [string]$version = &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;,
  [string]$err = &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
)

&lt;span class="kwrd"&gt;begin&lt;/span&gt; {
  [System.Reflection.Assembly]::LoadWithPartialName(&lt;span class="str"&gt;&amp;quot;System.Web&amp;quot;&lt;/span&gt;) | out-null
  $get = &lt;span class="str"&gt;&amp;quot;http://api.pushingbox.com/pushingbox?devid=YOUR_DEV_ID_HERE&amp;amp;version=&amp;quot;&lt;/span&gt;
    + [System.Web.HttpUtility]::UrlEncode($version) + &lt;span class="str"&gt;&amp;quot;&amp;amp;error=&amp;quot;&lt;/span&gt; 
    + [System.Web.HttpUtility]::UrlEncode($err)
  (new-object System.Net.WebClient).DownloadString($get)
}&lt;/pre&gt;

&lt;p&gt;In the same light, you could also &lt;em&gt;POST&lt;/em&gt; from Powershell in a very similar way. I prefer this because it’s cleaner and you don’t need to encode anything, it will happen automatically for you:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;###############&lt;/span&gt;
&lt;span class="rem"&gt;# POST Method #&lt;/span&gt;
&lt;span class="rem"&gt;###############&lt;/span&gt;
&lt;span class="kwrd"&gt;param&lt;/span&gt; (
  [string]$version = &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;,
  [string]$err = &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
)

&lt;span class="kwrd"&gt;begin&lt;/span&gt; {
  [System.Reflection.Assembly]::LoadWithPartialName(&lt;span class="str"&gt;&amp;quot;System.Web&amp;quot;&lt;/span&gt;) | out-null
  $post = new-object System.Collections.Specialized.NameValueCollection
  $post.Add(&lt;span class="str"&gt;&amp;quot;devid&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;YOUR_DEV_ID_HERE&amp;quot;&lt;/span&gt;)
  $post.Add(&lt;span class="str"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;, $version)
  $post.Add(&lt;span class="str"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;, $err)
  $wc = new-object System.Net.WebClient
  $wc.UploadValues(&lt;span class="str"&gt;&amp;quot;http://api.pushingbox.com/pushingbox&amp;quot;&lt;/span&gt;, $post)
}&lt;/pre&gt;

&lt;p&gt;You can execute this script from the command-line using the named parameters to send the info you need: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;@powershell -NoProfile -ExecutionPolicy unrestricted -file pushingbox.ps1 -version &amp;quot;1.2.3.4&amp;quot; -err &amp;quot;Something went wrong!&amp;quot;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a onclick="javascript: _gaq.push([&amp;#39;_trackPageview&amp;#39;, &amp;#39;PushingBox_Powershell.zip&amp;#39;]);" href="/posts/files/PushingBox_Powershell.zip" target="_self"&gt;Download Powershell Script&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;I think this is an awesome service that could be used for a variety of scenarios where you would want instant notification via multiple channels when something interesting happens. Breaking builds triggering a Karotz, automating Tweets when you create a blog entry, there are so many… They also provide the source for certain Arduino shields so you can push notifications due to electrical/mechanical interaction with the environment, check out the videos on the home page for more cool ideas.&lt;/p&gt;
</description>
      <pubDate>Thu, 30 May 2013 19:47:13 Z</pubDate>
      <a10:updated>2013-06-01T06:48:59Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/maximum-lossless-image-compressionoptimization</guid>
      <link>https://brutaldev.com/post/maximum-lossless-image-compressionoptimization</link>
      <a10:author>
        <a10:name>Werner</a10:name>
      </a10:author>
      <title>Maximum lossless image compression and optimization</title>
      <description>&lt;p&gt;&lt;a href="http://www.hanselman.com" target="_blank"&gt;Scott Hanselman&lt;/a&gt; has referred to lossless image compression/optimization, especially PNG files, for quite some time. I’ve also been using these tools for ages but was surprised to hear how few people were actually aware of them. I also wanted to go a step further and see if there were ways to compress and optimize JPEGs and GIFs in the same lossless fashion, this is what I found…&lt;/p&gt;&lt;h3&gt;Image Compression Tools&lt;/h3&gt;&lt;p&gt;&lt;a href="http://pnggauntlet.com/" target="_blank"&gt;PNGGauntlet&lt;/a&gt; is a fantastic option which uses three different tools to perform the optimizations and in parallel. However, it doesn’t provide any optimizations for JPEG or (everyone's favorite format) GIF files although it can convert them to PNG. I like to be up to date as well and the tools that it uses are not always the latest that are out there. Then along comes &lt;a href="http://imageoptim.pornel.net/" target="_blank"&gt;ImageOptim&lt;/a&gt; and &lt;a href="http://trimage.org/" target="_blank"&gt;Trimage&lt;/a&gt; which run your images through a couple more tools but, the software is only available for Mac and Linux. Based on these, this is the list of tools I settled on:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.lcdf.org/gifsicle/" target="_blank"&gt;Gifsicle&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kokkonen.net/tjko/projects.html" target="_blank"&gt;Jpegoptim&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://jpegclub.org/jpegtran" target="_blank"&gt;Jpegtran&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://sourceforge.net/projects/optipng/" target="_blank"&gt;OptiPNG&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://pmt.sourceforge.net/" target="_blank"&gt;PNGcrush&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://advsys.net/ken/utils.htm" target="_blank"&gt;PNGOUT&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.walbeehm.com/download/index.html" target="_blank"&gt;DeflOpt&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;I won’t touch on any more information about these tools, visit the sites if you are interested in further information.&lt;/p&gt;&lt;h3&gt;Automation Batch File&lt;/h3&gt;&lt;p&gt;The secret is in the combination of all these tools to get the maximum compression possible, as well as using the correct options for best results. I didn't want to create a UI for it but also wanted to make it easy enough for anyone to use, so I wrote a &lt;a onclick="_gaq.push(['_trackPageview','BrutalImageOptimization_1.5.zip'])" href="/posts/files/BrutalImageOptimization_1.5.zip" target="_self"&gt;batch file&lt;/a&gt; to run your images through the tools in the correct order with the right options. All the requires is that you specify a directory path to recursively compress, this could easily be used as a shell extension as well.&lt;/p&gt;&lt;p&gt;The &lt;a onclick="_gaq.push(['_trackPageview','BrutalImageOptimization_1.5.zip'])" href="/posts/files/BrutalImageOptimization_1.5.zip" target="_self"&gt;download&lt;/a&gt; contains two batch files.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;em&gt;&lt;strong&gt;_Download.bat&lt;/strong&gt;&lt;/em&gt; will just pull and extract the required tools off the internet (PowerShell required) so that you can always have the latest versions.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;em&gt;_Optimize.bat&lt;/em&gt;&lt;/strong&gt; will do all the heavy lifting of trawling through a directory for images to compress.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="/posts/files/ImageCompressionScreenshot.png"&gt;&lt;img style="background-image:none;border-right-width:0;padding-left:0;padding-right:0;display:inline;border-top-width:0;border-bottom-width:0;border-left-width:0;padding-top:0" title="ImageCompressionScreenshot" border="0" alt="ImageCompressionScreenshot" src="/posts/files/ImageCompressionScreenshot_thumb.png" width="515" height="213" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Even the above screenshot was compressed from 20KB to 12KB, now imagine what this can do to your whole website or even games?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;NB:&lt;/strong&gt;Running this tool will overwrite the images in their respective directories, just be aware of that.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;NB2:&lt;/strong&gt; I have seen some rare scenarios recently where images that have gone through &lt;a href="http://www.walbeehm.com/download/index.html" target="_blank"&gt;DeflOpt&lt;/a&gt; do not render on certain mobile browsers. Be careful of this and remove the call if you experience this problem.&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;Using these tools is a fast and cheap way of reducing the bandwidth requirements for your websites and games. More people need to know that there are tools available like this and start making use of them. Stop wasting bandwidth and compress your images today, you have nothing to lose, not even image quality!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a onclick="_gaq.push(['_trackPageview','BrutalImageOptimization_1.5.zip'])" href="/posts/files/BrutalImageOptimization_1.5.zip" target="_self"&gt;Download Image Optimizer&lt;/a&gt;&lt;/strong&gt; – includes &lt;a href="http://www.7-zip.org/" target="_blank"&gt;7-zip&lt;/a&gt; command-line to extract the downloaded tools, don't use spaces in your path, some of the tools don't like it so the batch file doesn't try handle it.&lt;/p&gt;</description>
      <pubDate>Sun, 17 Jun 2012 19:46:00 Z</pubDate>
      <a10:updated>2015-08-15T18:54:26Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/logging-setup-in-5-minutes-with-nlog</guid>
      <link>https://brutaldev.com/post/logging-setup-in-5-minutes-with-nlog</link>
      <a10:author>
        <a10:name>werner</a10:name>
      </a10:author>
      <title>Logging setup in 5 minutes with NLog</title>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: The latest version of NLog breaks many of these tools by &lt;a href="https://groups.google.com/d/msg/nlog-users/lwEe2cHxYwE/7YI2D9H1-DcJ" target="_blank"&gt;including NLog data by default&lt;/a&gt;. The code has been updated to correct this problem by excluding it for both Sentinel and Harvester applications.&lt;/p&gt;  &lt;p&gt;The time has come where I needed to add some sophisticated logging to &lt;a href="http://brutaldev.com/page/Directory-Monitor.aspx" target="_blank"&gt;Directory Monitor&lt;/a&gt;. I know there are a couple of frameworks out there and &lt;a href="http://www.dotnetlogging.com/comparison/" target="_blank"&gt;this comparison chart&lt;/a&gt; lays it out quite nicely although it may be quite dated. So after some quick investigation on the free ones I decided to go with &lt;a href="http://nlog-project.org" target="_blank"&gt;NLog&lt;/a&gt;.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.theobjectguy.com/dotnetlog/" target="_blank"&gt;ObjectGuy Framework&lt;/a&gt; was really appealing at only a 40KB dependency. It's really simple and actually quite powerful, &lt;span style="text-decoration: line-through"&gt;I chose not to use it because as you can see in the chart, thread safety is questionable (not sure why or hasn’t been tested) and it doesn't have built in file rotation/archiving.&lt;/span&gt; - @&lt;em&gt;Lorne&lt;/em&gt; set me straight, the&amp;#160; &lt;a href="http://www.theobjectguy.com/dotnetlog/" target="_blank"&gt;ObjectGuy Framework&lt;/a&gt; does indeed have these features, however, my decision still stands for reasons beyond this. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://logging.apache.org/log4net/" target="_blank"&gt;Log4Net&lt;/a&gt; is a good option but the license didn’t agree with me because I want to use it for closed source applications. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/cc467894.aspx" target="_blank"&gt;Enterprise Library&lt;/a&gt; is a monster with lots of other dependencies, really only for large scale applications and if you want to extend it to do crazy things. Use it at work almost exclusively but not ideal for my small app. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://nlog-project.org/"&gt;NLog&lt;/a&gt; is 372KB, works in Mono (enterprise library as well), and has a build for WP7, all versions of the framework, Silverlight etc. Because of this I didn't mind checking it out because it immediately allows me to take that learning anywhere, to almost any application I build. There is also a great monitoring tools you can use along with it such as &lt;a href="http://sentinel.codeplex.com/" target="_blank"&gt;Sentinel&lt;/a&gt; or &lt;a href="http://harvester.codeplex.com/" target="_blank"&gt;Harvester&lt;/a&gt;. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h3&gt;Quick Setup&lt;/h3&gt;  &lt;p&gt;You can &lt;a href="http://nlog-project.org/download" target="_blank"&gt;download and install&lt;/a&gt; it or just add it through &lt;a href="http://nuget.org/profiles/jkowalski"&gt;NuGet&lt;/a&gt; in Visual Studio.&lt;/p&gt;  &lt;h3&gt;Configuration File&lt;/h3&gt;  &lt;p&gt;Just make a config called &lt;em&gt;NLog.config&lt;/em&gt; in the root of your project.&lt;/p&gt;  &lt;p&gt;When editing the configuration file, reference the XSD and you'll get intellisense and inline comments in Visual Studio: &lt;em&gt;&lt;strong&gt;Right-click the config -&amp;gt; Properties -&amp;gt; Schemas -&amp;gt; &amp;quot;C:\Program Files (x86)\Microsoft Visual Studio 10.0\xml\Schemas\NLog.xsd&amp;quot;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;If you have a separate config file, make sure you set the &lt;strong&gt;&amp;quot;Copy to Output Directory&amp;quot;&lt;/strong&gt; is set to &lt;strong&gt;&amp;quot;Copy Always&amp;quot;&lt;/strong&gt; to avoid many tears wondering why the logging doesn't work.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://nlog-project.org/wiki/Tutorial" target="_blank"&gt;&lt;img title="CopyToOutputDirectory" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="CopyToOutputDirectory" src="/posts/files/nlog_copytooutputdir.png" width="405" height="183" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You get &lt;a href="http://nlog-project.org/wiki/Layout_renderers"&gt;a lot of layout options&lt;/a&gt; to play with, this is my configuration file, it writes to the event log on error and fatal, creates a daily rolling log file (30 days max), everything is asynchronous and the configuration auto reloads on the fly if anything changes. It also writes to a location that you will have write access to in low privilege situations.&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;utf-8&amp;quot;&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;nlog&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://www.nlog-project.org/schemas/NLog.xsd&amp;quot;&lt;/span&gt;
      &lt;span class="attr"&gt;xmlns:xsi&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;/span&gt;
      &lt;span class="attr"&gt;autoReload&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;true&amp;quot;&lt;/span&gt;
      &lt;span class="attr"&gt;throwExceptions&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;variable&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;appName&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;YourAppName&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
  
  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;targets&lt;/span&gt; &lt;span class="attr"&gt;async&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;xsi:type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;File&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;default&amp;quot;&lt;/span&gt;            
            &lt;span class="attr"&gt;layout&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;${longdate} - ${level:uppercase=true}: ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;fileName&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;${specialfolder:ApplicationData}\${appName}\Debug.log&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;keepFileOpen&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;false&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;archiveFileName&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;${specialfolder:ApplicationData}\${appName}\Debug_${shortdate}.{##}.log&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;archiveNumbering&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Sequence&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;archiveEvery&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Day&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;maxArchiveFiles&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;30&amp;quot;&lt;/span&gt;
            &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
    
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;xsi:type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;EventLog&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;eventlog&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;source&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;${appName}&amp;quot;&lt;/span&gt;
            &lt;span class="attr"&gt;layout&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;${message}${newline}${exception:format=ToString}&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;targets&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;rules&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;logger&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;writeTo&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;default&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;minlevel&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Info&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;logger&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;writeTo&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;eventlog&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;minlevel&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Error&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;rules&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;nlog&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;h3&gt;&amp;#160;&lt;/h3&gt;

&lt;h3&gt;Code&lt;/h3&gt;

&lt;p&gt;This is the singleton class that I use which also includes targets for &lt;a href="http://sentinel.codeplex.com/"&gt;Sentinel&lt;/a&gt; and &lt;a href="http://harvester.codeplex.com/" target="_blank"&gt;Harvester&lt;/a&gt; when I'm running a debug build.&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NLog;
&lt;span class="kwrd"&gt;using&lt;/span&gt; NLog.Config;
&lt;span class="kwrd"&gt;using&lt;/span&gt; NLog.Targets;

&lt;span class="kwrd"&gt;namespace&lt;/span&gt; YourAppName.Logging
{
  &lt;span class="kwrd"&gt;internal&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Log
  {
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Logger Instance { get; &lt;span class="kwrd"&gt;private&lt;/span&gt; set; }
    &lt;span class="kwrd"&gt;static&lt;/span&gt; Log()
    {
&lt;span class="preproc"&gt;#if&lt;/span&gt; DEBUG
      &lt;span class="rem"&gt;// Setup the logging view for Sentinel - http://sentinel.codeplex.com&lt;/span&gt;
      var sentinalTarget = &lt;span class="kwrd"&gt;new&lt;/span&gt; NLogViewerTarget()
      {
        Name = &lt;span class="str"&gt;&amp;quot;sentinal&amp;quot;&lt;/span&gt;,
        Address = &lt;span class="str"&gt;&amp;quot;udp://127.0.0.1:9999&amp;quot;&lt;/span&gt;,
        IncludeNLogData = &lt;span class="kwrd"&gt;false&lt;/span&gt;
      };
      var sentinalRule = &lt;span class="kwrd"&gt;new&lt;/span&gt; LoggingRule(&lt;span class="str"&gt;&amp;quot;*&amp;quot;&lt;/span&gt;, LogLevel.Trace, sentinalTarget);
      LogManager.Configuration.AddTarget(&lt;span class="str"&gt;&amp;quot;sentinal&amp;quot;&lt;/span&gt;, sentinalTarget);
      LogManager.Configuration.LoggingRules.Add(sentinalRule);

      &lt;span class="rem"&gt;// Setup the logging view for Harvester - http://harvester.codeplex.com&lt;/span&gt;
      var harvesterTarget = &lt;span class="kwrd"&gt;new&lt;/span&gt; OutputDebugStringTarget()
      { 
        Name = &lt;span class="str"&gt;&amp;quot;harvester&amp;quot;&lt;/span&gt;,
        Layout = &lt;span class="str"&gt;&amp;quot;${log4jxmlevent:includeNLogData=false}&amp;quot;&lt;/span&gt;
      };
      var harvesterRule = &lt;span class="kwrd"&gt;new&lt;/span&gt; LoggingRule(&lt;span class="str"&gt;&amp;quot;*&amp;quot;&lt;/span&gt;, LogLevel.Trace, harvesterTarget);
      LogManager.Configuration.AddTarget(&lt;span class="str"&gt;&amp;quot;harvester&amp;quot;&lt;/span&gt;, harvesterTarget);
      LogManager.Configuration.LoggingRules.Add(harvesterRule);
&lt;span class="preproc"&gt;#endif&lt;/span&gt;

      LogManager.ReconfigExistingLoggers();

      Instance = LogManager.GetCurrentClassLogger();
    }
  }
}&lt;/pre&gt;

&lt;h3&gt;&amp;#160;&lt;/h3&gt;

&lt;h3&gt;Monitoring Tools&lt;/h3&gt;

&lt;p&gt;Let &lt;a href="http://sentinel.codeplex.com/" target="_blank"&gt;Sentinel&lt;/a&gt; or &lt;a href="http://harvester.codeplex.com/" target="_blank"&gt;Harvester&lt;/a&gt; run &lt;span style="text-decoration: underline"&gt;all the time&lt;/span&gt; when you are debugging and it will pick up everything that your app is logging (screenshots from their respective project pages).&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sentinel.codeplex.com/" target="_blank"&gt;&lt;img title="Sentinel" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Sentinel" src="/posts/files/sentinel.jpg" width="556" height="484" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://harvester.codeplex.com/" target="_blank"&gt;&lt;img title="Harvester" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Harvester" src="/posts/files/harvester.jpg" width="550" height="433" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;&amp;#160;&lt;/h3&gt;

&lt;h3&gt;Start Logging&lt;/h3&gt;

&lt;p&gt;Now to start log anything, anywhere in your app, just use the static log instance.&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;try&lt;/span&gt;
{
  Log.Instance.Debug(&lt;span class="str"&gt;&amp;quot;We're going to throw an exception now.&amp;quot;&lt;/span&gt;);
  Log.Instance.Warn(&lt;span class="str"&gt;&amp;quot;It's gonna happen!!&amp;quot;&lt;/span&gt;);
  &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ApplicationException();
}
&lt;span class="kwrd"&gt;catch&lt;/span&gt; (ApplicationException ae)
{
  Log.Instance.ErrorException(&lt;span class="str"&gt;&amp;quot;Error doing something...&amp;quot;&lt;/span&gt;, ae);
}&lt;/pre&gt;

&lt;p&gt;This is a really simple and effective setup but you can of course get really advanced from there, all in the configuration file.&lt;/p&gt;

&lt;p&gt;The cool thing about having a separate log file is that you can send someone a modified log file that will write trace level logging or even ship the log to a web server or database for you to check out.&lt;/p&gt;

&lt;p&gt;Logging is so important in any live application. This just makes it easy while still leaving it very powerful if you ever need to use that power. The logger itself is pretty easy to debug because of it's massive internal logging and setting to expose the internals of it.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;
</description>
      <pubDate>Tue, 27 Mar 2012 18:37:00 Z</pubDate>
      <a10:updated>2020-01-02T07:33:05Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/generating-statistics-from-svn</guid>
      <link>https://brutaldev.com/post/generating-statistics-from-svn</link>
      <a10:author>
        <a10:name>Werner</a10:name>
      </a10:author>
      <title>Generating statistics from SVN</title>
      <description>&lt;p&gt;A subversion repository contains a tome of information about your code and looking at this data can prove very useful in identifying trends in development. Quite some time ago I put together some batch files and a small application (for filtering SVN logs) to automate the generating of statistics using &lt;a href="http://www.statsvn.org/index.html" target="_blank"&gt;StatSVN&lt;/a&gt; and &lt;a href="http://sourceforge.net/projects/svnstat/" target="_blank"&gt;SvnStat&lt;/a&gt;. I am sharing this with you today.&lt;/p&gt;
&lt;p&gt;Example of high level change overview chart. This is one of the many charts and graphs available in &lt;a href="http://www.statsvn.org/index.html" target="_blank"&gt;StatSVN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/posts/files/StatSVN_Sample.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="StatSVN_Sample" src="/posts/files/StatSVN_Sample_thumb.png" alt="StatSVN_Sample" width="576" height="367" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Example of changes per user graph. This is one of the many charts and graphs available in &lt;a href="http://sourceforge.net/projects/svnstat/" target="_blank"&gt;SvnStat&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/posts/files/SvnStat_Sample.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="SvnStat_Sample" src="/posts/files/SvnStat_Sample_thumb.png" alt="SvnStat_Sample" width="559" height="248" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The reason I&amp;rsquo;ve looked at this again was because I wanted to change the helper application to a C# script file (.csx) and use &lt;a href="http://msdn.com/roslyn" target="_blank"&gt;Microsoft Roslyn&lt;/a&gt; to execute it. I never liked the fact that my simple filtering and looping was in an application, it should be scripted. Unfortunately, &lt;a href="http://msdn.com/roslyn" target="_blank"&gt;Roslyn&lt;/a&gt; does not yet support the &lt;em&gt;XDocument&lt;/em&gt; type so I couldn&amp;rsquo;t easily convert it and I really didn&amp;rsquo;t want to do it in PowerShell cause I&amp;rsquo;m lazy...&lt;/p&gt;
&lt;h3&gt;Stat Helper&lt;/h3&gt;
&lt;p&gt;In &lt;a onclick="javascript: _gaq.push(['_trackPageview', 'BrutalStatHelper_1.0.zip']);" href="/posts/files/BrutalStatHelper_1.0.zip" target="_self"&gt;the download&lt;/a&gt; you will find the following files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;_branches.config&lt;/strong&gt;: Local checkout locations that statistics will be generated for.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;_excludepaths.config&lt;/strong&gt;: Path to exclude like merges from &amp;ldquo;trunk&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;_excludeusers.config&lt;/strong&gt;: Users to exclude from the statistics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GenerateLog.bat&lt;/strong&gt;: Updates the checkout location and extracts the log.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GenerateStats.bat&lt;/strong&gt;: Calls the external libraries to generates the statistics pages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BrutalStatHelper.exe&lt;/strong&gt;: Calls the batch files and performs filtering on log data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BrutalStatHelper.cs&lt;/strong&gt;: The source code for the filtering app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: The code is not the best in the world and I don&amp;rsquo;t claim it to be production code by any means but it does the job as intended. Hopefully it gives people more ideas on improving the stats that get generated. What about making a contents page linking to all the stats index pages for example?&lt;/p&gt;
&lt;h3&gt;Instructions&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a onclick="javascript: _gaq.push(['_trackPageview', 'BrutalStatHelper_1.0.zip']);" href="/posts/files/BrutalStatHelper_1.0.zip" target="_self"&gt;Download Stat Helper&lt;/a&gt; - Extract the files anywhere you want&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.java.com/download" target="_blank"&gt;Install Java&lt;/a&gt; - You need this for both stats libraries&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.sliksvn.com/download" target="_blank"&gt;Install command-line SVN&lt;/a&gt; - You need this to extract the log&lt;/li&gt;
&lt;li&gt;Edit &lt;strong&gt;_branches.config&lt;/strong&gt; with the SVN checkout locations you want to generate stats for&lt;/li&gt;
&lt;li&gt;Edit &lt;strong&gt;_excludepaths.config&lt;/strong&gt; with any paths you want to ignore&lt;/li&gt;
&lt;li&gt;Edit &lt;strong&gt;_excludeusers.config&lt;/strong&gt; with any usernames you want to ignore&lt;/li&gt;
&lt;li&gt;Run &lt;em&gt;&lt;strong&gt;BrutalStatHelper.exe&lt;/strong&gt;&lt;/em&gt; to generate the stats&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Stats will be generated in subdirectories of the directory you are running &lt;em&gt;BrutalStatHelper.exe&lt;/em&gt; from&lt;em&gt;.&lt;/em&gt; Open up &lt;em&gt;index.html&lt;/em&gt; for &lt;a href="http://www.statsvn.org/index.html" target="_blank"&gt;StatSVN&lt;/a&gt; and &lt;em&gt;more\index.html&lt;/em&gt; for &lt;a href="http://sourceforge.net/projects/svnstat/" target="_blank"&gt;SvnStat&lt;/a&gt; info from their respective directories when the process is complete.&lt;/p&gt;
&lt;h3&gt;Extra Information&lt;/h3&gt;
&lt;p&gt;Check out the additional options available for each library. The helper will get you going quickly but it&amp;rsquo;s up to you to investigate more and see what you can get out of generating statistics.&lt;/p&gt;
&lt;p&gt;Call the helper from a Windows Scheduled Task or during an automated build (CI) to keep the stats fresh.&lt;/p&gt;
&lt;p&gt;Start looking at and analyzing more statistics. I use &lt;a href="http://rescuetime.com/ref/332532" target="_blank"&gt;RescueTime&lt;/a&gt; to gather statistics about my daily work and like any statistical analysis, it highlights areas of strength and weakness where you may not normally be able to see it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: When I introduced this into the workplace it was greeted with interest from management but major apprehension from co-workers. A lot of people don&amp;rsquo;t like the &amp;ldquo;big brother&amp;rdquo; aspect and because the data could be skewed or misinterpreted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a onclick="javascript: _gaq.push(['_trackPageview', 'BrutalStatHelper_1.0.zip']);" href="/posts/files/BrutalStatHelper_1.0.zip" target="_self"&gt;Download Stat Helper&lt;/a&gt;&lt;/strong&gt; - includes source code and both statistics libraries.&lt;/p&gt;
</description>
      <pubDate>Sun, 23 Oct 2011 12:13:00 Z</pubDate>
      <a10:updated>2011-10-30T19:46:36Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/reset-bugnet-admin-password</guid>
      <link>https://brutaldev.com/post/reset-bugnet-admin-password</link>
      <a10:author>
        <a10:name>Werner</a10:name>
      </a10:author>
      <title>Reset BugNET admin password</title>
      <description>&lt;p&gt;I recently ran into a situation where I had the wrong/forgot the password for my BugNET bug/project tracker site and had no way to get it back. The reasons for password recovery failure are probably due to one of the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You didn’t use a real email address.&lt;/li&gt;&lt;li&gt;You didn’t set a security question or forgot the answer.&lt;/li&gt;&lt;li&gt;It’s the admin account, did you think it will be that easy?&lt;/li&gt;&lt;li&gt;It’s the &lt;em&gt;only&lt;/em&gt; admin account so you can’t login and change it.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;I needed a fast way to get the password reset and because everything is hashed and salted in the database (as it should be) and it’s not that easy to just update the tables with your own generated password. The only thing I could find was &lt;a href="http://www.bugnetproject.com/Forums/tabid/54/forumid/1/threadid/2317/scope/posts/Default.aspx" target="_blank"&gt;unlocking your account from the DB&lt;/a&gt;. That got me to the next point…&lt;/p&gt;&lt;p&gt;Since &lt;a href="http://bugnetproject.com" target="_blank"&gt;BugNET&lt;/a&gt; uses the built-in &lt;a href="http://msdn.microsoft.com/en-us/library/yh26yfzy.aspx" target="_blank"&gt;ASP.NET Membership Role Provider&lt;/a&gt;, it’s actually quite easy as long as you have access to the web server or FTP site. This is a hack I’ve used before and will work for other sites as well, though usually if you have access to the web server you’re probably a big enough admin to do this and more.&lt;/p&gt;&lt;h3&gt;Instructions&lt;/h3&gt;&lt;p&gt;Create a page called &lt;em&gt;&lt;strong&gt;ResetPass.aspx&lt;/strong&gt;&lt;/em&gt; with the following code (or &lt;a onclick="_gaq.push(['_trackPageview','BugNET_ResetPass.zip'])" href="/posts/files/BugNET_ResetPass.zip" target="_self"&gt;download it&lt;/a&gt;):&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language="C#" AutoEventWireup="true" %&amp;gt;&lt;/span&gt;
&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;
  &lt;span class="kwrd"&gt;try&lt;/span&gt;
  {
    MembershipUser mem = Membership.GetUser(&lt;span class="str"&gt;"admin"&lt;/span&gt;);
    &lt;span class="kwrd"&gt;if&lt;/span&gt; (mem.IsLockedOut)
    {
      mem.UnlockUser();
    }
    mem.ChangePassword(mem.ResetPassword(), &lt;span class="str"&gt;"password"&lt;/span&gt;);
  }
  &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception ex)
  {
    Response.Write(ex.ToString());
  }
&lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;

&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Admin Password Reset&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;p&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Done resetting password...&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;p&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;p&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Please delete this page from the web server!&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;p&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Upload the page to the root of your &lt;a href="http://bugnetproject.com" target="_blank"&gt;BugNET&lt;/a&gt; website and browse to &lt;strong&gt;&lt;em&gt;http://yourwebdomain/ResetPass.aspx&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;If anything goes wrong your default error page will show or the usual yellow page of death, if not the password would have been reset to “&lt;em&gt;password&lt;/em&gt;” and you’ll get a nice message.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;NB: &lt;/strong&gt;&lt;u&gt;Delete the page from your web server as it poses a major security risk!&lt;/u&gt;&lt;/p&gt;&lt;p&gt;The code is simple pretty self-explanatory so I'm not going to go into detail, use it, don't use it (at your own risk).&lt;/p&gt;&lt;b&gt;&lt;div style="padding-bottom:0;margin:0;padding-left:0;padding-right:0;display:inline;float:none;padding-top:0" id="scid:F60BB8FA-6F02-4999-8F5E-9DD4E92C4DA7:f086d1a4-85b8-4bd2-8586-e35c5ee43c06" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;a href="/posts/files/BugNET_ResetPass.zip" target="_self" onclick="_gaq.push(['_trackPageview','BugNET_ResetPass.zip'])"&gt;Download sample code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/b&gt;</description>
      <pubDate>Mon, 22 Aug 2011 19:38:00 Z</pubDate>
      <a10:updated>2025-07-27T03:52:50Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="true">https://brutaldev.com/post/show-calendar-on-click-datetimepicker-control</guid>
      <link>https://brutaldev.com/post/show-calendar-on-click-datetimepicker-control</link>
      <a10:author>
        <a10:name>Werner</a10:name>
      </a10:author>
      <title>Show calendar on click DateTimePicker control</title>
      <description>&lt;p&gt;Something that’s common-place on the web is clicking a text box to pop-up a calendar. What if you wanted the calendar to pop-up when simply clicking on any area of a &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx" target="_blank"&gt;System.Windows.Forms.DateTimePicker&lt;/a&gt; control? You would expect the control to have a method to easily do this but when you notice there isn't even a public &lt;em&gt;Click&lt;/em&gt; event things start looking bleak...&lt;/p&gt;  &lt;p&gt;I found a way to get this to work by sending a &amp;quot;mouse button down/up&amp;quot; message to the control in the area where the regular display button is. By forcing this extra click in the right area it's as if you just clicked the button yourself. So I made an extension method with some parts borrowed which add an additional method on the &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx" target="_blank"&gt;System.Windows.Forms.DateTimePicker&lt;/a&gt; control.&lt;/p&gt;  &lt;p&gt;We want to make a click happen here (in the &lt;font color="#ff0000"&gt;red&lt;/font&gt; box):&lt;/p&gt;  &lt;p&gt;&lt;a href="/posts/files/datetimepicker.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="datetimepicker" border="0" alt="datetimepicker" src="/posts/files/datetimepicker_thumb.png" width="212" height="32" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So we can use some native calls as well as basic math to find a good position to click it and send the message like so:&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Runtime.InteropServices;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Forms;

&lt;span class="kwrd"&gt;namespace&lt;/span&gt; Brutal.Dev.Extenstions
{
  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; DateTimePickerExtensions
  {
&lt;span class="preproc"&gt;#if&lt;/span&gt; NETCF
    [DllImport(&lt;span class="str"&gt;&amp;quot;coredll&amp;quot;&lt;/span&gt;)]
&lt;span class="preproc"&gt;#else&lt;/span&gt;
    [DllImport(&lt;span class="str"&gt;&amp;quot;user32&amp;quot;&lt;/span&gt;)]
&lt;span class="preproc"&gt;#endif&lt;/span&gt;
    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;extern&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; SendMessage(IntPtr hWnd, &lt;span class="kwrd"&gt;uint&lt;/span&gt; uMsg,
                                  &lt;span class="kwrd"&gt;int&lt;/span&gt; wParam, &lt;span class="kwrd"&gt;int&lt;/span&gt; lParam);

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; WM_LBUTTONDOWN = 0x0201;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; WM_LBUTTONUP = 0x0202;

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ShowCalendar(&lt;span class="kwrd"&gt;this&lt;/span&gt; DateTimePicker picker,
                                    MouseEventArgs clickEvent)
    {
      &lt;span class="kwrd"&gt;if&lt;/span&gt; (picker != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
      {
        &lt;span class="rem"&gt;// Remove any existing event to prevent an infinite loop.&lt;/span&gt;
        var suppressor = &lt;span class="kwrd"&gt;new&lt;/span&gt; EventSuppressor(picker);
        suppressor.Suppress();

        &lt;span class="rem"&gt;// Get the position on the button.&lt;/span&gt;
        &lt;span class="kwrd"&gt;int&lt;/span&gt; x = picker.Width - 10;
        &lt;span class="kwrd"&gt;int&lt;/span&gt; y = picker.Height / 2;
        &lt;span class="kwrd"&gt;int&lt;/span&gt; lParam = x + y * 0x00010000;

        &lt;span class="rem"&gt;// Ignore if the calendar button was clicked&lt;/span&gt;
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (clickEvent.X &amp;lt; picker.Width - 35)
        {
          SendMessage(picker.Handle, WM_LBUTTONDOWN, 1, lParam);
          SendMessage(picker.Handle, WM_LBUTTONUP, 1, lParam);
        }

        suppressor.Resume();
      }
    }
  }
}&lt;/pre&gt;

&lt;h3&gt;Explanation&lt;/h3&gt;

&lt;p&gt;If you are using the .NET Compact Framework you need to import &lt;em&gt;coredll&lt;/em&gt; instead for the &lt;a href="http://www.pinvoke.net/default.aspx/coredll/sendmessage.html" target="_blank"&gt;P/Invoke for SendMessage&lt;/a&gt;&lt;em&gt;&lt;/em&gt; so the compiler directive is there to support that.&lt;/p&gt;

&lt;p&gt;The extra mouse click event info requirement is so that we can detect where the click happened and ignore it if it was actually on the calendar button (because otherwise it clicks again hiding the calendar – DOH!).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;EventSuppressor&lt;/em&gt; is something I picked up &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/c873d460-2f7d-41f6-8149-3055a9dd5e7a" target="_blank"&gt;on this discussion&lt;/a&gt; to stop any other events from firing and also prevent infinite loops calling your mouse events over and over (the code for this in included in the &lt;a href="/posts/files/BrutalDatePickerExample.zip" target="_self" onClick="javascript: _gaq.push(['_trackPageview', 'BrutalDatePickerExample.zip']);"&gt;sample code&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If you attach to the &lt;em&gt;MouseDown&lt;/em&gt; or &lt;em&gt;MouseUp&lt;/em&gt; event you can easily add one line to call the extension on you control and just like that, your calendar will pop-up: &lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Brutal.Dev.Extenstions;

&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; dateTimePicker1_MouseDown(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, MouseEventArgs e)
{    
  dateTimePicker1.ShowCalendar(e);
}&lt;/pre&gt;

&lt;p&gt;If you're a &lt;strong&gt;VB.NET&lt;/strong&gt; kind of person, you can easily convert this code into &lt;strong&gt;VB.NET&lt;/strong&gt; using &lt;a href="http://www.developerfusion.com/tools/convert/csharp-to-vb/" target="_blank"&gt;this online tool&lt;/a&gt; which I’ve tried and it works pretty well.&lt;/p&gt;
&lt;b&gt;
  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:F60BB8FA-6F02-4999-8F5E-9DD4E92C4DA7:84e68d8d-ab27-42ad-a108-57bfd51fc0fe" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;a href="/posts/files/BrutalDatePickerExample.zip" target="_self" onClick="javascript: _gaq.push(['_trackPageview', 'BrutalDatePickerExample.zip']);"&gt;Download sample code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/b&gt;
</description>
      <pubDate>Wed, 04 May 2011 23:03:00 Z</pubDate>
      <a10:updated>2011-06-13T11:25:59Z</a10:updated>
    </item>
  </channel>
</rss>