Google Analytics

Monday, January 19, 2015

Creating a Local Windows Group on Install

One of the recent Installer/Wix issues that needed to be addressed in our product was to automatically create a Windows group on install. Our installer creates and starts a number of Windows services that are run as a particular user with the appropriate permissions. Currently, whomever is installing the product must create the local group, create the user account to run the services and add the user to the local group before running our installer. In order to count down on the tasks the user must do manually, we added a story to both create the local group and user on install, and I first took to doing a spike solution to create the group.

Doing a spike allowed me to isolate the action needed to just creating the group. Our installer is currently made up of several Wix libraries, approximately a dozen Wix source files, and a number of custom actions. I wasn't confident about finding just the right place to add the group before I even understood how to do it. Also, the spike allowed me to quickly run the install and uninstall without going through all the other actions our current installer does.

My hope was that I could do it fairly simply with Wix or with the Wix Utilities extension. The utilities extension does provide a group element; however, "This element is not capable of creating new groups but can be used to add new or existing users to an existing group." Luckily a Google search indicated that someone had already done the work to create the necessary custom actions to do this. Msiext (http://dblock.github.io/msiext/) contains a UserPrivileges extension along with a number of other Wix extensions which I wish I had known about before. The download includes runnable demos, but there isn't much in the way of documentation. Ergo, I'm writing this blog post rather than simply pointing you to some documentation there.

So I got the latest msiext download. I created a console app as my spike and added a Wix Setup project to the solution. To the setup project I added a reference to the WixUserPrivilegesExtension DLL in the msiext package. This gives access to the LocalGroup element:

<UserPrivileges:LocalGroup Id="DemoLocalGroup1" Name="DemoLocalGroup1"
Description="Demo local group 1 for a spike." CheckIfExists="yes"
CreateOnInstall="yes" DeleteOnUnInstall="yes">
</UserPrivileges:LocalGroup>

The explanation is pretty straight-forward. the Id is the unique element id, Name is the name of the group and description is what appears in the description when you Manage Groups in Windows. CheckIfExists will prevent it from re-creating the group if the group already exists. CreateOnInstall creates the group on install and DeleteOnUnInstall will delete the group on uninstall. These can be changed if, for example your application creates the groups itself and all you want to do is delete it on uninstall, or if you want to leave the group unchanged even if the user chooses to uninstall. Like I said, pretty straight-forward.

My entire Product.wxs looks like so:


xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
     xmlns:UserPrivileges="http://schemas.appsecinc.com/wix/UserPrivilegesExtension">
    <Product Id="*" Name="SetupProject1" Language="1033" Version="1.0.0.0" Manufacturer="PlutoTech" UpgradeCode="5eec8bc9-ee30-451b-b3c2-fd4ce78bf277">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
        <MediaTemplate />
        <Feature Id="ProductFeature" Title="SetupProject1" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
        </Feature>
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLFOLDER" Name="SetupProject1" />
            </Directory>
        </Directory>
    </Fragment>
 
    <Fragment>
        <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
            <Component Id="LocalUserGroupDemo1" Guid="YOUR-GUID-HERE">
                <UserPrivileges:LocalGroup Id="DemoLocalGroup1" Name="DemoLocalGroup1"
                    Description="Demo local group 1 for a spike." CheckIfExists="yes"
                    CreateOnInstall="yes" DeleteOnUnInstall="yes">
                </UserPrivileges:LocalGroup>
                <File Id="File.MyService" KeyPath="yes" Source="$(var.AddLocalGroupSpike.TargetPath)" />
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

Note that the INSTALLFOLDER cannot be the keypath for the component. In this case it is actually the executable, Id="File.MyService. In our actual installer, the component that creates the necessary groups is separate from any other files, but the component group it belongs to is associated with the TARGETDIR directory.

Thursday, January 15, 2015

Keystone XL

I'm hoping I can weigh in on the Keystone pipeline before Obama vetoes the bi-partisan bill authorizing its construction. This is one of those political issues that I think highlight how dysfunctional our federal government has become. The stance of Congress, mostly on the right side of the aisle with a couple handsful of supporters on the left, is that the Keystone XL pipeline, an offshoot, a shortcut, if you will, of the rest of Keystone that has already been built would make it easier to get oil down from Alberta, Canada, to the refineries on the U.S. gulf coast. The majority of Democrats, following the lead of the White House, believe that more time is necessary to study the environmental and economic effects of the pipeline before authorizing its construction. Note, no one is saying it should NEVER be built, only that we need to take more time to examine the impact of building it.

First, let's get one thing right out of the way: the Republican argument that it will create 42,000 jobs. That's the figure thrown out there all the time by those on the right. It isn't a made-up one, but the report they are citing says that money spent on the the pipeline would help sustain 42,000 jobs. In the short-term it would create about 4000 construction jobs. Other jobs would be created both directly and indirectly by the construction, but not 42,000. Some of those jobs involved actually already exist, are filled, and are being paid today. So, while I'm sure that while locally it will help economies where it is being built, the construction isn't really going to have a big impact on the national job market. That was just a nice story for Republicans to throw out there when unemployment was so high.

I certainly wouldn't dispute that there are environmental issues at play with the construction of Keystone XL. However, assessments of its impact have been going on for five years now. The government itself published its "Final Supplemental Environmental Impact Statement" this time last year. Note the terms Final and Supplemental. It's supplemental because the government wasn't happy with the assessment created by a third-party that they allowed the owner of the pipeline to commission, which said that no significant environmental impact would occur. Guess what the government's independent assessment stated. That's right, that the pipeline and construction of the pipeline would have no significant environmental impact. Well, there was money well-spent. And how can the president insist that more study is needed when they've been holding on to the "Final" impact assessment for a year now?

In fact, Obama himself fast-tracked the construction of phase 3 of the pipeline in 2012. (The XL extension is phase 4). He made a big proclamation to the people of Oklahoma how he was going to cut through the bureaucratic red tape to get that phase built. We already have hundreds of thousands of miles of pipeline criss-crossing the U.S. It isn't the pipeline nor its construction that is really at issue in regards to the environment. It is oil sands.

Oil sand, as the name suggests, is oily (really bituminous) sand. Like its stony shale cousin, only recently has the technology made it profitable to try and extract oil from. The extraction of it is more akin to strip-mining with pits and tailings, than it is to an oil well. Also, it burns with higher carbon emissions than lighter crude oil, but much less than those given off by burning coal. Environmentalists real concern is that the pipeline will give easier access to Alberta's oil sands reserves and expand its extraction.

Two things about this: 1) Canada has been expanding the oil sands extraction regardless of access to the pipeline. That has been driven by the high price of oil and better and better technology. With the oil price plummeting, extraction may not increase, but you can bet, at some point, that oil is going to come out of that ground. 2) Currently those gulf coast refineries already refine oil from oil sands, only that oil comes from Venezuela. Venezuela is one of the largest exporters of crude oil in the world. Their government is also a repressive, socialist one that has consistently labeled capitalism and the U.S. specifically as its great enemy. The U.S. imports about 300 million barrels of crude oil annually from Venezuela. Even with today's price, that's $15 billion U.S. dollars flowing to Venezuela annually. Why would we want that money going to Venezuela instead of Canada?

