<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Durgaprasad Gorti's WebLog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/dgorti/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/dgorti/feed.xml</link>
    <description />
    <lastBuildDate>Sat, 13 May 2017 17:38:45 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Calling JavaScript functions from TSQL</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/calling-javascript-functions-from-tsql</link>
      <pubDate>Thu, 11 May 2017 08:23:40 GMT</pubDate>
      <dc:creator><![CDATA[Durgaprasad Gorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/?p=195</guid>
      <description><![CDATA[Why would you ever want to call a JavaScript function from TSQL? It does sound crazy, but there may...]]></description>
      <content:encoded><![CDATA[Why would you ever want to call a JavaScript function from TSQL? It does sound crazy, but there may be a case where you might want to do this. Even if it not for real production use, it might be useful for some test scenarios. And it is very cool and you get to brag about it.

Here is a case I ran into. We have a web app where we do lot of currency conversions. These calculations are done in JavaScript. On the back end, in the SQL Server, we needed to do similar calculations in TSQL. However depending on the datatypes, precision and scale and rounding algorithms used, we ran into a case where the JavaScript calculations in the browser sometimes differed by a penny from the calculations done in the back end. We fixed these, but we needed to test that the JavaScript calculations matched the calculations made in the data base.

The first thing I tried is to see if I can host a JavaScript engine in C#. The idea is that if I can call a JS function from C#, I can then use that output to compare with my data base by using EF or ADO.NET or LINQ. I can then turn this in a test case I can run regularly. The goal is to eliminate UI automation.

I looked at various posts and git hub projects, and eventually landed on <a href="https://github.com/Microsoft/Chakra-Samples/tree/master/Chakra%20Samples/JSRT%20Win32%20Hosting%20Samples/Edge%20JSRT%20Samples/C%23">this</a> git hub Edge/Chakra JavaScript hosting sample. Note that there may be other JavaScript hosting samples, but the one I mentioned is the latest. It uses the  Chakra.dll (Edge) instead of JScript9.dll (IE 11). I also looked into the MSDN docs for JSRT (JavaScript runtime) for more information, but our friends in JSRT did not get to spend too much time with the API documentation, so it was not that useful. At the end, I got what I needed from the sample and reading git bug issue list on the sample.


Here I will show you the bare bones code to host JSRT in C#. [ I am assuming that you downloaded the sample from git hub link above. We will keep most that sample except the main program.cs. Remove that and lets start clean]
<pre>JavaScriptRuntime jsRuntime = JavaScriptRuntime.Create();
JavaScriptContext jsContext = jsRuntime.CreateContext();
</pre>
Once you create a JavaScriptRuntime and JavaScriptContext, you are pretty much ready to run code.
The one small detail is that you need to know is that you need to set a current context on the thread of execution before you do anything in that context.
<pre>using (new JavaScriptContext.Scope(jsContext))
{
....
}
</pre>
In this case, we want to run some JS script. So lets get the source defined in a string for convinience
<pre>private static string jsSource = @"
                  var some_var = Math.pow(10,6);
                  function convert(amount, sourceToUSDRate, targetToUSDRate)
                  {
                      ... your JS Code here ...
                  }";
</pre>
So your using statement above will now look like this.
<pre>using (new JavaScriptContext.Scope(jsContext))
{
    // run the script so functions get defined.
    JavaScriptContext.RunScript(jsSource);
}
</pre>
Now that the script ran, your function is defined, and now you can get hold of the function in C#.
Apparently there is a different way to do this, that is by calling ParseScript(). I could not get that to work, So I will skip that path.

JS has the concept of Global Object. A function defined in your code (outside of any object) is a function of the global object.
So first we need to get hold of the global object, ask for the "id" of the function using the name of the function and then get the function. All of this brings back the memories of IDispatch.
<pre>        static JSEngine()
        {
            jsRuntime = JavaScriptRuntime.Create();
            jsContext = jsRuntime.CreateContext();

            using (new JavaScriptContext.Scope(jsContext))
            {
                // run the script so functions get defined.
                JavaScriptContext.RunScript(jsSource);

                // Get the convert function handle.
                Native.JsGetGlobalObject(out jsGlobalObj);
                JavaScriptPropertyId id;
                if (Native.JsGetPropertyIdFromName("convert", out id) != JavaScriptErrorCode.NoError)
                {
                    throw new Exception("Convert function not found");
                }
                Native.JsGetProperty(jsGlobalObj, id, out jsConvertFunc);
            }
        }
</pre>
The type of the <code>jsConvertFunc</code> is <code>JavaScriptValue</code>
A couple of points here. Most of what you deal with in JSRT are the JavaScriptValue types.
A JavaScriptValue cane a function, an object, a number, a string or an array. So what we did so far is to get hold of a value that represents a function. Once you have that all that remains is to invoke that function. Here is the code for that.

There is a <strong>Gotcha</strong> here. When you call a function, the first parameter is the "this" pointer to the object on which the function is called on. In our case there is no specific object, it is the global object. That's why we pass the global object we obtained earlier as the first parameter.
Here is the code we have so far.
<pre>namespace EdgeJSHost
{
    public class JSEngine
    {
        private static JavaScriptRuntime jsRuntime;
        private static JavaScriptContext jsContext;
        private static JavaScriptValue jsConvertFunc;
        private static JavaScriptValue jsGlobalObj;
        private static string jsSource = @"
                    shiftValue = Math.pow(10,6);
                    function convert(amount, sourceToUSDRate, targetToUSDRate)
                    {
                      .... your JS code...
                    }
                ";

        static JSEngine()
        {
            jsRuntime = JavaScriptRuntime.Create();
            jsContext = jsRuntime.CreateContext();

            using (new JavaScriptContext.Scope(jsContext))
            {
                // run the script so functions get defined.
                JavaScriptContext.RunScript(jsSource);

                // Get the convert function handle.
                Native.JsGetGlobalObject(out jsGlobalObj);
                JavaScriptPropertyId id;
                if (Native.JsGetPropertyIdFromName("convert", out id) != JavaScriptErrorCode.NoError)
                {
                    throw new Exception("Convert function not found");
                }
                Native.JsGetProperty(jsGlobalObj, id, out jsConvertFunc);
            }
        }

        public static double JSCurrencyConvertFunc(
                    double amount,
                    double sourceToUSDRate,
                    double targetToUSDRate)

        {
            double resultVal;
            using (new JavaScriptContext.Scope(jsContext))
            {
                // convert args
                JavaScriptValue[] jsArguments = new JavaScriptValue[4];
                jsArguments[0] = jsGlobalObj;
                jsArguments[1] = JavaScriptValue.FromDouble(amount);
                jsArguments[2] = JavaScriptValue.FromDouble(sourceToUSDRate);
                jsArguments[3] = JavaScriptValue.FromDouble(targetToUSDRate);

                JavaScriptValue result;
                JavaScriptErrorCode err = Native.JsCallFunction(jsConvertFunc, jsArguments, (ushort)jsArguments.Length, out result);
                if (err != JavaScriptErrorCode.NoError)
                {
                    throw new Exception($"{err} - Error calling function");
                }

                JavaScriptValueType type = result.ValueType;
                if (type == JavaScriptValueType.Number)
                {
                    resultVal = result.ToDouble();
                }
                else if (type == JavaScriptValueType.String)
                {
                    resultVal = double.Parse(result.ToString());
                }
                else
                {
                    throw new Exception("Invalid return type from Convert Java Script function");
                }
            }
            return resultVal;
        }
    }
 }
</pre>
See how we got the return value and then can test the type of that value. Depending on the type,
we can convert the JavaScriptValue to a desired C# type.

At this point a simple console app can use the class we created to call the JS function we have
and use it to test various inputs and integrate with the rest of the test framework, call the back end data base and compare values. Pretty cool!.
<pre>     double val = JSEngine.JSCurrencyConvertFunc(220.00d, 1.0000000d, 0.7375991148811d);
     Console.WriteLine(val);
</pre>
While this is really cool stuff, I wanted to see if I can take it one step further. I wanted to be able to integrate this into the SQL, and do comparisons directly in TSQL. As you know, SQL Server supported CLR integration since 2005. So we could expose our C# function above as a user defined function in SQL via SQL CLR. Once we do that, you could call that UDF in your stored procedures, triggers, views, etc.
So you would be calling CLR from SQL and the CLR code hosts the JavaScript engine, and runs your JavaScript function.

But before that we need to expose a static function from our <code>JSEngine</code> class above
<pre>
        [Microsoft.SqlServer.Server.SqlFunction(
            DataAccess = DataAccessKind.None,
            SystemDataAccess = SystemDataAccessKind.None,
            IsPrecise = true,
            IsDeterministic = true
        )]
        public static SqlMoney JSCurrencyConvert(SqlMoney amount, SqlDecimal sourceToUSDRate, SqlDecimal targetToUSDRate)
        {
            double amountd = amount.ToDouble();
            double sourceToUSDRated = sourceToUSDRate.ToDouble();
            double targetToUSDRated = targetToUSDRate.ToDouble();
            double result = JSCurrencyConvertFunc(amountd, sourceToUSDRated, targetToUSDRated);
            return new SqlMoney(result);
        }
</pre>

Now we have a C# assembly (dll) that can be used with SQL CLR. 

We need to do a bit of preparation.
<ol>
 	<li>Enable CLR in SQL.
<pre>  sp_configure 'show advanced options', 1;  
  GO  
  RECONFIGURE;  
  GO  
  sp_configure 'clr enabled', 1;  
  GO  
  RECONFIGURE;  
  GO  
</pre>
</li>
 	<li>You need to have permission to register unsafe assemblies. In your SQL Sever Management Studio, go to your login, and under securables, set the unsafe assemblies to GRANT</li>
 	<li>You need to set TRUSTWORTHY ON
<pre>ALTER DATABASE SET TRUSTWORTHY ON;</pre>
</li>
 	<li>Create Assembly in CLR
<pre>                  
        CREATE ASSEMBLY  your assembly name without the dll ext
        FROM 'Your assembly path'
        WITH PERMISSION_SET = UNSAFE
</pre>
</li>
 	<li>Map the C# function to a user defined function in SQL.
<pre>
  CREATE FUNCTION JSCurrencyConvert(@amount money, @sourceToUSDRate as decimal(25,13), @targetToUSDRate as decimal(25,13)) 
  returns money 
  as 
  external name [EdgeJSHost].[EdgeJSHost.JSEngine].JSCurrencyConvert
</pre>
</li>

</ol>

<strong>Here is another Gotcha </strong>. If the class is under a name space, you need to enclose the
path to the class in [] like [NameSpace1.Namespace2.Class]. It took me a while to figure out how to get this to work. 

Anyways, we now have the assembly in the database, mapped the function in C# to an SQL UDF. When you call the UDF in SQL, it will call into CLR which hosts the JS Engine and runs your JavaScript. Let try that.

<pre>
  declare @amount money
  declare @s as decimal(25,13)
  declare @t as decimal(25,13)
  set @amount = 345
  set @s = 1.000000000
  set @t = 0.7375991148811

  select dbo.JSCurrencyConvert(@amount, @s, @t)

</pre>

That's how you call a Java Script function from SQL via CLR.]]></content:encoded>
    </item>
    <item>
      <title>Getting supported character sets of an OPOS printer</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/getting-supported-character-sets-of-an-opos-printer</link>
      <pubDate>Tue, 31 Mar 2015 16:58:26 GMT</pubDate>
      <dc:creator><![CDATA[Durgaprasad Gorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2015/03/31/getting-supported-character-sets-of-an-opos-printer/</guid>
      <description><![CDATA[It is good to return to this blog after several years of not blogging anything.I have been working...]]></description>
      <content:encoded><![CDATA[<p>It is good to return to this blog after several years of not blogging anything.<br />I have been working on the Dynamics AX / Retail system for sometime, particularly on the&nbsp;<br />hardware related to Point of Sale software. You know, the cash drawers, barcode scanners, printers etc.</p>
<p>Recently someone has asked me, when configuring the retail module in AX, particularly the hardware profile,&nbsp;<br />how to know exactly what character set to use for a particular OPOS device. Let me address that here.&nbsp;</p>
<p>When speaking about OPOS, there are several layers of components. Your application uses <br />what is called Common Control Objects. The common control objects call the service objects.<br />The following picture is lifted directly from the UPOS specification document.&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/38/27/6283.Capture.PNG" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-38-27/6283.Capture.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/38/27/6283.Capture.PNG" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-38-27/6283.Capture.PNG" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Now, the service objects are the controls or "software" provided by a particular vendor of particular device.</p>
<p>The common control objects are vendor independent and are generally available for use at&nbsp;https://www.monroecs.com/oposccos_current.htm</p>
<p>The standard configuration of the Service Objects is through the &nbsp;windows registry.</p>
<p>Becasue the service objects are "generally" 32 bit COM objects you need to use the 32 bit regedt32 to view the<br />registry configuration for service objects.</p>
<p>Under <br />HKEY_LOCAL_MACHINE\Software there should be a key called <strong>OLEforRetail<br /></strong>Under that key there should be another key called &nbsp;<strong>ServiceOPOS<br /></strong>Under that key, there are entries for individual device classes such as <strong>CashDrawer, LineDisplay, POSPrinter etc.<br /></strong>These are the device classes. Under each device class you will find the manufacturer specific entries<br /><br />Under that you can find the COM prog id of the service object. Once you find the service object you can write <br />a simple script to create and call methods on the service object and even interact with that device.</p>
<p>You don't even need visual studio - just using note pad, you can write simple 2 or 3 line VB Script&nbsp;<br />code<br />Set so = CreateObject("progid")<br />WScript.echo so.CharacterSetList</p>
<p>That should give you the list of character sets supported and you can&nbsp;<br />then use the correct character set in the configuration of AX Hardware profile.&nbsp;</p>
<p>Next, in a series of posts, I want to show you how to write a fake Printer, Cash Register&nbsp;<br />or any OPOS fake or mock device so you can test your software with these fake<br />devices without having to run around finding a piece of OPOS hardware to test <br />your POS software. Stay tuned...&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Outlook quick steps and mouse button programming</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/outlook-quick-steps-and-mouse-button-programming</link>
      <pubDate>Thu, 01 Dec 2011 00:08:39 GMT</pubDate>
      <dc:creator><![CDATA[Durgaprasad Gorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2011/12/01/outlook-quick-steps-and-mouse-button-programming/</guid>
      <description><![CDATA[I have acciddentally discovered a cool tip.
Most of you use the Outlook Quick Steps correct? Now,...]]></description>
      <content:encoded><![CDATA[<p>I have acciddentally discovered a cool tip.</p>
<p>Most of you use the Outlook Quick Steps correct? Now, one of the ways you can customize is&nbsp;to create your own Quick Steps</p>
<p>For example, I have a quick step to move my mail to a folder. Then I assigned a keyboard short cut. Unfortunately the <br />keyboard shortcuts are CTRL+SHIFT+1 through 12 and I have to pick one of those. The problem is that <br />that key sequence CTRL + SHIFT + 2 is a difficult combination to type.</p>
<p>While I was playing with the Microsoft Mouse, I discoverd that the middle button can have programmating specific settings.</p>
<p>Now when you select that, you can actually assign a macro to that button. By choosing the macro there will be an<br />editor that popsup and you enter your key strokes,delays, and otehr stuff there. Bingo!</p>
<p>Now, when I am in outlook, just click the middle mouse button, which sends CTRL+SHIFT+2 and the <br />mail moves to a folder.</p>
<p>Hope some one finds this useful :-)</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>"Start In" folder is not honored when you launch the command prompt as an administrator</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/start-in-folder-is-not-honored-when-you-launch-the-command-prompt-as-an-administrator</link>
      <pubDate>Mon, 10 May 2010 18:55:11 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2010/05/10/start-in-folder-is-not-honored-when-you-launch-the-command-prompt-as-an-administrator/</guid>
      <description><![CDATA[Say you have a command prompt shortcut. You set it to start in a particular folder by editing...]]></description>
      <content:encoded><![CDATA[<p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_2.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_thumb.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_thumb.png" width="177" height="244"></a> <a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_4.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_thumb_1.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_thumb_1.png" width="244" height="182"></a> <a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_6.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_6.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_thumb_2.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/StartInfolderisnothonoredwhenyoulaunchth_10A0C/image_thumb_2.png" width="244" height="123"></a> </p> <p>Say you have a command prompt shortcut. You set it to start in a particular folder by editing the properties <br>as shown above. Then you also set the Advanced properties to launch it as Administrator. <br><br>You launch the shortcut and it puts you in windows\System32. Basically blows away the config you did and decides <br>to put you in the system32 folder. Countless times, I find myself irritated by this behavior. I launch the prompt, <br>i type in a command like Dir, and it goes on and on listing the system32 contents. I have to change <br>back to the folder I want to proceed with any work that I wanted to. </p> <p>My good friends&nbsp; tell me that this is a "security" feature. But this is annoying to me.<br><br>In any case, the simple fix is to add <strong><font size="4">C:\Windows\System32\cmd.exe /k cd /d c:\users\public<br></font></strong>By adding the configuration of the "Start in" folder a second time, the command prompt would behave the way you want to behave. </p> <p>Not a great tip, I agree, but it annoyed me enough that I wanted to post a note.</p>]]></content:encoded>
    </item>
    <item>
      <title>Embed Ruby code directly inside MSBUILD file using DLR and IronRuby</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/embed-ruby-code-directly-inside-msbuild-file-using-dlr-and-ironruby</link>
      <pubDate>Fri, 11 Sep 2009 02:27:00 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2009/09/11/embed-ruby-code-directly-inside-msbuild-file-using-dlr-and-ironruby/</guid>
      <description><![CDATA[You know the MSBUILD drill. Everything is a custom task. Need to add two numbers? Write a custom...]]></description>
      <content:encoded><![CDATA[<P>You know the MSBUILD drill. Everything is a custom task. Need to add two numbers? Write a custom task.<BR>Need to replace spaces in a string with '_' character? Write a custom task. I don't like the overhead of <BR>writing custom tasks. </P>
<P>What you have with MSBuild is bascially an XML file and good amount of "build logic". Anything that you <BR>could have done with even a simple batch script has to be in a custom task.&nbsp; I don't like to have<BR>some code in the MSBUILD script and some other code in a custom task tucked in somewhere else. <BR>I want to be able to embed a simple script in the MSBuild file. I searched for it and I could not find a way.</P>
<P>I have to to roll my own way? Well thats what I did. <BR>I found that I could actually embedding IronRuby in MSBuild file. </P>
<P><CODE><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>Project</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>DefaultTargets</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>ExecuteTest</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>xmlns</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>https://schemas.microsoft.com/developer/msbuild/2003</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;<BR>&lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>UsingTask</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>TaskName</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>MSBuildTasks.RubyHost</FONT></FONT><FONT size=2>" </FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>AssemblyFile</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>MSBuild_Ruby.dll</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>/&gt;<BR>&lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>PropertyGroup</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;</FONT></FONT></P>
<P><FONT color=#0000ff>&nbsp;&nbsp; <FONT size=2><FONT size=2>&lt;</FONT></FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>RubyScript</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;&lt;![CDATA[</FONT></FONT></P>
<P><FONT color=#808080 size=2><FONT color=#808080 size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; File.open("MyTestFile.txt", "w") do |file|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.times do |i|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file.write "This is line #{i}\n"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>end<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "success"</FONT></FONT></P>
<P><FONT color=#0000ff size=2><FONT color=#0000ff size=2>]]&gt;&lt;/</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>RubyScript</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;</FONT></FONT></P>
<P><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&lt;/</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>PropertyGroup</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;</P>
<P mce_keep="true">&lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>Target</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>Name</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>ExecuteTest</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>RubyHost</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>ScriptSource</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>$(RubyScript)</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>Output</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>TaskParameter</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>ReturnValue</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>PropertyName</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>ReturnValue</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> /&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>RubyHost</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>Message</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> </FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>Text</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>=</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>$(ReturnValue)</FONT></FONT><FONT size=2>"</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2> /&gt;<BR>&lt;/</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>Target</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;<BR>&lt;/</FONT></FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>Project</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>&gt;</P></FONT></FONT>
<P mce_keep="true">With this,&nbsp;you can see that the ruby code is embedded&nbsp;within the MSBUILD file itself, and no need to maintain <BR>separate set of tasks. NO MORE a task for adding, deleting, and multiplying numbers!!.</P>
<P mce_keep="true">Of course you need 1 custom task that hosts the ruby engine and runs the ruby code for you.&nbsp;&nbsp;<BR></P>
<P mce_keep="true">Here is the source code for the custom task </P><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> System;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> System.Collections.Generic;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> System.Text;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> Microsoft.Scripting.Hosting;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> IronRuby.Runtime;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> IronRuby;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>using</FONT></FONT><FONT size=2> Microsoft.Build.Framework;</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>namespace</FONT></FONT><FONT size=2> MSBuildTasks</P>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>public</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>class</FONT></FONT><FONT size=2> </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>RubyHost</FONT></FONT><FONT size=2> : </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>ITask</P></FONT></FONT><FONT size=2>
<P>{ </P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>private</FONT></FONT><FONT size=2> </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>IBuildEngine</FONT></FONT><FONT size=2> engine; </P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>public</FONT></FONT><FONT size=2> </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>IBuildEngine</FONT></FONT><FONT size=2> BuildEngine</P>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>get</FONT></FONT><FONT size=2> { </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>return</FONT></FONT><FONT size=2> engine; }</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>set</FONT></FONT><FONT size=2> { engine = </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>value</FONT></FONT><FONT size=2>; }</P>
<P>} </P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>private</FONT></FONT><FONT size=2> </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>ITaskHost</FONT></FONT><FONT size=2> host;</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>public</FONT></FONT><FONT size=2> </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>ITaskHost</FONT></FONT><FONT size=2> HostObject</P>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>get</FONT></FONT><FONT size=2> { </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>return</FONT></FONT><FONT size=2> host; }</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>set</FONT></FONT><FONT size=2> { host = </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>value</FONT></FONT><FONT size=2>; }</P>
<P>}</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>private</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>string</FONT></FONT><FONT size=2> scriptSource;</P>
<P>[</FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>Required</FONT></FONT><FONT size=2>]</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>public</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>string</FONT></FONT><FONT size=2> ScriptSource</P>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>get</FONT></FONT><FONT size=2> { </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>return</FONT></FONT><FONT size=2> scriptSource; }</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>set</FONT></FONT><FONT size=2> { scriptSource = </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>value</FONT></FONT><FONT size=2>; }</P>
<P>}</P>
<P mce_keep="true">&nbsp;</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>private</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>string</FONT></FONT><FONT size=2> returnValue;</P>
<P>[</FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>Output</FONT></FONT><FONT size=2>]</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>public</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>string</FONT></FONT><FONT size=2> ReturnValue</P>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>get</FONT></FONT><FONT size=2> { </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>return</FONT></FONT><FONT size=2> returnValue; }</P>
<P>}</P>
<P mce_keep="true">&nbsp;</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>private</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>void</FONT></FONT><FONT size=2> LogMessage(</FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>string</FONT></FONT><FONT size=2> message, </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>MessageImportance</FONT></FONT><FONT size=2> imp)</P>
<P>{</P>
<P></FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>BuildMessageEventArgs</FONT></FONT><FONT size=2> args = </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>new</FONT></FONT><FONT size=2> </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>BuildMessageEventArgs</FONT></FONT><FONT size=2>(</P>
<P>message, </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>string</FONT></FONT><FONT size=2>.Empty, </FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>"RubyIntegration.RunRake"</FONT></FONT><FONT size=2>, </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>MessageImportance</FONT></FONT><FONT size=2>.Normal);</P>
<P>engine.LogMessageEvent(args);</P>
<P>}</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>public</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>bool</FONT></FONT><FONT size=2> Execute()</P>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>try</P></FONT></FONT><FONT size=2>
<P>{</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>var</FONT></FONT><FONT size=2> runtime = IronRuby.</FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>Ruby</FONT></FONT><FONT size=2>.CreateRuntime();</P>
<P></FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>ScriptEngine</FONT></FONT><FONT size=2> Ruby_Engine = runtime.GetEngine(</FONT><FONT color=#a31515 size=2><FONT color=#a31515 size=2>"Ruby"</FONT></FONT><FONT size=2>);</P>
<P></FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>ScriptSource</FONT></FONT><FONT size=2> src = Ruby_Engine.CreateScriptSourceFromString(scriptSource, Microsoft.Scripting.</FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>SourceCodeKind</FONT></FONT><FONT size=2>.Statements);</P>
<P></FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>Object</FONT></FONT><FONT size=2> result = src.Execute();</P>
<P>returnValue = result.ToString();</P>
<P>}</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>catch</FONT></FONT><FONT size=2> (</FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>Exception</FONT></FONT><FONT size=2> ex)</P>
<P>{</P>
<P>LogMessage(ex.ToString(), </FONT><FONT color=#2b91af size=2><FONT color=#2b91af size=2>MessageImportance</FONT></FONT><FONT size=2>.High);</P>
<P>}</P>
<P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>return</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>true</FONT></FONT><FONT size=2>;</P>
<P>}</P>
<P>}</P>
<P>}</P>
<P>I know that the code is quick and dirty and can be improved on this. <BR>I hope to enhance it soon. But I can't contain my joy and wanted to <BR>spread the love.... </P>
<P>Let me know if you liked this idea and share any further thoughts</P></FONT></CODE>]]></content:encoded>
    </item>
    <item>
      <title>Ruby On Rails with IIS 7 - Reloaded</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/ruby-on-rails-with-iis-7-reloaded</link>
      <pubDate>Wed, 17 Jun 2009 18:15:11 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2009/06/17/ruby-on-rails-with-iis-7-reloaded/</guid>
      <description><![CDATA[In my last post I mentioned getting into Ruby and making MABUILD and rake work together. I think I...]]></description>
      <content:encoded><![CDATA[<p>In my last post I mentioned getting into Ruby and making MABUILD and rake work together. <br>I think I found a better way to do the same with PowerShell integration, which I will <br>write up sometime this week.</p> <p>Now, it is inevitable that Ruby leads to Ruby On Rails. I needed to get RoR work on my dev machine with VISTA SP1 and IIS7.<br>I was little surprised to find that there are very few articles on this. A few I found were outdated. Then I landed on Ruslan's <a href="https://ruslany.net/2008/08/ruby-on-rails-in-iis-70-with-url-rewriter/">post</a>. <br>It got me to a point but then I was stuck. So I walked to Ruslan's office and we discussed this quite a bit. I went back to my office and researched this some more.<br>I finally got this working after about 3 days of stumbling around. So here is the post that builds on Ruslan's <br>post and clarifies a few things. Mostly I wanted to make sure that even folks like me, who are beginners in the Ruby universe, <br>could follow the steps to get RoR and IIS 7 working.</p> <p>I am starting here with a clean system with VISTA SP1. </p> <p><u><strong>Step 1:</strong>&nbsp; INSTAL IIS AND CGI</u><br></p> <p>Go to control panel, Programs and Features and then select "Turn features on or off". You can get there quicker<br>by typing appwiz.cpl on a command prompt and then selecting "Turn features on or off " from the left side<br><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_2.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_2.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb.png" width="199" height="175"></a> </p> <p>From the features select World Wide Web Services<br>and make sure that you select the following </p> <ol> <li><font color="#008000"><strong>REQUIRED: CGI. This is the same as the FastCGI support</strong></font>  <li><strong><font color="#008000">REQUIRED: IIS Management Console</font></strong>  <li><strong><font color="#0000ff">OPTIONAL: Custom HTTP Features: Static Content, etc<br>[We will use this to check if IIS is installed correctly]</font></strong>  <li><strong><font color="#0000ff">OPTIONAL : Other health and diagnostics info as <br>Shows in the picture below</font></strong></li></ol> <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_8.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_8.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_3.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_3.png" width="321" height="485"></a> </p> <p>Click OK and let the Windows install IIS and FastCGI for you.</p> <p><u><strong>Step 2:</strong>&nbsp; VERIFY THAT IIS IS WORKING</u></p> <p>Once installed you can verify that the IIS is installed by going to the <br><a href="https://localhost/iisstart.htm">https://localhost/iisstart.htm</a><br><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_10.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_10.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_4.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_4.png" width="244" height="163"></a> </p> <p>Please note: If you did not enable STATIC CONTENT feature when you installed, you will get a 404 NOT FOUND. <br>You see the file right there but you still get 404 NOT FOUND. Security aside, it would be great <br>if there is a more easier way to diagnose this, I ended up researching this for more than 3 hours wondering<br>why am I getting the 404 not found. Anyway, now you know and you don't need to waste those hours. <br>Again, enable the static content feature to be able to see this static content page. </p> <p>&nbsp;</p> <p><u><strong>Step 3: INSTALL RUBY</strong></u></p> <p>Navigate to <a title="Ruby 1.8.6 One-Click Installer" href="https://rubyforge.org/frs/download.php/47082/ruby186-27_rc2.exe">Ruby 1.8.6 One-Click Installer</a> and install RUBY<br><font color="#ff0000">CAUTION: DONT INSTALL TO A FOLDER that has SPACES in the name or its path.&nbsp; like "C:\program Files\Ruby"<br>I had strange errors and I had to reinstall RUBY. It is best to stick with the C:\Ruby.</font> <br>As a side and tangential note, what's up with installing to C:\Ruby? Aren't the people in that <br>alternate universe hate installing to root directories like C:\? Why can't it default to C:\Program Files\Ruby?<br>RUBY is supposed to make you HAPPY!! Huh!</p> <p>&nbsp;</p> <p><u><strong>Step 4: VERIFY RUBY IS IN THE PATH</strong></u></p> <p>RUBY Installation should have added the C:\ruby\bin [or whatever the path you gave] to the path. <br>Open a command prompt; type "SET PATH" and look at the path. You should see the ruby\bin in the path.<br>If not, no big deal, add ruby to the path in the System Variables. <br>Type ruby -help to see that you get some help on the screen. This verifies that Ruby is indeed installed. </p> <p>&nbsp;</p> <p><u><strong>Step 5: INSTALL RAILS</strong></u></p> <p>on a command prompt, type "Gem Install Rails". [The dependencies are already <br>included, contrary to popular belief that suggests the -includedependencies option<br>is required to do so]<br>Once installed, do "gem list" to list the gems locally installed. You should see <br>something like this - which should include rails. </p> <p>C:\Windows\system32&gt;gem list  <p>*** LOCAL GEMS ***  <p>actionmailer (2.3.2)<br>actionpack (2.3.2)<br>activerecord (2.3.2)<br>activeresource (2.3.2)<br>activesupport (2.3.2)<br>fxri (0.3.6)<br>fxruby (1.6.16)<br>hpricot (0.6.164)<br>log4r (1.0.5)<br>ptools (1.1.6)<br><strong><font color="#008000" size="4">rails (2.3.2)</font></strong><br>rake (0.8.7, 0.8.1)<br>ruby-opengl (0.60.0)<br>test-unit (2.0.1)<br>win32-api (1.2.1, 1.2.0)<br>win32-clipboard (0.4.4)<br>win32-dir (0.3.2)<br>win32-eventlog (0.5.0)<br>win32-file (0.5.5)<br>win32-file-stat (1.3.1)<br>win32-process (0.5.9)<br>win32-sapi (0.1.4)<br>win32-sound (0.4.1)<br>windows-api (0.2.4)<br>windows-pr (0.9.3) <p><u><strong>Step 6: Install FASTCGI Update for IIS7<br><a title="https://www.microsoft.com/downloads/details.aspx?FamilyID=19600729-8470-4956-a276-200450d814bd&amp;displaylang=en&amp;Hash=CUX9vrzjILBL3AghMTxtsgswXdorhJT4JhGOGHeRvFL9AQlhlkUkNtnVcQflyQD7VwtQyh51gAxTnsa4f4OlBA%3d%3d" href="https://www.microsoft.com/downloads/details.aspx?FamilyID=19600729-8470-4956-a276-200450d814bd&amp;displaylang=en&amp;Hash=CUX9vrzjILBL3AghMTxtsgswXdorhJT4JhGOGHeRvFL9AQlhlkUkNtnVcQflyQD7VwtQyh51gAxTnsa4f4OlBA%3d%3d">https://www.microsoft.com/downloads/details.aspx?FamilyID=19600729-8470-4956-a276-200450d814bd&amp;displaylang=en&amp;Hash=CUX9vrzjILBL3AghMTxtsgswXdorhJT4JhGOGHeRvFL9AQlhlkUkNtnVcQflyQD7VwtQyh51gAxTnsa4f4OlBA%3d%3d</a></strong></u></p> <p><strong><font color="#0000ff" size="3">I am not quite sure if this is a general update for FastCGI or just for PHP. To avoid hitting my head against a wall, I installed this anyway.</font></strong></p> <p><br><u><strong>Step 7: Pretend that you are hosting a website called&nbsp; RoRIIS7 </strong></u></p> <p>Add a HOST Entry so that the name resolver won't go out to the DNS Server. <br><font color="#ff0000" size="3">Don't forget to use an administrative command prompt from which you <br></font><font color="#ff0000" size="3">launch notepad. edit Windows\System32\Driver\Etc\Hosts like below. </font><br><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_20.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_20.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_8.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_8.png" width="472" height="267"></a>  </p> <p><strong><u>Step 8&nbsp; Add a new Website to IIS</u></strong></p> <blockquote> <p>Go to C:\InetPub [or where ever you installed the inetpub to]</p> <p>create a directory called RoRIIS7. We are not touching the wwwroot directory for this experiment.<br>i will post an update dealing with default web site later. <br></p> <p>Open MMC, Add the Internet Services Manager to the MMC Console.<br>Expand the IIS Manager. It will show the local web server. <br>Click on Add WebSite to add a website like below<br><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_12.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_12.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_2.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_2.png" width="378" height="260"></a> </p></blockquote> <blockquote> <p>Here we are adding a new WebSite RoRIIS7<br>mapped to the physical directory C:\Inetpub\RoRIIS7 <br>Please note that the HostName we are using<strong><font color="#0000ff" size="3"> </font></strong><font color="#0000ff" size="3"><strong>RoRIIS7</strong></font><br><strong><font color="#0000ff">AND that I am using port 8080<br></font></strong><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_18.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_18.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_7.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_7.png" width="408" height="398"></a> </p> <p>The website should start.</p></blockquote> <div><strong><u>Step 9 Verify that the new website is working</u></strong></div> <blockquote> <div>To verify that the website is working, copy the IISSTART.HTM and the <br>welcome.png files into the RoRIIS7 directory. <br>Then navigate to <a href="https://RoRIIS7/iisstart.htm">https://RoRIIS7/iisstart.htm</a><br>You should see a page similar to step 2. </div> <div>&nbsp;</div></blockquote> <div><strong><u>Step 10 Generate a RAILS APP</u></strong></div> <blockquote> <div>NOTE: When you generate the Rails app, make sure that <br>you use -D option. -D option generated the dispatchers<br>for CGI and FastCGI</div></blockquote> <blockquote> <table border="0" cellspacing="0" cellpadding="2" width="400"> <tbody> <tr> <td valign="top" width="400"><font color="#0000ff">Luanch CMD prompt as administrator</font></td></tr> <tr> <td valign="top" width="400"><font color="#0000ff">cd C:\inetpub\RoRIIS7<br><strong><font size="4">rails -D MyApp<br></font></strong>cd MyApp<br>ruby script\generate Controller Test Index</font></td></tr></tbody></table> <p>&nbsp;</p></blockquote> <p><strong><u>Step 11 Generate a RAILS APP</u></strong></p> <blockquote> <p>Modify the app\controllers\test_controller.rb like below<br>This will enable it to display some test when we navigate to this URL</p> <p>class TestController &lt; ApplicationController<br>&nbsp;&nbsp; def index<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; render :text=&gt;"The index action"<br>&nbsp;&nbsp; end<br>&nbsp;&nbsp; def about<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; render :text=&gt;"The about action"<br>&nbsp;&nbsp; end<br>end</p></blockquote> <p><strong><u>Step 12 Hook up IIS to Ruby</u></strong></p> <blockquote> <p>So far, we have IIS Setup and a Ruby App Setup<br>We need to hook them together now. <br></p> <p>Go to the MMC/Internet Services Manager, Go to the site RoRIIS7 in the <br>MMC snapin. We are now going to add a module mapping</p> <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_22.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_22.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_9.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_9.png" width="373" height="324"></a> </p> <p>We are mapping * ==&gt; All requests To the FastCGI Module. <br>The executable we use is of course the ruby interpreter. <br>But then there you see a PIPE character and the path to the <br>dispatch.fcgi. The reason is that Ruby by iteself does not handle<br>the FCGI. The FCGI handling is provided by the Rails <br>Dispatcher. </p> <p>Once you add the mapping go to <br>C:\windows\system32\inetsrv\config and open <br>applicationhost.config<br>In this config file you should see a section for fast cgi</p> <p>&lt;fastCgi&gt;<br>&nbsp;&nbsp;&nbsp; &lt;application fullPath="C:\ruby\bin\ruby.exe" arguments="C:\inetpub\RoRIIS7\MyApp\public\dispatch.fcgi" /&gt;<br>&lt;/fastCgi&gt; <p> and another section for<br>the module mapping. </p> <p>&lt;/system.webServer&gt;<br>&lt;location path="RoRIIS7"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;system.webServer&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;handlers&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="RubyFastCGI" path="*" verb="*" modules="FastCgiModule" scriptProcessor="C:\ruby\bin\ruby.exe|C:\inetpub\RoRIIS7\MyApp\public\dispatch.fcgi" resourceType="Unspecified" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/handlers&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/system.webServer&gt; <p><br>These sections are created for you when you added the module mapping <br>through the GUI above. Now, you could directly edit the file yourself if you wanted to. <br><br>Sensing the smell of completion you go to <a href="https://roriis7/test/about">https://roriis7/test/about</a> <br>but then you are greeted by the<br> <a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_24.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_24.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_10.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_10.png" width="420" height="316"></a></p></blockquote> <p><strong><u>Step 13 Fixing Permissions</u></strong></p> <blockquote> <p> When working with IIS, I always turn on the security audit.<br>Turning the security audit on can it self be somewhat of a challenge. <br>Without going into detail, what is required apparently is to <br>grant NETWORK Service Account permissions to the RoRIIS7 Folder. <br>Since Ruby will write some logfiles etc to that dir. You could<br>possibly fine tune this later if you needed to. <br><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_26.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_26.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_11.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_11.png" width="286" height="348"></a> </p> <p>Do an IISRESET and go visit <a href="https://roriis7:8080/test/about">https://roriis7:8080/test/about</a><br>OOPS! What went wrong now?<br><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_28.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_28.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_12.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_12.png" width="685" height="254"></a> <br>Apparently the RAILS needs SQLITE and it is not happy that it can't find SQLLite</p></blockquote> <p><strong><u>Step 14 Installing SQLITE and the SQLLITE GEM</u></strong></p> <blockquote> <p>Download SQLITE-3.6.15 ZIP file and place the exe in the Ruby\Bin directory<br>this is the command line config&nbsp; tool</p></blockquote> <blockquote> <p>Download SQLITEDLL-3.6.15 ZIP and place the dll in the system32 directory.<br>This is the engine for SQLITE. </p></blockquote> <blockquote> <p>Now we need to install the GEM</p> <p><a title="https://blog.anlek.com/2008/09/installing-updating-sqlite3-on-windows/" href="https://blog.anlek.com/2008/09/installing-updating-sqlite3-on-windows/">https://blog.anlek.com/2008/09/installing-updating-sqlite3-on-windows/</a> has the <br>tip on getting this done<br><br><strong><code>gem install --version 1.2.3 sqlite3-ruby</code></strong></p></blockquote> <p><strong><u>Step 14 COMPLETE!!!</u></strong></p> <blockquote> <p><strong><u>Now you navigate to <a href="https://roriis7:8080/test/about">https://roriis7:8080/test/about</a></u></strong></p> <p><strong><u>and you should see</u></strong></p> <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_30.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_30.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_13.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/RubyOnRailswithIIS7Reloaded_14757/image_thumb_13.png" width="791" height="279"></a> <br><br>There you go ladies and gentlemen, I give you Ruby On Rails with IIS7</p></blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Calling RAKE from MSBUILD</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/calling-rake-from-msbuild</link>
      <pubDate>Sun, 07 Jun 2009 19:08:27 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2009/06/07/calling-rake-from-msbuild/</guid>
      <description><![CDATA[These days, I am spending quite a bit of time with Ruby,&nbsp; Ruby On Rails and RAKE. I am not into...]]></description>
      <content:encoded><![CDATA[<p>These days, I am spending quite a bit of time with Ruby,&nbsp; Ruby On Rails and RAKE. I am not into the Ruby religion yet,<br>and not drinking the ruby Kool-aid that much. Just a little. </p> <p>I needed to integrate the TeamBuild (Team System) with Ruby/RAKE.&nbsp; So in other words, I needed to <br> call RAKE from MSBUILD. It turned out to be slightly harder than I expected. I am new to MSBUILD, Ruby and RAKE, <br>so I had to figure out quite a few of things.</p> <p>I ended up writing a custom task. From the custom task, I called Rake and captured the output. <br>Definitely not rocket science, but getting everything working properly is not easy either. </p> <p>So here it is. </p> <p>First the custom task. </p> <p>Here is the entire code file</p> <p>using System;<br>using System.Collections.Generic;<br>using System.Linq;<br>using System.Text;<br>using Microsoft.Build.Framework;<br>using System.Diagnostics;<br>using System.IO;<br>namespace RubyIntegration<br>{<br>&nbsp;&nbsp;&nbsp; public class RunRake : ITask<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private IBuildEngine engine;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IBuildEngine BuildEngine<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return engine; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { engine = value; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private ITaskHost host;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ITaskHost HostObject<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return host; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { host = value; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string RakeFileDir;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Required]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string RakeFileDirectory<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return RakeFileDir; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { RakeFileDir = value; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string RubyInstallDir;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Required]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string RubyInstallDirectory<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return RubyInstallDir; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { RubyInstallDir = value; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string RakeTargetName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Required]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string RakeTarget<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return RakeTargetName; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { RakeTargetName = value; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private string strOutput;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Output]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string RakeOutput<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return strOutput; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set { strOutput = value; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void LogMessage(string message, MessageImportance imp)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BuildMessageEventArgs args = new BuildMessageEventArgs(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; message, string.Empty, "RubyIntegration.RunRake", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; engine.LogMessageEvent(args);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool Execute()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool result = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("***** MSBUILD&nbsp; &lt;--&gt; RAKE ********", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Preparing to invoke Rake", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("*** INPUTS", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Ruby Install Directory " + RubyInstallDir, MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Rake File Directory " + RakeFileDirectory, MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("RakeTargetName " + RakeTargetName, MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("***", MessageImportance.Normal);  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("*** CHEKING PARAMETERS", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Directory.Exists(RubyInstallDir)){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Ruby Install Directory " + RubyInstallDir + " exists.", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Ruby Install Directory " + RubyInstallDir + " does not exist - exiting task.", MessageImportance.High);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string RubyPath = Path.Combine(RubyInstallDir, "ruby.exe");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (File.Exists(RubyPath))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Ruby.exe is found at " + RubyPath, MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("Ruby.exe is not found at path" + RubyPath + " . - exiting task.", MessageImportance.High);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string RakePath = Path.Combine(RubyInstallDir, "rake");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (File.Exists(RakePath))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("rake file (rake ruby code) is found at " + RakePath, MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("rake file (rake ruby code) is is not found at path" + RakePath + ". - exiting task.", MessageImportance.High);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage("***", MessageImportance.Normal);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessStartInfo info = new ProcessStartInfo();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.RedirectStandardOutput = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.UseShellExecute = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.FileName = RubyPath;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.Arguments = RakePath + " "&nbsp; + RakeTargetName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info.WorkingDirectory = RakeFileDirectory;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process p = Process.Start(info);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strOutput = p.StandardOutput.ReadToEnd();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p.WaitForExit();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Exception ex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogMessage(ex.ToString(), MessageImportance.High);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return result;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>}  <p>&nbsp; <p>Here is the MSBUILD FILE  <p>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br>&lt;Project xmlns="<a href="https://schemas.microsoft.com/developer/msbuild/2003&quot;">https://schemas.microsoft.com/developer/msbuild/2003"</a>&gt;<br>&nbsp; &lt;UsingTask AssemblyFile="RunRake.dll" TaskName="RubyIntegration.RunRake" /&gt;<br>&nbsp; &lt;Target Name="default" &gt;<br>&nbsp;&nbsp;&nbsp; &lt;RunRake&nbsp;&nbsp;&nbsp; RubyInstallDirectory ="C:\ruby\bin"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RakeFileDirectory="$(MSbuildProjectDirectory)\ruby\edgeserver" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RakeTarget="alcoholic:getSmashed"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;Output TaskParameter="RakeOutput" PropertyName="RakeOutput"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/RunRake&gt;<br>&nbsp;&nbsp;&nbsp; &lt;Message Text="The output from Rake is... $(RakeOutput)" /&gt;<br>&nbsp; &lt;/Target&gt;<br>&lt;/Project&gt;<br> <p><font color="#0000ff"><strong>NOTES</strong></font> <p><font color="#0000ff">Fist I attempted to creating a RAKE process by trying to call rake.&nbsp; (pass rake as the file name to the process start info).<br>The process failed to start saying that the file not found.<br>Then I tried with rake.bat </font>(pass rake.bat as the file name to the process start info).<br><font color="#0000ff">The problem seems to be that RAKE.BAT does some <br>manipulation and calls ruby.exe and passes the file "rake" to ruby.exe.&nbsp; Normally it would end up something like<br>[ruby.exe c:\ruby\bin\rake ..].<br>However The path to rake is getting resolved incorrectly when I pass the rake.bat as the <br>program name.&nbsp; It is using the working directory and ending up with </font></p> <p><font color="#0000ff">[ruby.exe &lt;MY WORKING DIR&gt;\rake...]<br>Obviously there is no file called "rake"&nbsp; in my working directory. <br></font><font color="#0000ff">I need to resolve this, but for now this should get me going. </font></p> <p><font color="#0000ff"></font>&nbsp;</p> <p><font color="#0000ff">The installation for for Ruby could be possibly obtained by resolving <br>the file extension .rb to an executable. That is another task that can be added <br>to eliminate the need to send the ruby install directory as a parameter. </font></p>]]></content:encoded>
    </item>
    <item>
      <title>Doing the same thing again and again</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/doing-the-same-thing-again-and-again</link>
      <pubDate>Thu, 04 Jun 2009 01:39:20 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2009/06/04/doing-the-same-thing-again-and-again/</guid>
      <description><![CDATA[A while back in the C++ days, there were class libraries doing things like sending mail,...]]></description>
      <content:encoded><![CDATA[<p>A while back in the C++ days, there were class libraries doing things like <br>sending mail, opening/reading cert store, event log writing reading, etc. These were written in C++ to encapsulate some of the pain associated with <br>routine tasks. Great Idea!!</p> <p>MFC's CReg CSocket and a bumch of classes started with C made things easier. If you need to do stuff <br>you would use one of those reusable libraries or you would write one of those reusable classes. </p> <p>Then came COM. Once again there are COM components that did read/write registry, read/write to event log.<br>you can see the ATL's incarnation of Registry classes as an example. </p> <p>Then came ASP/VbScript. You need to read write the file system and registry from ASP files. Enter the Scripting.FileSystemObject<br>and its cousins. Due the popularity of ASP and web development, great amount of functionality needed to be cast through <br>IDispatch interfaces to make it available for VBScript programmers. </p> <p><br>Human beings want to make progress. So .NET arrived. So there are new way to read&nbsp; / write registry<br>and read write event log and read write files. OK</p> <p>What about powershell? The world then needs to be packaged in Cmdlets to be consumed from the <br>powershell. great. </p> <p><br>Well how about MSBuild? How can I read write registry from MSBUILD? Of course you need to <br>have the MSBuild tasks. Custom or not custom, you need to view the world through the MSBUILD Tasks. <br>There are community libraries that help you read registry and do great things like send mail, etc. </p> <p><strong>I wonder. How many ways do we need to read/write the registry?</strong></p>]]></content:encoded>
    </item>
    <item>
      <title>To Agile or Not Agile</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/to-agile-or-not-agile</link>
      <pubDate>Fri, 23 Jan 2009 02:33:02 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2009/01/23/to-agile-or-not-agile/</guid>
      <description><![CDATA[I have been pondering over the Agile, Iterative, XTreme, waterfall and several other software...]]></description>
      <content:encoded><![CDATA[<p><font face="consol" color="#800080" size="2">I have been pondering over the Agile, Iterative, XTreme, waterfall and several other software methodologies. I think that each of them have their strengths, I have no reason to bash one methodology or pump some other methodology.<br>Personally, I really don't care about the methodology. I don't think it matters. Let me explain.<br><br>Since I started working about 20 years back, I always wanted to be organized and accomplish more. In one training class, 20 years back, the instructor said<br>"I know of no better idea than to just write down your tasks and prioritize". I heard it, I nodded. Since then, I have been trying to follow that simple principle.<br>I start using Outlook tasks and not really update them or use them as guidance. Before outlook, I used to purchase day planners from a reputed company<br>and I eventually left 95% of the days unfilled or unused. Then I bought a palm pilot. Then upgraded the memory. Then decided to switch to the paper based planners.<br>You get the idea. I am still looking for&nbsp; a methodology.<br><br>The point is that the methodology, or the means, is not the problem. "I" am the problem. If "I" decide to be more disciplined, a mere post it note would have <br>served the purpose. Well may be a $2 note book. The problem is that I imagine the most complex of situations, and imagine my self following the <br>discipline to the core and create all sorts of scenarios where I need to have more elaborate mechanisms to keep me disciplined. Some where along the line, <br>my focus goes form "I" to the "methodology" and one more year passes before I get really disciplined. </font></p> <p><font color="#800080" size="2">There is NOTHING wrong with the waterfall methodology. I am sure that if we send a bi-partisan committee they will find projects where waterfall is just worked fine. <br>There is NOTHING wrong with agile either. There are projects where it succeeds and there are projects where it fails.<br><br>But I BET that in both cases, the projects that succeeded would have succeeded with either methodology, and that they would have succeeded not because of <br>the methodology, but because of the fundamental discipline of how they conducted the business.</font></p> <p><font color="#800080" size="2">When it comes to software "engineering", there are some fundamental principles. Like Mr.Covey says in his "Principle Centered Leadership", there are some <br>fundamental principles when it comes to software engineering</font></p> <ol> <li><font color="#800080" size="2">Have your requirements</font>  <li><font color="#800080" size="2">Have an idea of what you are building</font>  <li><font color="#800080" size="2">Develop the fundamental architecture of the components</font>  <li><font color="#800080" size="2">Have a release plan </font> <li><font color="#800080" size="2">Write <strong><u>very detailed</u></strong> specs and design documents (sorry agile folks!). Spend time in making sure <br>that the design is solid. The code should be a mechanical reproduction of the design (as much as possible.not taking a religion here)</font>  <li><font color="#800080" size="2">Don't pretend Quality is #1, behave so. Role model Quality as fundametal principle</font>  <li><font color="#800080" size="2">Take time to test - functional, API, Perf, Stress</font>  <li><font color="#800080" size="2">Fix bugs. Don't let service packs be the bug fix vehicles.</font></li></ol> <p><font color="#800080" size="2">Fundamental, natural principles don't change. It is our never ending quest to invent schemes that lead us to believe that we can defy them - that blinds <br>us.<br><br>Let me tell you about losing weight. Atkins, South Beach, Nutri System? Or Eat Sensible and Exercise?<br>Let me go buy the South Beach Diet book. The girls on the cover page look <strong>beautiful.</strong></font></p> <p><font face="consol" color="#800080" size="2">Comments?</font></p> <p><font face="consol" color="#800080" size="2">&nbsp;<br><br></p></font>]]></content:encoded>
    </item>
    <item>
      <title>VSTT Controller and Agent setup</title>
      <link>https://docs.microsoft.com/archive/blogs/dgorti/vstt-controller-and-agent-setup</link>
      <pubDate>Tue, 02 Oct 2007 23:18:28 GMT</pubDate>
      <dc:creator><![CDATA[dgorti]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/dgorti/2007/10/02/vstt-controller-and-agent-setup/</guid>
      <description><![CDATA[In my previous blog article, I pointed out that in a workgroup environment, Windows XP has the force...]]></description>
      <content:encoded><![CDATA[<p>In my previous blog article, I pointed out that in a workgroup environment, Windows XP has the force guest policy on and it prevents agents and clients connecting to the controller.    <br />    <br />In this post, I want to expand on this and talk a bit more about VSTT controller and agent setup. Particularly, I will talk about the user account requirements on the machines running controller, agent and the client.     <br />    <br />Ed Glas already talked about the setup in general at <a href="https://blogs.msdn.com/edglas/pages/load-agent-and-load-controller-installation-and-configuration-guide.aspx">https://blogs.msdn.com/edglas/pages/load-agent-and-load-controller-installation-and-configuration-guide.aspx</a>, where he has a list of user account and password requirements on the machines running controller and agent. In this post, I want to drill down further into the reasons behind those user account requirements. I will do this by walking you through the installation process of a controller, agent and a client and troubleshooting the authentication issues. </p>  <p>To start with, I have 3 machines. VSTTClient, VSTTController and VSTTAgent1. I have Windows XP SP2 on all the 3 machines. They are all in a workgroup TESTRIG. I have intentionally made the administrator password <strong>different</strong> on all the 3 machines. The idea is not accidentally authenticate to another box as an Admin. Other than this, all the machines are clean and no other setup has been done. </p>  <p>Before we proceed to the installation, I want to go over a few things. </p>  <p><strong>1. Workgroup authentication</strong>     <br />In a Windows domain environment, there is a central authority to validate credentials. In a workgroup environment, there is no such central authority. Still, we should be able to have computers in a workgroup talk to each other and authenticate users. To enable this, local accounts have a special characteristic that allows the local security authority on the computer to authenticate a &quot;principal&quot; in a special way.     <br />If you have two computers and a principal &quot;UserXYZ&quot; on both machines the security identifiers are different for MACHINE1\UserXYZ and MACHINE2\UserXYZ and for all practical purposes they are two completely different &quot;Principals&quot;. However if the passwords are the same for them on each of these computers, the local security authority treats them as the     <br />same principal.     <br />    <br />So when MACHINE1\UserXYZ tries to authenticate to MACHINE2\UserXYZ, and if the passwords are the same, then on MACHINE2, the UserXYZ is authenticated successfully and     <br />is <strong>treated as MACHINE2\UserXYZ. </strong>Note the last sentence. The user MACHINE1\UserXYZ     <br />is authenticated as MACHINE2\UserXYZ if the passwords are the same.     <br /></p>  <p><strong>2. Force Guest      <br /></strong>As I mentioned in the previous article, in a default Windows XP workgroup environment, the force guest policy is on by default. You can quickly check this out by mapping a share from a one machine to the other. Here, I am trying to map a share from the VSTTController to VSTTAgent1 and I am forced to login as a Guest, and the User Name box is grayed out. </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/forcegueston_1.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/forcegueston_1.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="215" alt="forcegueston" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/forcegueston_thumb_1.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/forcegueston_thumb_1.png" width="244" border="0" /></a> </p>  <p>If the force guest policy is on, it does not matter if you have the same users and passwords, you can not authenticate to the machine as any other principal than the Guest. You can turn it off using Regedt32, HKLM\System\CurrentControlSet\Control\Lsa and editing the forceguest value to 0. </p>  <p><strong>3. Security Audit      <br /></strong>If you are having authentication issues and don't know what is happening, the security audit policy is your good friend. Normally security audit is turned off and hence you will not see the events in the event viewer.     <br />To turn the security auditing, open mmc, add group policy object editor, select local computer. Then Select Local Computer Policy\Computer Configuration\Windows Settings\Security settings\Local Policies\AuditPolicy and turn on Success and Failure auditing for the events of interest. For troubleshooting purposes, I turn on the audit for everything here. Granted this generates a lot of auditing but at least I know what is going on and once I am done troubleshooting, I can turn the auditing off.&#xA0; <br />Once done, on a command prompt, run gpupdate /force to apply the policy. </p>  <p>&#xA0;</p>  <p><strong><u>Installing the Controller and Agent        <br /></u></strong>Now that we have the basics covered, I started the installation process with the VSTTController box on which I intend to install the Controller. The first thing is to turn off the force guest policy since I already know that the agent and the client needs to connect and authenticate to the controller. Then I added a user CSUser. This is the user the controller service will run as. During the installation process, you need to pick a user under which the controller service runs under and I picked the CSUser.     <br />    <br />After the installation of the controller, I have, on the VSTTController machine,&#xA0; </p>  <ul>   <li>A user account CSUser </li>    <li>An empty TeamTestAgentService group </li>    <li>An empty TeamTestControllerAdmins group </li>    <li>An empty TeamTestControllerUsers group. </li> </ul>  <p>At this point the controller service seems to be running, so I am happy so far. </p>  <p>   <br />The next step then is to go to the VSTTAgent1 machine and install the Agent. Note that on this machine I did not yet turn the ForceGuest policy off on VSTTAgent1. The reason, once again, is that I wanted to understand the minimal stuff that I need to do get this rig working.&#xA0; <br />Before I started the agent installation, I added the user AgentUser to the machine. This is the identity under which the agent process will run. I logged in as <strong>Administrator</strong> on this box and started the agent installation. The installation process asks for the user identity of the agent process and I specified the identity as AgentUser. About 15 seconds in into the process, the agent installation has trouble. Apparently connecting to the controller failed.     <br />&#xA0;&#xA0; <a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AgentInstallationIssue_2.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AgentInstallationIssue_2.jpg"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="108" alt="AgentInstallationIssue" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AgentInstallationIssue_thumb.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AgentInstallationIssue_thumb.jpg" width="571" border="0" /></a>     <br />To start troubleshooting, I switched to the VSTTController machine, turn on the security auditing on the VSTTController machine as described above and then switched back to the VSTTAgent1 machine and clicked Retry on the dialog. As expected it fails again. Now when I switched to the VSTTController box and looked at the Event Viewer, under Security folder, I see the following event. </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AuthFailure_2.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AuthFailure_2.jpg"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="399" alt="AuthFailure" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AuthFailure_thumb.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/AuthFailure_thumb.jpg" width="355" border="0" /></a>     <br />    <br />The VSTTController machine is rejecting the logon VSTTAgent1\Administrator. This is for good reason. The Administrator passwords are intentionally not the same!     <br />    <br />It is easy to see why the agent installation connects to the controller box under the identity of the person who is installing the Agent. The user that is installing the agent is supposed to be part of the Administrators that administer the controller and agents. The act of installing an Agent and connecting to a controller makes the agent to be registered with the controller.     <br />This needs special permissions.</p>  <p>One thing we can do now is to adjust the administrator passwords to be the same. I don't particularly like the idea, since I don't want Administrators of one box to automatically connect to other boxes.    <br />    <br />So what I decided to do is to add a user AgentAdmin on the VSTTAgent1 machine and add it to the Administrators group. The idea is that I will restart the Agent installation as AgentAdmin, not as Administrator. Since AgentAdmin is part of the Administrators group on VSTTAgent1 machine, the installation should work fine. On the VSTTController machine,     <br />I need to add the same user AgentAdmin with the same password. However VSTTController\AgentAdmin <strong>will not be part of the Administrators group on VSTTController box.</strong> The idea is to not unnecessarily grant permissions that are not required.     <br />    <br />So at this point, here is what I have on each box</p>  <table cellspacing="0" cellpadding="2" width="371" border="0"><tbody>     <tr>       <td valign="top" width="215"><strong>VSTTController</strong></td>        <td valign="top" width="154"><strong>VSTTAgent1</strong></td>     </tr>      <tr>       <td valign="top" width="231">ForceGuest Off</td>        <td valign="top" width="161">ForceGuest ON</td>     </tr>      <tr>       <td valign="top" width="231">AgentAdmin as a <strong>normal user</strong></td>        <td valign="top" width="161">AgentAdmin user part of the Administrators group</td>     </tr>      <tr>       <td valign="top" width="234">CSUser as a normal user. The controller service is running under this user</td>        <td valign="top" width="163">AgentUser as a normal user. This is the intended identity under which the agent should be running</td>     </tr>      <tr>       <td valign="top" width="234">         <p>An empty TeamTestAgentService group            <br />An empty TeamTestControllerAdmins group             <br />An empty TeamTestControllerUsers group</p>       </td>        <td valign="top" width="164">N/A</td>     </tr>      <tr>       <td valign="top" width="234">Security Audit turned on </td>        <td valign="top" width="164">Security Audit off</td>     </tr>   </tbody></table>  <p>With this arrangement, I logged off as an Administrator on the VSTTAgent1 box and then logged in back as a AgentAdmin.    <br />    <br />But before I proceed, I will let you in on a cool trick. The .NET tracing's default trace listener writes the trace using the API called OutputDebugString and you can view the output in real time using a tool called DebugView. DebugView is a tool that is freely downloadable from the Technet. Go <a href="https://www.microsoft.com/technet/sysinternals/utilities/debugview.mspx">here</a> to download.What this means is that you don't need to configure file based trace listeners, and keep refreshing the file editor to see the latest contents. Since I wanted to see the controller's trace messages, I switched to the VSTTController box, edited the QtController.exe.Config file (under &lt;rootdrive&gt;:\Program Files\Microsoft Visual Studio 9.0 Team Test Load Agent\LoadTest), and adjusted the EqtTraceLevel to a value of &quot;4&quot; like shown below, saved the file and restarted the controller service for the settings to take effect. </p>  <p>&lt;switches&gt;    <br />&#xA0; &lt;!-- You must use integral values for &quot;value&quot;.     <br />&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; Use 0 for off, 1 for error, 2 for warn, 3 for info, and 4 for verbose. --&gt;     <br />&#xA0; &lt;add name=&quot;EqtTraceLevel&quot; value=&quot;4&quot; /&gt;     <br />&lt;/switches&gt;</p>  <p>Once I did this, I fired up the DebugView. You should see something like this on the VSTTController box. </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/DebugView_4.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/DebugView_4.jpg"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="213" alt="DebugView" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/DebugView_thumb_1.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/DebugView_thumb_1.jpg" width="765" border="0" /></a></p>  <p>Now that I have both the security audit and the controller tracing on the VSTTController box, I came back to the VSTTAgent1 box and started the install process. Remember that now I am logged in on the VSTTAgent1 Box as AgentAdmin, which is part of the VSTTAgent1\Administrators group. </p>  <p>The Agent installation fails again. On the VSTTController box, I look at the event viewer. I see a successful logon of the AgentAdmin. The Logon type = 3 which means that this is a network logon. So we made progress. We are able to authenticate to the VSTTContoller machine as VSTTController\AgentAdmin from VSTTAgent1 machine using VSTTAgent1\AgentAdmin principal.    <br />    <br /><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/Successlogin_2.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/Successlogin_2.jpg"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="349" alt="Successlogin" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/Successlogin_thumb.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/Successlogin_thumb.jpg" width="307" border="0" /></a>     <br />    <br />The next thing to look at is the QtController's trace messages from the DebugView tool. The trace messages show that the controller service is checking for group membership of this authenticated principal (user) AgentAdmin.&#xA0; It is checking to see if the AgentAdmin is a member of TestTestControllerUsers or TeamTestControllerAdmins group. Since I did not add the AgentAdmin as part of any group on the VSTTController box, the membership check fails and controller returns an error which is showing up as an installation failure on VSTTAgent1 machine.     <br />    <br />To fix the error it is not clear to me as to what group I need to add the AgentAdmin user to     <br />on the VSTTController box. At first I tried adding the AgentAdmin to the TeamTestControllerUsers group, went back to the VSTTAgent1 box and retried the installation. Even though the installation was successful, the agent was never registered with the controller. I could have tried to fix through other means, but decided to try this through the user scenario. So I added the AgentAdmin as part of the TestTestControllerAdmins group on the VSTTController box. I turned the security auditing on on the VSTTAgent1 machine too, just to aid in debugging. </p>  <p>I uninstalled the agent from the VSTTAgent1 box and&#xA0; attempted a reinstall of the Agent.    <br />Here is what I have just before the reinstall of the Agent on VSTTAgent1 machine</p>  <table cellspacing="0" cellpadding="2" width="371" border="0"><tbody>     <tr>       <td valign="top" width="215"><strong>VSTTController</strong></td>        <td valign="top" width="154"><strong>VSTTAgent1</strong></td>     </tr>      <tr>       <td valign="top" width="231">ForceGuest Off</td>        <td valign="top" width="161">ForceGuest ON</td>     </tr>      <tr>       <td valign="top" width="231">AgentAdmin as a <strong>normal user            <br />and part of the TeamTestControllerAdmins</strong></td>        <td valign="top" width="161">AgentAdmin user part of the Administrators group</td>     </tr>      <tr>       <td valign="top" width="234">CSUser as a normal user. The controller service is running under this user</td>        <td valign="top" width="163">AgentUser as a normal user. This is the intended identity under which the agent should be running</td>     </tr>      <tr>       <td valign="top" width="234">         <p>Empty TeamTestAgentService group&#xA0; <br />            <br />TeamTestControllerAdmins group             <br />has&#xA0; AgentAdmin as a member             <br />            <br />Empty TeamTestControllerUsers </p>       </td>        <td valign="top" width="164">N/A</td>     </tr>      <tr>       <td valign="top" width="234">Security Audit turned on </td>        <td valign="top" width="164">Security Audit <strong>ON</strong></td>     </tr>   </tbody></table>  <p>The installation seems to be successful. On the VSTTController machine I see trace messages showing that the installation went well and the VSTTAGENT1 is added to the AgentManager. So far so good. </p>  <p>Now that the installation of the agent is successful, I really don't see the point of having the AgentAdmin as a user on the VSTTController machine or on the VSTTAgent1 machine. I want to delete this account so that I can reduce any unnecessary user accounts with the same passwords floating around. So I deleted the AgentAdmin account from both the VSTTController and VSTTAgent1 machines ( first I need to logoff as AgentAdmin on the VSTTAgent1 box, log back in as Administrator before deleting the AgentAdmin user). With the AgentAdmin account deleted from both the machines, here is the configuration</p>  <table cellspacing="0" cellpadding="2" width="371" border="0"><tbody>     <tr>       <td valign="top" width="215"><strong>VSTTController</strong></td>        <td valign="top" width="154"><strong>VSTTAgent1</strong></td>     </tr>      <tr>       <td valign="top" width="231">ForceGuest Off</td>        <td valign="top" width="161">ForceGuest ON</td>     </tr>      <tr>       <td valign="top" width="234">CSUser as a normal user. The controller service is running under this user</td>        <td valign="top" width="163">AgentUser as a normal user. This is the intended identity under which the agent should be running</td>     </tr>      <tr>       <td valign="top" width="234">         <p>Empty TeamTestAgentService group            <br />Empty TeamTestControllerAdmins group             <br />Empty TeamTestControllerUsers group</p>       </td>        <td valign="top" width="164">N/A</td>     </tr>      <tr>       <td valign="top" width="234">Security Audit turned on </td>        <td valign="top" width="164">Security Audit <strong>ON</strong></td>     </tr>   </tbody></table>  <p>The Agent installation suggested a reboot after the installation so I did that.    <br />After the reboot when I logged into the VSTTAgent1 box as an Administrator and opened the event viewer. I am expecting that the Agent service will not be able to connect to the controller. Why? The agent is running as VSTTAgent1\AgentUser and there is no corresponding user on the VSTTController box. So Agent should fail to connect.     <br />Sure enough the event viewer on VSTTAGent1 machine and the event viewer on the VSTTController machine both point to the failed logon of VSTTAgent1\AgentUser on VSTTController machine.     <br /><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/FailedAgentUser_2.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/FailedAgentUser_2.jpg"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="388" alt="FailedAgentUser" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/FailedAgentUser_thumb.jpg" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/FailedAgentUser_thumb.jpg" width="343" border="0" /></a> </p>  <p>Since we need the agent to connect to controller, we must add the AgentUser to the VSTTController machine with the same password as it has on VSTTAgent1 machine.    <br />Even after adding this, the agent is still not happy since it still can't connect yo the controller service. Looking at the debug view, you can figure out that the AgentUser needs to be part of the TeamTestAgentService group. So I added the AgentUser to the TeamTestAgentService group. Still no success.&#xA0;&#xA0; <br />Looking at the Event viewer on VSTTAgent1, I find that the VSTTController\CSUser is trying to authenticate to VSTTAgent1 machine. This because the controller wants to callback to&#xA0; the agent on a different connection and that connection needs to be authenticated.     <br />    <br /><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_2.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_2.png"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="391" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_thumb.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_thumb.png" width="347" border="0" /></a>     <br />The auth fails due to the fact that we have force guest on and we don't have CSUser as a local user on VSTTAgent1. Lets fix this now. After fixing, and rebooting, this is what I have for configuration</p>  <table cellspacing="0" cellpadding="2" width="371" border="0"><tbody>     <tr>       <td valign="top" width="215"><strong>VSTTController</strong></td>        <td valign="top" width="154"><strong>VSTTAgent1</strong></td>     </tr>      <tr>       <td valign="top" width="231">ForceGuest Off</td>        <td valign="top" width="161">ForceGuest Off</td>     </tr>      <tr>       <td valign="top" width="234">CSUser as a normal user. The controller service is running under this user</td>        <td valign="top" width="163">CSUser as a normal user. </td>     </tr>      <tr>       <td valign="top" width="234">The AgentUser as a normal user.</td>        <td valign="top" width="163">AgentUser as a normal user. This is the intended identity under which the agent should be running</td>     </tr>      <tr>       <td valign="top" width="234">         <p>TeamTestAgentService group contains the AgentUser            <br />Empty TeamTestControllerAdmins TeamTestControllerUsers group contains AgentUser</p>       </td>        <td valign="top" width="164">N/A</td>     </tr>      <tr>       <td valign="top" width="234">Security Audit turned on </td>        <td valign="top" width="164">Security Audit <strong>ON</strong></td>     </tr>   </tbody></table>  <p>That does it. The agent and controller are now happy and are communicating to each other. </p>  <p><strong><u>Installing the Client and getting it to connect to the controller</u></strong></p>  <p>I created a ClientUser on the VSTTClient machine. I installed VSTT, logged off and logged in back again a VSTTClient\ClientUser. I fired up VS, created a test project, specified VSTTController as the remote controller and started a run. As expected, the VS is trying to    <br />open an authenticated connection to the VSTTControlelr as VSTTClient\ClientUser. Since that user is not defined on the VSTTController machine, the logon fails. </p>  <p>&#xA0;<a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_4.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_4.png"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="325" alt="image" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_thumb_1.png" original-url="https://blogs.msdn.com/blogfiles/dgorti/WindowsLiveWriter/VSTTControllerandAgentsetup_96FA/image_thumb_1.png" width="289" border="0" /></a>     <br />The fix is to add the ClientUser to the VSTTController box and then to the TeamTestControllerUsers group.     <br />Even after doing that you would still find that you are able to execute tests.     <br />Following the same troubleshooting techniques and turning on the security audit on the     <br />VSTTClient, the VSTTController\CSUser is failing to authenticate to the VSTTClient.     <br />So the fix is to turn off the force guest poilicy and create the CSUser on the VSTTClient machine.     <br />    <br />Now I can run tests in my multi machine workgroup topology.... </p>  <p>Here is the final recipe for the VSTT Client, Controller and agent setup</p>  <ol>   <li>On all machines in the workgroup, turn off the ForceGuest policy. </li>    <li>Determine the user account under which the controller is going to run under [CSUser]      <br />Add this account to *all* the machines - client, controller and agent machines [VSTTClient, VSTTController, and VSTTAgent1 in my case] </li>    <li>On the controller machine, install the controller. When you install the controller it will      <br />create a set of user groups mentioned above. </li>    <li>Determine the account under which the agent is going to run under [AgentUser]. Add the account to the controller machine and to the TeamSystemAgentService Group. </li>    <li>On the agent machine, create a temporary account called AgentAdmin. Add the account to the Administrators group. Add the AgentAdmin account to the controller machine and to the TeamSystemControllerAdmins user group </li>    <li>Login to the Agent machine as the AgentAdmin      <br />Install the agent, specify AgentUser as the user underwhich the server is going to run under </li>    <li>Once the install is complete, on the Agent machine, logoff as AgentAdmin, log back in as Administator and delete the AgentAdmin account. Delete the AgentAdmin account on the Controller machine. There is no need for this account any more. </li>    <li>Determine the account under which you are going to run the Visual Studio on the      <br />client machine. [ClientUser on VSTTClient].&#xA0; Add the account to the controller machine with the same password. On the controller machine, add the client user to the TeamSystemControllerUsers group. </li> </ol>  <p>Thats it. I hope this article showed you in detail how to install controller and agent in a workgroup and troubleshoot any authentication issues using the DebugView and Security Audit policy. </p>]]></content:encoded>
    </item>
  </channel>
</rss>