Finally, as I said before, that oil is going to come out of the ground in Alberta one way or another. They are still expanding their operations up there. How does that oil get to the refinery now?, you might ask. Because it IS still going to refineries. The answer is by trucks and trains. Go ahead and Google "train derailments". Go ahead and Google "Lac-Mégantic". Tell me that moving that oil by train is somehow safer than by pipeline. I don't even need to start on truck accidents. We had one here just north of Brighton, carrying diesel rather than crude, that closed the interstate yesterday. What emits more carbon into the air, pumping oil through a pipe or moving it by truck or train?

This is federal government bullshit on both sides. Okay there's some job impact but not significant enough to say that the pipeline is definitely in the national interest. There is some environmental impact but not enough to say it isn't in the national interest. This issue is simply a political card that the two sides are wielding against each other. I, like most other Americans according to polls, believe the pipeline should be built. Yes, we have low gas prices now, but the market will adjust. When prices go back up, we'll be bemoaning the fact that Alberta is sitting on a ton of crude they can't move fast enough. However, whether it makes sense or not, let's stop with the bullshit coming out of Washington.

Sunday, January 4, 2015

RIP Stuart Scott

Honestly, I never thought I'd shed a tear over the passing of Stuart Scott. I'll admit that I wasn't the biggest fan of him as a Sportscaster. Initially I thought that Boo-yah and all the other catch-phrases were sort of gimicky. I just wanted the sports scores. I didn't need SportsCenter to be entertaining. As a young white kid used to the vanilla delivery of the sports report each night and then each morning with the advent of ESPN, I found Scott's delivery to be uppity. There, I said it. Yes, UPPITY. Subtle racism that I didn't recognize at the time, but it amounted to this black man coming into a white man's world and overturning the apple cart. Thank God he did. Sportscasting has never been, and will never be, the same again.

There is one fewer innovator in the world this morning. Scott brought real soul, soul in the most African-American, hip-hop, barbershop, gospel choir sense of the word, to the mayonnaise on white bread world of Bristol, Connecticut's ESPN, and he did it unabashedly and without compromise. (Can I get an Amen?) He changed how every sportscaster would deliver the news, black or white. I'm paraphrasing, but one of the more insightful comments I read about Scott this morning was from one of his white colleagues (it escapes me just which one) saying that Scott came in and was just himself, a rather brash black man, and that gave everyone else the permission to just be themselves as well.

More recently, while I appreciated what he had contributed to his profession, I felt that he had grown a bit long in the tooth for his "cool as the other side of the pillow" persona. I remember though after his eye injury, someone taking a shot at him on social media about it and thinking, Really? Here's this big brash persona who puts himself in the public eye (still completely unabashed), is very good at what he does, and yet has plenty to criticize, and you want to take a shot at his appearance? And it was with great sorrow that I heard of his cancer diagnosis. Having lost loved ones to cancer, it isn't something I would wish on anyone.

I started following Stuart Scott on Twitter. It was then that I really began to admire him. Here was a man who was facing a devastating illness with such a quiet dignity. On Twitter he told of the doctor visits, the treatments, but never with any complaint. His concern was more for the young cancer patients he saw. He was always optimistic, always a warrior attitude toward the fight ahead. And still his timeline was mostly about sports and his children. On air, other than his increasingly thinner appearance, you'd never know anything about his fight with cancer. I was impressed to see him on air so soon after what I'm sure was some exhausting round of radiation. A professional. Cal Ripken and Lou Gehrig rolled up into one.

Today's news is further proof that cancer is a bitch. It doesn't care how optimistic you are, or how much of a fighter. I mean, those traits are certainly healthier for you in a fight with cancer than resignation. I just mean, that I've seen the most optimistic, courageous people lose to cancer. It's more proof that the fight cannot be left to those who have the disease or their immediate circles. The fight against cancer belongs to all of us. This world was a better one with Stuart Scott in it.

Tuesday, December 16, 2014

What Does That Have to do With the Price of Oil?

One thing that I really haven't heard anyone talk about concerning this precipitous drop in the price of oil is how OPEC hasn't cut their production. In years past when the price of oil threatened to drop liek this, OPEC would only need to threaten to cut production to keep prices stable. Now, even in the face of plummeting prices, OPEC has said they will not cut production. (http://www.bloomberg.com/news/2014-12-14/u-a-e-says-opec-won-t-change-output-even-if-price-drops-to-40.html)

The result of this is that Russia's economy, so dependent on oil, is going right down the crapper. (http://www.bloomberg.com/quote/RUBUSD:CUR) I don't think this is any accident. Of course, one can only speculate the conversations that go on behind closed doors between U.S. politicians and Arabian sheikhs, but certainly no one is in any hurry to throw Vladimir Putin a life ring. For as much as Putin kicked around Obama this summer, maybe Obama deserves some consideration for throwing a haymaker of his own.

Friday, December 5, 2014

I want to get this down in writing, though it is certainly only a half-baked idea. It isn't even really an idea, but a stream of thoughts. And this is why I need to get it down: it's a thought experiment, or a series of thought experiments, a hypothesis that needs to be held up to scrutiny in order to be refined.It's on the nature of good and evil, morality, right and wrong. I've been ruminating on it, turning over this big ball of dough in my mind, adding ingredients here and there, throwing out the entire batch at times and remixing it. Now it's time for me to throw these into the oven, bake it, and see how it tastes, see how others think it tastes.

As with baking, putting these ideas out there for public scrutiny is final. Final in a sense that once it's out there, especially on the internet, you can't just take it back. Just as once a cookie is baked, if it's terrible, you just can't break it back down and get your eggs, flour, sugar, and chocolate chips back. However, you can take the entire batch, throw it into the garbage and start again. I need to actually try this and see how it tastes finally.

So here goes: Good exists.

Good exists inherently in the universe. It wasn't invented by humans, but I'm not sure that I can positively say that it existed before humans. It wasn't invented by humans anymore than intelligence or creativity was invented by humans. It goes hand-in-hand with those things, so whether it existed before humans, I guess is one of those points of debate. I'll assert that it did, but that's for a later time. 

Good exists. That is my little leap of faith at which I start. It is the basis upon which I say I am a religious person. Science is based in fact. Theories that support certain facts are studied and tested until some fact, some evidence comes along to make that theory in its current state invalid. The theory changes and is re-evaluated until it no longer supports the evidence and must be again re-evaluated, ad infinitum. Philosophy and mathematics incorporate logic. "I think therefore I am." Logic is baked into that statement. Socrates used logic to prove that non-truths failed in their logical absurdity. Taking the logical inverse of some truth, he whittled it down to a point where it made no sense, thus the opposite of that case must be true. (I'm saying this badly, but again, this is just a thought experiment.) I have no evidence to disprove that Good does not exist. We can start with the premise that it does not exist and follow the logic through, and there is nothing that I think I can come up with that would make it a logical fallacy. Good may not exist. It's an examination that I want to work through later. However, I know that if Good does not exist in this world, then it is no ta world I want to be part of. And that is frightening. Too frightening for now, so I am starting with the premise that it does exist.

If good does not exist, then morality does not exist. Again, it's an assertion that I would like to examine later through its inverse, but provided I am making the assumption that Good exists, then I will also say that it is upon that assumption that morality is based. Let's now also assert that Evil then also exists being the negative counterpart or exact opposite of Good, Murder is evil. Rape is evil. Theft is evil. (As an aside, I think another idea to examine is that theft is at the heart of all crime, murder being the theft of life, rape the theft of sex and so forth.) One can argue that the idea that these things are bad or evil have emerged as a necessity of human evolution, that we are repulsed by the concept of murder or rape because it has been hard-wired into our DNA. If we were murdering and raping each other, we'd never evolve as a species in the first place. One can argue that the laws against theft were key inventions to us being able to live in an organized society (and thus not murder each other). This may be. Perhaps our sense of Good vs. Evil is some evolutionary necessity, that our sense of morality springs from us all trying to get along together. However, that begs the question. Perhaps we are evolving, but to what end? What's to say that evolving at all is good? Again, I simply have faith that it is. I believe, have mere faith in the fact, that we are being drawn toward some higher... purpose, for lack of a better term. And it is this higher purpose, this THING, that is not just the idea of Good, the Form of Good in the Platonic sense, but is God itself.

Tuesday, July 22, 2014

Begging Your Pardon

I have to get something off my chest, and it's petty and trite just as all my little pet peeves are. Consider it a public service announcement: "Begging the question" and "raising the question" are not the same, and normally when people say the former, they mean the latter.

Begging the question means to assume the very conclusion that you are trying to prove. Here's this example from the Wikipedia entry:

"To allow every man an unbounded freedom of speech must always be, on the whole, advantageous to the State, for it is highly conducive to the interests of the community that each individual should enjoy a liberty perfectly unlimited of expressing his sentiments."

Basically the above quote is saying "Freedom of speech in society is good because it is good to have a society with free speech." The argument begs the question of what makes free speech good in the first place. It is circular reasoning.

I don't fault and don't correct people who use "begs the question" to mean "raises the question". I used to do it as well until relatively recently. Now that I've been set right, when I hear it, and how often I hear it, it grates on me; just as fewer v. less and who v. whom are like sandpaper to my eardrums when I hear them. It's just personal, and I'm a grammatical jerk (uh... to coin a phrase).

Tuesday, July 15, 2014

Configuring GhostDoc Pro, part 1 - File, Class, and Properties Headers

I started this post back in the beginning of 2013. I apparently tossed my exported rules(*), so am redoing them and thought I would revisit my own, hopefully useful, advice.

I swear, why is it so hard to find documentation on doing some of the simplest programming things? Why is it that these companies can create such a relatively useful product, and yet it is impossible to find any real sort of documentation on how to use them? I have multiple blog entries brewing on my frustrations using Infragistics Reporting in their NetAdvantage suite. Today though, I'm going to rail against SubMain's GhostDoc Pro.

GhostDoc, a VisualStudio extension for automatically generating XML documentation and code comments, is a total time saver when it is properly configured. Because of the government certification our products generally go through, our code has to be meticulously commented in very specific ways. At one point my rules were properly configured and I had spent about half a day configuring the templates as well as I could, so that all I had to do was go to Tools > GhostDoc > Document File and my code file would be properly documented including headers, properties and fields. Unfortunately when I chose to upgrade my current version of GhostDoc it apparently took my desire to "Upgrade" my current rules file and merely used that as a "suggestion", which it then ignored, and I ended up with the default GhostDoc Rules templates. *So my first suggestion to you is that as soon as you have the templates the way you want them, export them to avoid having go through the hassle of re-creating them.

Secondly, bookmark this blog post as reference to refer back to later. The extent of SubMain's online documentation for creating your own templates seems to be to simply rely on the community of GhostDoc users creating blog posts in their community pages (http://community.submain.com/) and on StackOverflow. If you go to the tutorials on SubMain's site you will notice there are no tutorials for GhostDoc. (I take it back. Since I first began writing this blog article way long ago, a simple tutorial has been added. It is a very quick overview of all of GhostDoc Pro's features.) The help file that is downloaded for GhostDoc Pro contains an Introduction to T4 Templates and a description of each of the different templates that are used.

Editing T4 Templates
To edit the GhostDoc Pro T4 Rules templates, in Visual Studio go to Tools > GhostDoc Pro > Options > Rules and select the template you want to update. The template will open into an editor that does provide some Intellisense and color-coding.

Adding Global Properties
Global properties, accessible through the GetGlobalProperty of the Context object, can be added in the GhostDoc Pro options window under Global Properties. Here you can enter the key-value pairs for commonly used properties throughout your templates.

File Header

By necessity, our standard file header looks like this:

// -----------------------------------------------------------------------------
// <copyright company="Dominion Voting Systems" file="ElectionDataService.cs">
//     Copyright (c) 2013 Dominion Voting Systems, Inc. All Rights Reserved.
//     Any distribution of source code by others is prohibited.
// </copyright>
// <summary>
//    This file contains the Election Data Service Class
// </summary>
// <revision date="1/11/2013" revisor="ben.rice">
//     File created.
// </revision>
// -----------------------------------------------------------------------------

In order to create this I am using the following template.

<#@ template language="C#" #>
// -----------------------------------------------------------------------------
// ">
//     Copyright (c) <#= Context.DestinationFileCreationDateTime.ToString("yyyy") #> <#= Context.GetGlobalProperty("CompanyName") #>. All Rights Reserved.
//     Any distribution of source code by others is prohibited.
//
//









//     This file contains the <#= System.IO.Path.GetFileNameWithoutExtension(Context.DestinationFile) #> class.
//
// " date="<#= Context.DestinationFileCreationDateTime.ToString("MM/dd/yyyy") #>">
//     File created.
//
// -----------------------------------------------------------------------------

You can see that the Context object is perhaps the most useful in creating these templates. It's use is based on the template in which it is used. In this case, for example, the Context.Exceptions would be empty since the context of its use here is the File and Exceptions enumerates only any exceptions thrown by a method or property. You can also see the use of Context to retrieve our Global Properties. Of course, this can be done in whatever template you are editing. Also note that I can use the standard .Net objects and methods to get the file name without the file extension, for example. 

I need to modify the rule yet to add the "File modified." revision note. For now I throw that on manually.

Class Header
Our standard class header:

///









///     Interaction logic for navigating views within the Ballot Viewer.
///
///
///     Class created.
///

For this I am basically using the default class template for GhostDoc Pro.

<#@ template language="C#" #>
<#  CodeElement codeElement = Context.CurrentCodeElement; #>
///









///<# GenerateSummaryText(); #>
///
<#  if(codeElement.HasTypeParameters) 
{
for(int i = 0; i < codeElement.TypeParameters.Length; i++) 

TypeParameter typeParameter = codeElement.TypeParameters[i]; 
#>
/// <# GenerateTypeParamText(typeParameter, i); #>
<# }

#>
<#= Context.GetNonGeneratedTags() #>
<# GenerateRemarksText(); #>

<#+
private void GenerateSummaryText()
{
if(Context.HasExistingTagText("summary"))
{
this.WriteLine(Context.GetExistingTagText("summary"));
}
else if(Context.CurrentCodeElement.IsSealed)
{
this.WriteLine("TODO: Add summary for Class " + Context.CurrentCodeElement.Name + ". This class cannot be inherited." + Context.ExecMacro("$(End)"));
}
else
{
this.WriteLine("TODO: Add summary for Class " + Context.CurrentCodeElement.Name + "." + Context.ExecMacro("$(End)"));
}
}
    
private void GenerateTypeParamText(TypeParameter typeParameter, int index)
{
if(Context.HasExistingTagText("typeparam", index)) 

this.Write(Context.GetExistingTagText("typeparam", index));

else 
{
string typeParameterName = typeParameter.Name;
if(typeParameterName != null)
{
if(typeParameterName.Length == 1)
{
this.Write("");
}
else
{
this.Write("The type of " + Context.ExecMacro(typeParameterName, "$(TheAndAll)") + ".");
}            
}          

}

private void GenerateRemarksText()
{
if(Context.HasExistingTagText("remarks"))
{ #>
/// <#= Context.GetExistingTagText("remarks") #>
<#+    }
else if (!string.IsNullOrEmpty(Context.GetGlobalProperty("DefaultBlankRemarksText")))

            // Should you require a default comment, set it in
// Options -> Global Properties -> DefaultBlankRemarksText
#>
/// <#= Context.GetGlobalProperty("DefaultBlankRemarksText") #>
<#+    }
}
#>

Notice that within the template I can call C# methods coded below. In this case we are generating type parameter elements for each type parameter if there are any. If a summary element or remarks element already exists in the header, that text is preserved, else some default text is inserted. Mine has a TODO reminder in the summary text to highlight that I need to change that.

I'm going to skip ahead now to the Properties rule

Our property header will look something like this:
///









///     Gets or sets the name of the election.
///
/// The name of the election.
///
///     Member created.
///
///
///     Changed to allow public setter.
///

I added to the default property generated in order to set the revision element.

<#@ template language="C#" #>
///









///<# GenerateSummaryText(); #>
///
<# GenerateValueText(); #>
<# if(Context.HasExceptions) 
{
foreach (System.Collections.Generic.KeyValuePair> pair in Context.ExceptionDictionary)
{
string exceptionText = Context.GetExistingExceptionTagText(pair.Key); #>
/// <#= ((exceptionText != null && exceptionText.Length > 0) ? exceptionText : Context.GetExceptionTagText(pair.Value)) #>
<# }
}  #>
<#= Context.GetNonGeneratedExceptionTags() #>
<#= Context.GetNonGeneratedTags() #>
<# GenerateRevisionTag(); #>
<# GenerateRemarksText(); #>

<#+
private void GenerateSummaryText()
{
CodeElement codeElement = Context.CurrentCodeElement;
   
if(Context.HasExistingTagText("summary"))
{
this.WriteLine(Context.GetExistingTagText("summary"));
}
else if(Context.HasInheritedTagText("summary"))
{
this.WriteLine(Context.GetInheritedTagText("summary"));
}
else if(IsBooleanProperty()) 
{
if(IsStateCheckBooleanProperty())
{
// state check boolean property
this.WriteLine(Context.ExecMacro("$(PropertyAccessText) a value indicating whether this instance $(PropertyName.Words.All)."));
}
else if(IsSingleWordBooleanProperty())
{
// single word boolean property
this.WriteLine(Context.ExecMacro("$(PropertyAccessText) a value indicating whether this $(DeclaringTypeName.ShortNameAsSee) is $(PropertyName.Words.First)."));
}
else
{
// boolean property
this.WriteLine(Context.ExecMacro("$(PropertyAccessText) a value indicating whether $(End)[$(PropertyName.Words.All)]."));
}
}
else if(Context.ContainsOfTheReordering(codeElement.Name)) 
{
this.WriteLine(Context.ExecMacro(codeElement.Name, "$(OfTheReordering)", Context.ExecMacro("$(PropertyAccessText)")));
}
else
{
this.WriteLine(Context.ExecMacro("$(PropertyAccessText) $(PropertyName.Words.TheAndAll)."));
}
}
        
private void GenerateValueText()
{
CodeElement codeElement = Context.CurrentCodeElement;
        
if(Context.HasExistingTagText("value"))

#>
/// <#= Context.GetExistingTagText("value") #>
<#+
}
else if(Context.HasInheritedTagText("value"))

#>
/// <#= Context.GetInheritedTagText("value") #>
<#+
}
else /*if(Context.CurrentCodeElement.HasSet) - commented for StypeCop compliance (SA1609) */
{
if(IsBooleanProperty()) 
{
.....
}
}

.... 
    
private bool IsBooleanProperty()
{
CodeElement codeElement = Context.CurrentCodeElement;  
return (bool)(codeElement.ReturnType != null && 
codeElement.ReturnType.FullName != null &&
codeElement.ReturnType.FullName.Length > 0 && 
(string.Compare( codeElement.ReturnType.FullName, "System.Boolean", true) == 0 ||
string.Compare( codeElement.ReturnType.FullName, "System.Nullable", true) == 0));
}  

private void GenerateRemarksText()
{
if(Context.HasInheritedTagText("remarks"))
{ #>
/// <#= Context.GetInheritedTagText("remarks") #>
<#+    }
else if(Context.HasExistingTagText("remarks"))
{ #>
/// <#= Context.GetExistingTagText("remarks") #>
<#+    }
else if (!string.IsNullOrEmpty(Context.GetGlobalProperty("DefaultBlankRemarksText")))

            // Should you require a default comment, set it in
// Options -> Global Properties -> DefaultBlankRemarksText
#>
/// <#= Context.GetGlobalProperty("DefaultBlankRemarksText") #>
<#+    }
}

private void GenerateRevisionTag()

if( Context.HasExistingTagText("summary")) {#>
/// <revision revisor="<#= Context.GetGlobalProperty("UserName") #>" date="<#= DateTime.Now.ToString("MM/dd/yyyy") #>">
///     TODO: Add Revision
/// </revision>
///
<#+
} else { #>
/// " date="<#= DateTime.Now.ToString("MM/dd/yyyy") #>">
///     Member created.
///
<#+ }
}
#>

Please note that in the interest of space, I omitted code that determines how to word the value element of a boolean property. In the custom "GenerateRevisionTag" method I did a check to see if the summary has already been written. If it hadn't been (prior to this GD call), I assume the property has just been created and add "Member created." to the revision text. Otherwise, I assume that the property already existed, but is being revised and add the reminder to myself to update what the revision was. Also note that the GenerateRevisionTag call is made after Context.GetNonGeneratedTags is written. This makes later revisions appear below previously written ones.